|
|
|
@ -9,7 +9,6 @@ using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
@ -39,7 +38,7 @@ namespace SharpChat {
|
|
|
|
|
public IWebSocketServer Server { get; }
|
|
|
|
|
public ChatContext Context { get; }
|
|
|
|
|
|
|
|
|
|
public static HttpClient HttpClient { get; }
|
|
|
|
|
private readonly HttpClient HttpClient;
|
|
|
|
|
|
|
|
|
|
private IReadOnlyCollection<IChatCommand> Commands { get; } = new IChatCommand[] {
|
|
|
|
|
new AFKCommand(),
|
|
|
|
@ -53,21 +52,15 @@ namespace SharpChat {
|
|
|
|
|
return Sessions.FirstOrDefault(x => x.Connection == conn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SockChatServer() {
|
|
|
|
|
// "fuck it"
|
|
|
|
|
HttpClient = new HttpClient();
|
|
|
|
|
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(@"SharpChat");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ManualResetEvent Shutdown { get; }
|
|
|
|
|
private ManualResetEvent Shutdown { get; set; }
|
|
|
|
|
private bool IsShuttingDown = false;
|
|
|
|
|
|
|
|
|
|
public SockChatServer(ManualResetEvent mre, ushort port) {
|
|
|
|
|
public SockChatServer(HttpClient httpClient, ushort port) {
|
|
|
|
|
Logger.Write("Starting Sock Chat server...");
|
|
|
|
|
|
|
|
|
|
Shutdown = mre ?? throw new ArgumentNullException(nameof(mre));
|
|
|
|
|
HttpClient = httpClient;
|
|
|
|
|
|
|
|
|
|
Context = new ChatContext(this);
|
|
|
|
|
Context = new ChatContext(HttpClient, this);
|
|
|
|
|
|
|
|
|
|
Context.Channels.Add(new ChatChannel(@"Lounge"));
|
|
|
|
|
#if DEBUG
|
|
|
|
@ -79,6 +72,10 @@ namespace SharpChat {
|
|
|
|
|
Context.Channels.Add(new ChatChannel(@"Staff") { Rank = 5 });
|
|
|
|
|
|
|
|
|
|
Server = new SharpChatWebSocketServer($@"ws://0.0.0.0:{port}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Listen(ManualResetEvent mre) {
|
|
|
|
|
Shutdown = mre;
|
|
|
|
|
|
|
|
|
|
Server.Start(sock => {
|
|
|
|
|
if(IsShuttingDown || IsDisposed) {
|
|
|
|
@ -143,7 +140,7 @@ namespace SharpChat {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(sess.User is ChatUser && sess.User.HasFloodProtection) {
|
|
|
|
|
if(sess.User is not null && sess.User.HasFloodProtection) {
|
|
|
|
|
sess.User.RateLimiter.AddTimePoint();
|
|
|
|
|
|
|
|
|
|
if(sess.User.RateLimiter.State == ChatRateLimitState.Kick) {
|
|
|
|
@ -182,7 +179,7 @@ namespace SharpChat {
|
|
|
|
|
if(args.Length < 3 || !long.TryParse(args[1], out long aUserId))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
FlashiiAuth.Attempt(HttpClient, new FlashiiAuthRequest {
|
|
|
|
|
FlashiiAuth.AttemptAsync(HttpClient, new FlashiiAuthRequest {
|
|
|
|
|
UserId = aUserId,
|
|
|
|
|
Token = args[2],
|
|
|
|
|
IPAddress = sess.RemoteAddress.ToString(),
|
|
|
|
@ -276,7 +273,7 @@ namespace SharpChat {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(messageText.Length > MSG_LENGTH_MAX)
|
|
|
|
|
messageText = messageText.Substring(0, MSG_LENGTH_MAX);
|
|
|
|
|
messageText = messageText[..MSG_LENGTH_MAX];
|
|
|
|
|
|
|
|
|
|
messageText = messageText.Trim();
|
|
|
|
|
|
|
|
|
@ -293,14 +290,13 @@ namespace SharpChat {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(message == null)
|
|
|
|
|
message = new ChatMessage {
|
|
|
|
|
Target = mChannel,
|
|
|
|
|
TargetName = mChannel.TargetName,
|
|
|
|
|
DateTime = DateTimeOffset.UtcNow,
|
|
|
|
|
Sender = mUser,
|
|
|
|
|
Text = messageText,
|
|
|
|
|
};
|
|
|
|
|
message ??= new ChatMessage {
|
|
|
|
|
Target = mChannel,
|
|
|
|
|
TargetName = mChannel.TargetName,
|
|
|
|
|
DateTime = DateTimeOffset.UtcNow,
|
|
|
|
|
Sender = mUser,
|
|
|
|
|
Text = messageText,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Context.Events.Add(message);
|
|
|
|
|
mChannel.Send(new ChatMessageAddPacket(message));
|
|
|
|
@ -335,7 +331,7 @@ namespace SharpChat {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IChatMessage HandleV1Command(string message, ChatUser user, ChatChannel channel) {
|
|
|
|
|
string[] parts = message.Substring(1).Split(' ');
|
|
|
|
|
string[] parts = message[1..].Split(' ');
|
|
|
|
|
string commandName = parts[0].Replace(@".", string.Empty).ToLowerInvariant();
|
|
|
|
|
|
|
|
|
|
for(int i = 1; i < parts.Length; i++)
|
|
|
|
@ -370,8 +366,7 @@ namespace SharpChat {
|
|
|
|
|
offset = 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(targetUser == null)
|
|
|
|
|
targetUser = user;
|
|
|
|
|
targetUser ??= user;
|
|
|
|
|
|
|
|
|
|
if(parts.Length < offset) {
|
|
|
|
|
user.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
|
|
|
|
@ -389,7 +384,7 @@ namespace SharpChat {
|
|
|
|
|
if(nickStr == targetUser.Username)
|
|
|
|
|
nickStr = null;
|
|
|
|
|
else if(nickStr.Length > 15)
|
|
|
|
|
nickStr = nickStr.Substring(0, 15);
|
|
|
|
|
nickStr = nickStr[..15];
|
|
|
|
|
else if(string.IsNullOrEmpty(nickStr))
|
|
|
|
|
nickStr = null;
|
|
|
|
|
|
|
|
|
@ -454,7 +449,7 @@ namespace SharpChat {
|
|
|
|
|
Flags = ChatMessageFlags.Action,
|
|
|
|
|
};
|
|
|
|
|
case @"who": // gets all online users/online users in a channel if arg
|
|
|
|
|
StringBuilder whoChanSB = new StringBuilder();
|
|
|
|
|
StringBuilder whoChanSB = new();
|
|
|
|
|
string whoChanStr = parts.Length > 1 && !string.IsNullOrEmpty(parts[1]) ? parts[1] : string.Empty;
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrEmpty(whoChanStr)) {
|
|
|
|
@ -476,7 +471,7 @@ namespace SharpChat {
|
|
|
|
|
if(whoUser == user)
|
|
|
|
|
whoChanSB.Append(@" style=""font-weight: bold;""");
|
|
|
|
|
|
|
|
|
|
whoChanSB.Append(@">");
|
|
|
|
|
whoChanSB.Append('>');
|
|
|
|
|
whoChanSB.Append(whoUser.DisplayName);
|
|
|
|
|
whoChanSB.Append(@"</a>, ");
|
|
|
|
|
}
|
|
|
|
@ -492,7 +487,7 @@ namespace SharpChat {
|
|
|
|
|
if(whoUser == user)
|
|
|
|
|
whoChanSB.Append(@" style=""font-weight: bold;""");
|
|
|
|
|
|
|
|
|
|
whoChanSB.Append(@">");
|
|
|
|
|
whoChanSB.Append('>');
|
|
|
|
|
whoChanSB.Append(whoUser.DisplayName);
|
|
|
|
|
whoChanSB.Append(@"</a>, ");
|
|
|
|
|
}
|
|
|
|
@ -546,7 +541,8 @@ namespace SharpChat {
|
|
|
|
|
|
|
|
|
|
int createChanHierarchy = 0;
|
|
|
|
|
if(createChanHasHierarchy)
|
|
|
|
|
int.TryParse(parts[1], out createChanHierarchy);
|
|
|
|
|
if(!int.TryParse(parts[1], out createChanHierarchy))
|
|
|
|
|
createChanHierarchy = 0;
|
|
|
|
|
|
|
|
|
|
if(createChanHierarchy > user.Rank) {
|
|
|
|
|
user.Send(new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY));
|
|
|
|
@ -554,7 +550,7 @@ namespace SharpChat {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string createChanName = string.Join('_', parts.Skip(createChanHasHierarchy ? 2 : 1));
|
|
|
|
|
ChatChannel createChan = new ChatChannel {
|
|
|
|
|
ChatChannel createChan = new() {
|
|
|
|
|
Name = createChanName,
|
|
|
|
|
IsTemporary = !user.Can(ChatUserPermissions.SetChannelPermanent),
|
|
|
|
|
Rank = createChanHierarchy,
|
|
|
|
@ -848,7 +844,7 @@ namespace SharpChat {
|
|
|
|
|
Sessions.ForEach(s => s.PrepareForRestart());
|
|
|
|
|
|
|
|
|
|
Context.Update();
|
|
|
|
|
Shutdown.Set();
|
|
|
|
|
Shutdown?.Set();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@ -859,13 +855,16 @@ namespace SharpChat {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~SockChatServer()
|
|
|
|
|
=> Dispose(false);
|
|
|
|
|
~SockChatServer() {
|
|
|
|
|
DoDispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
=> Dispose(true);
|
|
|
|
|
public void Dispose() {
|
|
|
|
|
DoDispose();
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Dispose(bool disposing) {
|
|
|
|
|
private void DoDispose() {
|
|
|
|
|
if(IsDisposed)
|
|
|
|
|
return;
|
|
|
|
|
IsDisposed = true;
|
|
|
|
@ -876,9 +875,6 @@ namespace SharpChat {
|
|
|
|
|
Server?.Dispose();
|
|
|
|
|
Context?.Dispose();
|
|
|
|
|
HttpClient?.Dispose();
|
|
|
|
|
|
|
|
|
|
if(disposing)
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|