Compare commits
3 commits
82973f7a33
...
903e39ab76
Author | SHA1 | Date | |
---|---|---|---|
flash | 903e39ab76 | ||
flash | 8c19c22736 | ||
flash | 4e0def980f |
78
Protocol.md
78
Protocol.md
|
@ -762,28 +762,6 @@ Just echo whatever is specified in the first argument.
|
||||||
- `string`: Message to be broadcast.
|
- `string`: Message to be broadcast.
|
||||||
|
|
||||||
|
|
||||||
#### `silence`: Silence notice
|
|
||||||
Informs the client that they've been silenced.
|
|
||||||
|
|
||||||
|
|
||||||
#### `unsil`: Silence revocation notice
|
|
||||||
Informs the client that their silence has been revoked.
|
|
||||||
|
|
||||||
|
|
||||||
#### `silok`: Silence confirmation
|
|
||||||
Informs the client that they have successfully silenced another user.
|
|
||||||
|
|
||||||
##### Arguments
|
|
||||||
- `string`: Username of the user.
|
|
||||||
|
|
||||||
|
|
||||||
#### `usilok`: Silence revocation confirmation
|
|
||||||
Informs the client that they have successfully revoked another user's silence.
|
|
||||||
|
|
||||||
##### Arguments
|
|
||||||
- `string`: Username of the user.
|
|
||||||
|
|
||||||
|
|
||||||
#### `flwarn`: Flood protection warning
|
#### `flwarn`: Flood protection warning
|
||||||
Informs the client that they are risking getting kicked for flood protection (spam) if they continue sending messages at the same rate.
|
Informs the client that they are risking getting kicked for flood protection (spam) if they continue sending messages at the same rate.
|
||||||
|
|
||||||
|
@ -1027,26 +1005,6 @@ Informs the client that they are not allowed to edit a channel.
|
||||||
Informs the client that they are not allowed to delete a message.
|
Informs the client that they are not allowed to delete a message.
|
||||||
|
|
||||||
|
|
||||||
#### `silerr`: Already silenced
|
|
||||||
Informs the client that the user they attempted to silence has already been silenced.
|
|
||||||
|
|
||||||
|
|
||||||
#### `usilerr`: Not silenced
|
|
||||||
Informs the client that the user whose silence they attempted to revoke has not been silenced.
|
|
||||||
|
|
||||||
|
|
||||||
#### `silperr`: Silence permission error
|
|
||||||
Informs the client that they are not allowed to silence the other user.
|
|
||||||
|
|
||||||
|
|
||||||
#### `usilperr`: Silence revocation permission error
|
|
||||||
Informs the client that they are not allowed to revoke the silence on the other user.
|
|
||||||
|
|
||||||
|
|
||||||
#### `silself`: Self silence
|
|
||||||
Informs the client that they are not allowed to silence themselves.
|
|
||||||
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
Actions sent through messages prefixed with `/`. Arguments are described as `[name]`, optional arguments as `[name?]`. The `.` character is ignored in command names (replaced by nothing).
|
Actions sent through messages prefixed with `/`. Arguments are described as `[name]`, optional arguments as `[name?]`. The `.` character is ignored in command names (replaced by nothing).
|
||||||
|
|
||||||
|
@ -1330,42 +1288,6 @@ Retrieves a list of banned users and IP addresses.
|
||||||
- `banlist`: The list of banned users and IP addresses.
|
- `banlist`: The list of banned users and IP addresses.
|
||||||
|
|
||||||
|
|
||||||
### `/silence`: Silence a user
|
|
||||||
Silences a user. If the time argument is not specified, the silence is indefinite.
|
|
||||||
|
|
||||||
#### Format
|
|
||||||
```
|
|
||||||
/silence [username] [time?]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Responses
|
|
||||||
- `cmdna`: The client is not allowed to silence users.
|
|
||||||
- `usernf`: The target user could not be found on the server.
|
|
||||||
- `silself`: The client tried to silence themselves.
|
|
||||||
- `silperr`: The target user has a higher rank that the client.
|
|
||||||
- `silerr`: The target user is already silenced.
|
|
||||||
- `cmderr`: The time argument is formatted incorrectly.
|
|
||||||
- `silence`: Informs the target user that they have been silenced.
|
|
||||||
- `silok`: The target has been successfully silenced.
|
|
||||||
|
|
||||||
|
|
||||||
### `/unsilence`: Revokes a user silence
|
|
||||||
Revokes a user's silenced status.
|
|
||||||
|
|
||||||
#### Format
|
|
||||||
```
|
|
||||||
/unsilence [username]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Responses
|
|
||||||
- `cmdna`: The client is not allowed to revoke silences.
|
|
||||||
- `usernf`: The target user could not be found.
|
|
||||||
- `usilperr`: The target user has a higher rank than the client.
|
|
||||||
- `usilerr`: The target user isn't silenced.
|
|
||||||
- `unsil`: Informs the target user that their silenced status has been revoked.
|
|
||||||
- `usilok`: The silenced status placed on the target has been successfully revoked.
|
|
||||||
|
|
||||||
|
|
||||||
### `/ip`: Retrieve IP addresses
|
### `/ip`: Retrieve IP addresses
|
||||||
Retrieves a user's IP addresses. If the user has multiple connections, multiple `ipaddr` responses may be sent.
|
Retrieves a user's IP addresses. If the user has multiple connections, multiple `ipaddr` responses may be sent.
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,13 @@ using SharpChat.EventStorage;
|
||||||
using SharpChat.Packet;
|
using SharpChat.Packet;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat
|
||||||
|
{
|
||||||
public class ChatContext {
|
public class ChatContext {
|
||||||
public record ChannelUserAssoc(long UserId, string ChannelName);
|
public record ChannelUserAssoc(long UserId, string ChannelName);
|
||||||
|
|
||||||
|
@ -25,6 +27,61 @@ namespace SharpChat {
|
||||||
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
|
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DispatchEvent(IChatEvent eventInfo) {
|
||||||
|
if(eventInfo is MessageCreateEvent mce) {
|
||||||
|
if(mce.IsBroadcast) {
|
||||||
|
Send(new LegacyCommandResponse(LCR.BROADCAST, false, 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
|
||||||
|
if(!mce.ChannelName.StartsWith("@"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
IEnumerable<long> uids = mce.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1);
|
||||||
|
if(uids.Count() != 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IEnumerable<ChatUser> users = Users.Where(u => uids.Any(uid => uid == u.UserId));
|
||||||
|
ChatUser target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
||||||
|
if(target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach(ChatUser user in users)
|
||||||
|
SendTo(user, new ChatMessageAddPacket(
|
||||||
|
mce.MessageId,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
mce.SenderId,
|
||||||
|
mce.SenderId == user.UserId ? $"{target.LegacyName} {mce.MessageText}" : mce.MessageText,
|
||||||
|
mce.IsAction,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
ChatChannel channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
|
||||||
|
SendTo(channel, new ChatMessageAddPacket(
|
||||||
|
mce.MessageId,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
mce.SenderId,
|
||||||
|
mce.MessageText,
|
||||||
|
mce.IsAction,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Events.AddEvent(
|
||||||
|
mce.MessageId, "msg:add",
|
||||||
|
mce.ChannelName,
|
||||||
|
mce.SenderId, mce.SenderName, mce.SenderColour, mce.SenderRank, mce.SenderNickName, mce.SenderPerms,
|
||||||
|
new { text = mce.MessageText },
|
||||||
|
(mce.IsBroadcast ? StoredEventFlags.Broadcast : 0)
|
||||||
|
| (mce.IsAction ? StoredEventFlags.Action : 0)
|
||||||
|
| (mce.IsPrivate ? StoredEventFlags.Private : 0)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Update() {
|
public void Update() {
|
||||||
foreach(ChatConnection conn in Connections)
|
foreach(ChatConnection conn in Connections)
|
||||||
if(!conn.IsDisposed && conn.HasTimedOut) {
|
if(!conn.IsDisposed && conn.HasTimedOut) {
|
||||||
|
@ -148,13 +205,13 @@ namespace SharpChat {
|
||||||
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
|
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
|
||||||
if(!IsInChannel(user, chan)) {
|
if(!IsInChannel(user, chan)) {
|
||||||
SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user));
|
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 AuthSuccessPacket(user, chan, conn, maxMsgLength));
|
||||||
conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank)));
|
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 ContextMessagePacket(msg));
|
||||||
|
|
||||||
conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank)));
|
conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank)));
|
||||||
|
@ -176,7 +233,7 @@ namespace SharpChat {
|
||||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||||
|
|
||||||
SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason));
|
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))
|
if(chan.IsTemporary && chan.IsOwner(user))
|
||||||
RemoveChannel(chan);
|
RemoveChannel(chan);
|
||||||
|
@ -213,14 +270,14 @@ namespace SharpChat {
|
||||||
ChatChannel oldChan = UserLastChannel[user.UserId];
|
ChatChannel oldChan = UserLastChannel[user.UserId];
|
||||||
|
|
||||||
SendTo(oldChan, new UserChannelLeavePacket(user));
|
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));
|
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 ContextClearPacket(chan, ContextClearMode.MessagesUsers));
|
||||||
SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank)));
|
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));
|
SendTo(user, new ContextMessagePacket(msg));
|
||||||
|
|
||||||
ForceChannel(user, chan);
|
ForceChannel(user, chan);
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace SharpChat {
|
||||||
public enum ChatUserPermissions : int {
|
public enum ChatUserPermissions : 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,
|
||||||
|
|
|
@ -10,20 +10,21 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ChatCommandContext ctx) {
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChatMessage ActionDispatch(ChatCommandContext ctx) {
|
|
||||||
if(!ctx.Args.Any())
|
if(!ctx.Args.Any())
|
||||||
return null;
|
return;
|
||||||
|
|
||||||
return new ChatMessage {
|
string actionStr = string.Join(' ', ctx.Args);
|
||||||
ChannelName = ctx.Channel.Name,
|
if(string.IsNullOrWhiteSpace(actionStr))
|
||||||
DateTime = DateTimeOffset.UtcNow,
|
return;
|
||||||
Sender = ctx.User,
|
|
||||||
Text = string.Join(' ', ctx.Args),
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
Flags = ChatMessageFlags.Action,
|
SharpId.Next(),
|
||||||
};
|
ctx.Channel,
|
||||||
|
ctx.User,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
actionStr,
|
||||||
|
false, true, false
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using SharpChat.Packet;
|
using SharpChat.Events;
|
||||||
|
using SharpChat.Packet;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.Commands {
|
||||||
public class BroadcastCommand : IChatCommand {
|
public class BroadcastCommand : IChatCommand {
|
||||||
|
@ -13,7 +15,14 @@ namespace SharpChat.Commands {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args)));
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
|
SharpId.Next(),
|
||||||
|
string.Empty,
|
||||||
|
ctx.User,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
string.Join(' ', ctx.Args),
|
||||||
|
false, false, true
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.EventStorage;
|
||||||
using SharpChat.Packet;
|
using SharpChat.Packet;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.Commands
|
||||||
|
{
|
||||||
public class DeleteMessageCommand : IChatCommand {
|
public class DeleteMessageCommand : IChatCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ChatCommandContext ctx) {
|
||||||
return ctx.NameEquals("delmsg") || (
|
return ctx.NameEquals("delmsg") || (
|
||||||
|
@ -26,7 +27,7 @@ namespace SharpChat.Commands {
|
||||||
return;
|
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)) {
|
if(delMsg == null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR));
|
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR));
|
||||||
|
@ -34,7 +35,7 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Chat.Events.RemoveEvent(delMsg);
|
ctx.Chat.Events.RemoveEvent(delMsg);
|
||||||
ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.SequenceId));
|
ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.Id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
using SharpChat.Packet;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
|
||||||
public class SilenceApplyCommand : IChatCommand {
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
|
||||||
return ctx.NameEquals("silence");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string silUserStr = ctx.Args.FirstOrDefault();
|
|
||||||
ChatUser silUser;
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(silUserStr) || (silUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(silUserStr))) == null) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, silUserStr ?? "User"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(silUser == ctx.User) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_SELF));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(silUser.Rank >= ctx.User.Rank) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_HIERARCHY));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(RNG.Next() > 1 /*silUser.IsSilenced*/) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_ALREADY));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeOffset silenceUntil = DateTimeOffset.MaxValue;
|
|
||||||
|
|
||||||
if(ctx.Args.Length > 1) {
|
|
||||||
if(!double.TryParse(ctx.Args.ElementAt(1), out double silenceSeconds)) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
silenceUntil = DateTimeOffset.UtcNow.AddSeconds(silenceSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
//silUser.SilencedUntil = silenceUntil;
|
|
||||||
ctx.Chat.SendTo(silUser, new LegacyCommandResponse(LCR.SILENCED, false));
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.LegacyName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
using SharpChat.Packet;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
|
||||||
public class SilenceRevokeCommand : IChatCommand {
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
|
||||||
return ctx.NameEquals("unsilence");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string unsilUserStr = ctx.Args.FirstOrDefault();
|
|
||||||
ChatUser unsilUser;
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(unsilUserStr) || (unsilUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unsilUserStr))) == null) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, unsilUserStr ?? "User"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(unsilUser.Rank >= ctx.User.Rank) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.UNSILENCE_HIERARCHY));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(RNG.Next() > 1 /*!unsilUser.IsSilenced*/) {
|
|
||||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NOT_SILENCED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//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.LegacyName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,24 +27,14 @@ namespace SharpChat.Commands {
|
||||||
if(whisperUser == ctx.User)
|
if(whisperUser == ctx.User)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string whisperStr = string.Join(' ', ctx.Args.Skip(1));
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
string whisperChan = ChatUser.GetDMChannelName(ctx.User, whisperUser);
|
SharpId.Next(),
|
||||||
DateTimeOffset dateTime = DateTimeOffset.Now;
|
ChatUser.GetDMChannelName(ctx.User, whisperUser),
|
||||||
|
ctx.User,
|
||||||
ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(new ChatMessage {
|
DateTimeOffset.Now,
|
||||||
DateTime = dateTime,
|
string.Join(' ', ctx.Args.Skip(1)),
|
||||||
ChannelName = whisperChan,
|
true, false, false
|
||||||
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,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,36 @@
|
||||||
using SharpChat.Events;
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SharpChat.EventStorage {
|
namespace SharpChat.EventStorage
|
||||||
|
{
|
||||||
public interface IEventStorage {
|
public interface IEventStorage {
|
||||||
void AddEvent(IChatEvent evt);
|
void AddEvent(
|
||||||
void RemoveEvent(IChatEvent evt);
|
long id, string type,
|
||||||
IChatEvent GetEvent(long seqId);
|
object data = null,
|
||||||
IEnumerable<IChatEvent> GetChannelEventLog(string channelName, int amount = 20, int offset = 0);
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
);
|
||||||
|
void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
);
|
||||||
|
void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
);
|
||||||
|
void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
);
|
||||||
|
|
||||||
|
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,13 @@
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
using SharpChat.Events;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Dynamic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace SharpChat.EventStorage {
|
namespace SharpChat.EventStorage
|
||||||
|
{
|
||||||
public partial class MariaDBEventStorage : IEventStorage {
|
public partial class MariaDBEventStorage : IEventStorage {
|
||||||
private string ConnectionString { get; }
|
private string ConnectionString { get; }
|
||||||
|
|
||||||
|
@ -13,46 +15,97 @@ namespace SharpChat.EventStorage {
|
||||||
ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString));
|
ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEvent(IChatEvent evt) {
|
public void AddEvent(
|
||||||
if(evt == null)
|
long id, string type,
|
||||||
throw new ArgumentNullException(nameof(evt));
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, null, 0, null, ChatColour.None, 0, null, 0, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
if(evt.SequenceId < 1)
|
public void AddEvent(
|
||||||
evt.SequenceId = SharpId.Next();
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, channelName, 0, null, ChatColour.None, 0, null, 0, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, null, senderId, senderName, senderColour, senderRank, senderNick, senderPerms, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
if(type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
RunCommand(
|
RunCommand(
|
||||||
"INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_flags`, `event_data`"
|
"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`)"
|
+ ", `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)",
|
+ ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
|
||||||
new MySqlParameter("id", evt.SequenceId),
|
new MySqlParameter("id", id),
|
||||||
new MySqlParameter("created", evt.DateTime.ToUnixTimeSeconds()),
|
new MySqlParameter("type", type),
|
||||||
new MySqlParameter("type", evt.GetType().FullName),
|
new MySqlParameter("target", string.IsNullOrWhiteSpace(channelName) ? null : channelName),
|
||||||
new MySqlParameter("target", evt.ChannelName),
|
new MySqlParameter("flags", (byte)flags),
|
||||||
new MySqlParameter("flags", (byte)evt.Flags),
|
new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)),
|
||||||
new MySqlParameter("data", JsonSerializer.SerializeToUtf8Bytes(evt, evt.GetType())),
|
new MySqlParameter("sender", senderId < 1 ? null : senderId),
|
||||||
new MySqlParameter("sender", evt.Sender?.UserId < 1 ? null : (long?)evt.Sender.UserId),
|
new MySqlParameter("sender_name", string.IsNullOrWhiteSpace(senderName) ? null : senderName),
|
||||||
new MySqlParameter("sender_name", evt.Sender?.UserName),
|
new MySqlParameter("sender_colour", senderColour.ToMisuzu()),
|
||||||
new MySqlParameter("sender_colour", evt.Sender?.Colour.ToMisuzu()),
|
new MySqlParameter("sender_rank", senderRank),
|
||||||
new MySqlParameter("sender_rank", evt.Sender?.Rank),
|
new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(senderNick) ? null : senderNick),
|
||||||
new MySqlParameter("sender_nick", evt.Sender?.NickName),
|
new MySqlParameter("sender_perms", senderPerms)
|
||||||
new MySqlParameter("sender_perms", evt.Sender?.Permissions)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatEvent GetEvent(long seqId) {
|
public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) {
|
||||||
|
if(type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
long id = SharpId.Next();
|
||||||
|
|
||||||
|
AddEvent(
|
||||||
|
id, type,
|
||||||
|
channel?.Name,
|
||||||
|
user?.UserId ?? 0,
|
||||||
|
user?.UserName,
|
||||||
|
user?.Colour ?? ChatColour.None,
|
||||||
|
user?.Rank ?? 0,
|
||||||
|
user?.NickName,
|
||||||
|
user?.Permissions ?? 0,
|
||||||
|
data,
|
||||||
|
flags
|
||||||
|
);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredEventInfo GetEvent(long seqId) {
|
||||||
try {
|
try {
|
||||||
using MySqlDataReader reader = RunQuery(
|
using MySqlDataReader reader = RunQuery(
|
||||||
"SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`"
|
"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`"
|
+ ", `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_created`) AS `event_created`"
|
||||||
|
+ ", 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", seqId)
|
||||||
);
|
);
|
||||||
|
|
||||||
while(reader.Read()) {
|
while(reader.Read()) {
|
||||||
IChatEvent evt = ReadEvent(reader);
|
StoredEventInfo evt = ReadEvent(reader);
|
||||||
if(evt != null)
|
if(evt != null)
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
@ -63,38 +116,37 @@ namespace SharpChat.EventStorage {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IChatEvent ReadEvent(MySqlDataReader reader) {
|
private static StoredEventInfo ReadEvent(MySqlDataReader reader) {
|
||||||
Type evtType = Type.GetType(Encoding.ASCII.GetString((byte[])reader["event_type"]));
|
return new StoredEventInfo(
|
||||||
IChatEvent evt = JsonSerializer.Deserialize(Encoding.ASCII.GetString((byte[])reader["event_data"]), evtType) as IChatEvent;
|
reader.GetInt64("event_id"),
|
||||||
evt.SequenceId = reader.GetInt64("event_id");
|
Encoding.ASCII.GetString((byte[])reader["event_type"]),
|
||||||
evt.ChannelName = Encoding.ASCII.GetString((byte[])reader["event_target"]);
|
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : new ChatUser(
|
||||||
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(
|
|
||||||
reader.GetInt64("event_sender"),
|
reader.GetInt64("event_sender"),
|
||||||
reader.GetString("event_sender_name"),
|
reader.GetString("event_sender_name"),
|
||||||
ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||||
reader.GetInt32("event_sender_rank"),
|
reader.GetInt32("event_sender_rank"),
|
||||||
(ChatUserPermissions)reader.GetInt32("event_sender_perms"),
|
(ChatUserPermissions)reader.GetInt32("event_sender_perms"),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick")
|
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick")
|
||||||
);
|
),
|
||||||
}
|
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
||||||
|
reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_deleted")),
|
||||||
return evt;
|
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) {
|
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||||
List<IChatEvent> events = new();
|
List<StoredEventInfo> events = new();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using MySqlDataReader reader = RunQuery(
|
using MySqlDataReader reader = RunQuery(
|
||||||
"SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`"
|
"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`"
|
+ ", `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_created`) AS `event_created`"
|
||||||
|
+ ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`"
|
||||||
+ " FROM `sqc_events`"
|
+ " FROM `sqc_events`"
|
||||||
+ " WHERE `event_deleted` IS NULL AND `event_target` = @target"
|
+ " WHERE `event_deleted` IS NULL AND (`event_target` = @target OR `event_target` IS NULL)"
|
||||||
+ " AND `event_id` > @offset"
|
+ " AND `event_id` > @offset"
|
||||||
+ " ORDER BY `event_id` DESC"
|
+ " ORDER BY `event_id` DESC"
|
||||||
+ " LIMIT @amount",
|
+ " LIMIT @amount",
|
||||||
|
@ -104,7 +156,7 @@ namespace SharpChat.EventStorage {
|
||||||
);
|
);
|
||||||
|
|
||||||
while(reader.Read()) {
|
while(reader.Read()) {
|
||||||
IChatEvent evt = ReadEvent(reader);
|
StoredEventInfo evt = ReadEvent(reader);
|
||||||
if(evt != null)
|
if(evt != null)
|
||||||
events.Add(evt);
|
events.Add(evt);
|
||||||
}
|
}
|
||||||
|
@ -117,12 +169,12 @@ namespace SharpChat.EventStorage {
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveEvent(IChatEvent evt) {
|
public void RemoveEvent(StoredEventInfo evt) {
|
||||||
if(evt == null)
|
if(evt == null)
|
||||||
throw new ArgumentNullException(nameof(evt));
|
throw new ArgumentNullException(nameof(evt));
|
||||||
RunCommand(
|
RunCommand(
|
||||||
"UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
|
"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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,14 @@ namespace SharpChat.EventStorage {
|
||||||
);
|
);
|
||||||
|
|
||||||
DoMigration("create_events_table", CreateEventsTable);
|
DoMigration("create_events_table", CreateEventsTable);
|
||||||
|
DoMigration("allow_null_target", AllowNullTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AllowNullTarget() {
|
||||||
|
RunCommand(
|
||||||
|
"ALTER TABLE `sqc_events`"
|
||||||
|
+ " CHANGE COLUMN `event_target` `event_target` VARBINARY(255) NULL AFTER `event_type`;"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateEventsTable() {
|
private void CreateEventsTable() {
|
||||||
|
|
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,94 @@
|
||||||
using SharpChat.Events;
|
using System;
|
||||||
using System;
|
|
||||||
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, IChatEvent> Events = new();
|
private readonly Dictionary<long, StoredEventInfo> Events = new();
|
||||||
|
|
||||||
public void AddEvent(IChatEvent evt) {
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, null, 0, null, ChatColour.None, 0, null, 0, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, channelName, 0, null, ChatColour.None, 0, null, 0, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
object data = null,
|
||||||
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
|
) {
|
||||||
|
AddEvent(id, type, null, senderId, senderName, senderColour, senderRank, senderNick, senderPerms, data, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(
|
||||||
|
long id, string type,
|
||||||
|
string channelName,
|
||||||
|
long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms,
|
||||||
|
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));
|
||||||
|
Events.Add(id, new(id, type, senderId < 1 ? null : new ChatUser(
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
senderColour,
|
||||||
|
senderRank,
|
||||||
|
senderPerms,
|
||||||
|
senderNick
|
||||||
|
), DateTimeOffset.Now, null, channelName, hack, flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) {
|
||||||
|
if(type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
long id = SharpId.Next();
|
||||||
|
|
||||||
|
AddEvent(
|
||||||
|
id, type,
|
||||||
|
channel?.Name,
|
||||||
|
user?.UserId ?? 0,
|
||||||
|
user?.UserName,
|
||||||
|
user?.Colour ?? ChatColour.None,
|
||||||
|
user?.Rank ?? 0,
|
||||||
|
user?.NickName,
|
||||||
|
user?.Permissions ?? 0,
|
||||||
|
data,
|
||||||
|
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)
|
if(evt == null)
|
||||||
throw new ArgumentNullException(nameof(evt));
|
throw new ArgumentNullException(nameof(evt));
|
||||||
Events.Add(evt.SequenceId, evt);
|
Events.Remove(evt.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatEvent GetEvent(long seqId) {
|
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||||
return Events.TryGetValue(seqId, out IChatEvent evt) ? evt : null;
|
IEnumerable<StoredEventInfo> subset = Events.Values.Where(ev => ev.ChannelName == channelName);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
int start = subset.Count() - offset - amount;
|
int start = subset.Count() - offset - amount;
|
||||||
if(start < 0) {
|
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 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SharpChat.Events {
|
namespace SharpChat.Events {
|
||||||
[Flags]
|
|
||||||
public enum ChatMessageFlags {
|
|
||||||
None = 0,
|
|
||||||
Action = 1,
|
|
||||||
Broadcast = 1 << 1,
|
|
||||||
Log = 1 << 2,
|
|
||||||
Private = 1 << 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IChatEvent {
|
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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
95
SharpChat/Events/MessageCreateEvent.cs
Normal file
95
SharpChat/Events/MessageCreateEvent.cs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
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 ChatColour SenderColour { get; }
|
||||||
|
public int SenderRank { get; }
|
||||||
|
public string SenderNickName { get; }
|
||||||
|
public ChatUserPermissions SenderPerms { get; }
|
||||||
|
public DateTimeOffset MessageCreated { get; }
|
||||||
|
public string MessageChannel { 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,
|
||||||
|
ChatColour senderColour,
|
||||||
|
int senderRank,
|
||||||
|
string senderNickName,
|
||||||
|
ChatUserPermissions 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,
|
||||||
|
ChatUser sender,
|
||||||
|
DateTimeOffset msgCreated,
|
||||||
|
string msgText,
|
||||||
|
bool isPrivate,
|
||||||
|
bool isAction,
|
||||||
|
bool isBroadcast
|
||||||
|
) : this(
|
||||||
|
msgId,
|
||||||
|
channelName,
|
||||||
|
sender?.UserId ?? -1,
|
||||||
|
sender?.UserName ?? null,
|
||||||
|
sender?.Colour ?? ChatColour.None,
|
||||||
|
sender?.Rank ?? 0,
|
||||||
|
sender?.NickName ?? null,
|
||||||
|
sender?.Permissions ?? 0,
|
||||||
|
msgCreated,
|
||||||
|
msgText,
|
||||||
|
isPrivate,
|
||||||
|
isAction,
|
||||||
|
isBroadcast
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public MessageCreateEvent(
|
||||||
|
long msgId,
|
||||||
|
ChatChannel channel,
|
||||||
|
ChatUser sender,
|
||||||
|
DateTimeOffset msgCreated,
|
||||||
|
string msgText,
|
||||||
|
bool isPrivate,
|
||||||
|
bool isAction,
|
||||||
|
bool isBroadcast
|
||||||
|
) : this(
|
||||||
|
msgId,
|
||||||
|
channel?.Name ?? null,
|
||||||
|
sender,
|
||||||
|
msgCreated,
|
||||||
|
msgText,
|
||||||
|
isPrivate,
|
||||||
|
isAction,
|
||||||
|
isBroadcast
|
||||||
|
) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,9 +23,6 @@ namespace SharpChat.Misuzu {
|
||||||
[JsonPropertyName("hierarchy")]
|
[JsonPropertyName("hierarchy")]
|
||||||
public int Rank { get; set; }
|
public int Rank { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("is_silenced")]
|
|
||||||
public DateTimeOffset SilencedUntil { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("perms")]
|
[JsonPropertyName("perms")]
|
||||||
public ChatUserPermissions Permissions { get; set; }
|
public ChatUserPermissions Permissions { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,46 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.EventStorage;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet
|
||||||
|
{
|
||||||
public class ChatMessageAddPacket : ServerPacket {
|
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) {
|
public ChatMessageAddPacket(
|
||||||
Message = message ?? throw new ArgumentNullException(nameof(message));
|
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)
|
public static ChatMessageAddPacket FromStoredEvent(StoredEventInfo sei) {
|
||||||
Message.SequenceId = SequenceId;
|
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() {
|
public override IEnumerable<string> Pack() {
|
||||||
|
@ -20,33 +49,32 @@ namespace SharpChat.Packet {
|
||||||
sb.Append('2');
|
sb.Append('2');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
sb.Append(Message.DateTime.ToUnixTimeSeconds());
|
sb.Append(Created.ToUnixTimeSeconds());
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
sb.Append(Message.Sender?.UserId ?? -1);
|
sb.Append(UserId);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
if(IsAction)
|
||||||
sb.Append("<i>");
|
sb.Append("<i>");
|
||||||
|
|
||||||
sb.Append(
|
sb.Append(
|
||||||
Message.Text
|
Text.Replace("<", "<")
|
||||||
.Replace("<", "<")
|
|
||||||
.Replace(">", ">")
|
.Replace(">", ">")
|
||||||
.Replace("\n", " <br/> ")
|
.Replace("\n", " <br/> ")
|
||||||
.Replace("\t", " ")
|
.Replace("\t", " ")
|
||||||
);
|
);
|
||||||
|
|
||||||
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
if(IsAction)
|
||||||
sb.Append("</i>");
|
sb.Append("</i>");
|
||||||
|
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(SequenceId);
|
sb.Append(SequenceId);
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"\t1{0}0{1}{2}",
|
"\t1{0}0{1}{2}",
|
||||||
Message.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0',
|
IsAction ? '1' : '0',
|
||||||
Message.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1',
|
IsAction ? '0' : '1',
|
||||||
Message.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0'
|
IsPrivate ? '1' : '0'
|
||||||
);
|
);
|
||||||
|
|
||||||
yield return sb.ToString();
|
yield return sb.ToString();
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.EventStorage;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet
|
||||||
|
{
|
||||||
public class ContextMessagePacket : ServerPacket {
|
public class ContextMessagePacket : ServerPacket {
|
||||||
public IChatEvent Event { get; private set; }
|
public StoredEventInfo Event { get; private set; }
|
||||||
public bool Notify { 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));
|
Event = evt ?? throw new ArgumentNullException(nameof(evt));
|
||||||
Notify = notify;
|
Notify = notify;
|
||||||
}
|
}
|
||||||
|
@ -22,15 +23,22 @@ namespace SharpChat.Packet {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append('1');
|
sb.Append('1');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Event.DateTime.ToUnixTimeSeconds());
|
sb.Append(Event.Created.ToUnixTimeSeconds());
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
switch(Event) {
|
switch(Event.Type) {
|
||||||
case IChatMessage msg:
|
case "msg:add":
|
||||||
sb.Append(Event.Sender.Pack());
|
case "SharpChat.Events.ChatMessage":
|
||||||
sb.Append('\t');
|
if((Event.Flags & StoredEventFlags.Broadcast) > 0)
|
||||||
|
sb.Append(V1_CHATBOT);
|
||||||
|
else {
|
||||||
|
sb.Append(Event.Sender.Pack());
|
||||||
|
sb.Append('\t');
|
||||||
|
}
|
||||||
|
if((Event.Flags & StoredEventFlags.Broadcast) > 0)
|
||||||
|
sb.Append("0\fsay\f");
|
||||||
sb.Append(
|
sb.Append(
|
||||||
msg.Text
|
Event.Data.RootElement.GetProperty("text").GetString()
|
||||||
.Replace("<", "<")
|
.Replace("<", "<")
|
||||||
.Replace(">", ">")
|
.Replace(">", ">")
|
||||||
.Replace("\n", " <br/> ")
|
.Replace("\n", " <br/> ")
|
||||||
|
@ -38,29 +46,33 @@ namespace SharpChat.Packet {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserConnectEvent _:
|
case "user:connect":
|
||||||
|
case "SharpChat.Events.UserConnectEvent":
|
||||||
sb.Append(V1_CHATBOT);
|
sb.Append(V1_CHATBOT);
|
||||||
sb.Append("0\fjoin\f");
|
sb.Append("0\fjoin\f");
|
||||||
sb.Append(Event.Sender.UserName);
|
sb.Append(Event.Sender.UserName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserChannelJoinEvent _:
|
case "chan:join":
|
||||||
|
case "SharpChat.Events.UserChannelJoinEvent":
|
||||||
sb.Append(V1_CHATBOT);
|
sb.Append(V1_CHATBOT);
|
||||||
sb.Append("0\fjchan\f");
|
sb.Append("0\fjchan\f");
|
||||||
sb.Append(Event.Sender.UserName);
|
sb.Append(Event.Sender.UserName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserChannelLeaveEvent _:
|
case "chan:leave":
|
||||||
|
case "SharpChat.Events.UserChannelLeaveEvent":
|
||||||
sb.Append(V1_CHATBOT);
|
sb.Append(V1_CHATBOT);
|
||||||
sb.Append("0\flchan\f");
|
sb.Append("0\flchan\f");
|
||||||
sb.Append(Event.Sender.UserName);
|
sb.Append(Event.Sender.UserName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserDisconnectEvent ude:
|
case "user:disconnect":
|
||||||
|
case "SharpChat.Events.UserDisconnectEvent":
|
||||||
sb.Append(V1_CHATBOT);
|
sb.Append(V1_CHATBOT);
|
||||||
sb.Append("0\f");
|
sb.Append("0\f");
|
||||||
|
|
||||||
switch(ude.Reason) {
|
switch((UserDisconnectReason)Event.Data.RootElement.GetProperty("reason").GetByte()) {
|
||||||
case UserDisconnectReason.Flood:
|
case UserDisconnectReason.Flood:
|
||||||
sb.Append("flood");
|
sb.Append("flood");
|
||||||
break;
|
break;
|
||||||
|
@ -81,16 +93,15 @@ namespace SharpChat.Packet {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Event.SequenceId < 1 ? SequenceId : Event.SequenceId);
|
sb.Append(Event.Id < 1 ? SequenceId : Event.Id);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Notify ? '1' : '0');
|
sb.Append(Notify ? '1' : '0');
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"\t1{0}0{1}{2}",
|
"\t1{0}0{1}{2}",
|
||||||
Event.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0',
|
Event.Flags.HasFlag(StoredEventFlags.Action) ? '1' : '0',
|
||||||
Event.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1',
|
Event.Flags.HasFlag(StoredEventFlags.Action) ? '0' : '1',
|
||||||
Event.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0'
|
Event.Flags.HasFlag(StoredEventFlags.Private) ? '1' : '0'
|
||||||
);
|
);
|
||||||
|
|
||||||
yield return sb.ToString();
|
yield return sb.ToString();
|
||||||
|
|
|
@ -76,15 +76,6 @@ namespace SharpChat.Packet {
|
||||||
public const string BROADCAST = "say";
|
public const string BROADCAST = "say";
|
||||||
public const string IP_ADDRESS = "ipaddr";
|
public const string IP_ADDRESS = "ipaddr";
|
||||||
public const string USER_NOT_FOUND = "usernf";
|
public const string USER_NOT_FOUND = "usernf";
|
||||||
public const string SILENCE_SELF = "silself";
|
|
||||||
public const string SILENCE_HIERARCHY = "silperr";
|
|
||||||
public const string SILENCE_ALREADY = "silerr";
|
|
||||||
public const string TARGET_SILENCED = "silok";
|
|
||||||
public const string SILENCED = "silence";
|
|
||||||
public const string UNSILENCED = "unsil";
|
|
||||||
public const string TARGET_UNSILENCED = "usilok";
|
|
||||||
public const string NOT_SILENCED = "usilerr";
|
|
||||||
public const string UNSILENCE_HIERARCHY = "usilperr";
|
|
||||||
public const string NAME_IN_USE = "nameinuse";
|
public const string NAME_IN_USE = "nameinuse";
|
||||||
public const string CHANNEL_INSUFFICIENT_HIERARCHY = "ipchan";
|
public const string CHANNEL_INSUFFICIENT_HIERARCHY = "ipchan";
|
||||||
public const string CHANNEL_INVALID_PASSWORD = "ipwchan";
|
public const string CHANNEL_INVALID_PASSWORD = "ipwchan";
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
using SharpChat.Commands;
|
using SharpChat.Commands;
|
||||||
using SharpChat.Config;
|
using SharpChat.Config;
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
|
using SharpChat.EventStorage;
|
||||||
using SharpChat.Packet;
|
using SharpChat.Packet;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat.PacketHandlers {
|
namespace SharpChat.PacketHandlers
|
||||||
|
{
|
||||||
public class SendMessageHandler : IChatPacketHandler {
|
public class SendMessageHandler : IChatPacketHandler {
|
||||||
private readonly CachedValue<int> MaxMessageLength;
|
private readonly CachedValue<int> MaxMessageLength;
|
||||||
|
|
||||||
|
@ -46,8 +48,7 @@ namespace SharpChat.PacketHandlers {
|
||||||
ctx.Chat.ContextAccess.Wait();
|
ctx.Chat.ContextAccess.Wait();
|
||||||
try {
|
try {
|
||||||
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out ChatChannel channel)
|
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out ChatChannel channel)
|
||||||
&& !ctx.Chat.IsInChannel(user, channel)
|
&& !ctx.Chat.IsInChannel(user, channel))
|
||||||
/*|| (user.IsSilenced && !user.Can(ChatUserPermissions.SilenceUser))*/)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(user.Status != ChatUserStatus.Online)
|
if(user.Status != ChatUserStatus.Online)
|
||||||
|
@ -63,8 +64,6 @@ namespace SharpChat.PacketHandlers {
|
||||||
Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}");
|
Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
IChatMessage message = null;
|
|
||||||
|
|
||||||
if(messageText.StartsWith("/")) {
|
if(messageText.StartsWith("/")) {
|
||||||
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
||||||
|
|
||||||
|
@ -77,24 +76,19 @@ namespace SharpChat.PacketHandlers {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(command != null) {
|
if(command != null) {
|
||||||
if(command is ActionCommand actionCommand)
|
command.Dispatch(context);
|
||||||
message = actionCommand.ActionDispatch(context);
|
return;
|
||||||
else {
|
|
||||||
command.Dispatch(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message ??= new ChatMessage {
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
ChannelName = channel.Name,
|
SharpId.Next(),
|
||||||
DateTime = DateTimeOffset.UtcNow,
|
channel,
|
||||||
Sender = user,
|
user,
|
||||||
Text = messageText,
|
DateTimeOffset.Now,
|
||||||
};
|
messageText,
|
||||||
|
false, false, false
|
||||||
ctx.Chat.Events.AddEvent(message);
|
));
|
||||||
ctx.Chat.SendTo(channel, new ChatMessageAddPacket(message));
|
|
||||||
} finally {
|
} finally {
|
||||||
ctx.Chat.ContextAccess.Release();
|
ctx.Chat.ContextAccess.Release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,8 +94,6 @@ namespace SharpChat {
|
||||||
new PardonUserCommand(msz),
|
new PardonUserCommand(msz),
|
||||||
new PardonAddressCommand(msz),
|
new PardonAddressCommand(msz),
|
||||||
new BanListCommand(msz),
|
new BanListCommand(msz),
|
||||||
//new SilenceApplyCommand(),
|
|
||||||
//new SilenceRevokeCommand(),
|
|
||||||
new RemoteAddressCommand(),
|
new RemoteAddressCommand(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue