HEH HEH YEAH ILIGHT HAHAHAHA WAHHH episode 2

This commit is contained in:
flash 2025-04-25 20:15:38 +00:00
parent a8f5c00e37
commit 626951ad10
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
37 changed files with 61 additions and 47 deletions

13
.editorconfig Normal file
View file

@ -0,0 +1,13 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
[*.cs]
# IDE1006: Naming Styles
dotnet_diagnostic.IDE1006.severity = none

View file

@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpChat", "SharpChat\Shar
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DF7A7073-A67A-4D93-92C6-F9D0F95E2359}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
LICENSE = LICENSE

View file

@ -11,13 +11,13 @@ namespace SharpChat.C2SPacketHandlers {
) : C2SPacketHandler {
private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
private List<IChatCommand> Commands { get; } = [];
private List<ChatCommand> Commands { get; } = [];
public void AddCommand(IChatCommand command) {
public void AddCommand(ChatCommand command) {
Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
}
public void AddCommands(IEnumerable<IChatCommand> commands) {
public void AddCommands(IEnumerable<ChatCommand> commands) {
Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
}
@ -61,7 +61,7 @@ namespace SharpChat.C2SPacketHandlers {
if(messageText.StartsWith('/')) {
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
foreach(IChatCommand cmd in Commands)
foreach(ChatCommand cmd in Commands)
if(cmd.IsMatch(context)) {
cmd.Dispatch(context);
return;

View file

@ -1,5 +1,5 @@
namespace SharpChat {
public interface IChatCommand {
public interface ChatCommand {
bool IsMatch(ChatCommandContext ctx);
void Dispatch(ChatCommandContext ctx);
}

View file

@ -16,17 +16,17 @@ namespace SharpChat {
public HashSet<ChatChannel> Channels { get; } = [];
public HashSet<ChatConnection> Connections { get; } = [];
public HashSet<ChatUser> Users { get; } = [];
public IEventStorage Events { get; }
public EventStorage.EventStorage Events { get; }
public HashSet<ChannelUserAssoc> ChannelUsers { get; } = [];
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = [];
public Dictionary<long, ChatChannel> UserLastChannel { get; } = [];
public ChatContext(IEventStorage evtStore) {
public ChatContext(EventStorage.EventStorage evtStore) {
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
RandomSnowflake = new(SnowflakeGenerator);
}
public void DispatchEvent(IChatEvent eventInfo) {
public void DispatchEvent(ChatEvent eventInfo) {
if(eventInfo is MessageCreateEvent mce) {
if(mce.IsBroadcast) {
Send(new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.BROADCAST, false, mce.MessageText));

View file

@ -2,7 +2,7 @@
using System.Text;
namespace SharpChat.Commands {
public class AFKCommand : IChatCommand {
public class AFKCommand : ChatCommand {
private const string DEFAULT = "AFK";
public const int MAX_GRAPHEMES = 5;
public const int MAX_BYTES = MAX_GRAPHEMES * 10;

View file

@ -1,7 +1,7 @@
using SharpChat.Events;
namespace SharpChat.Commands {
public class ActionCommand : IChatCommand {
public class ActionCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("action")
|| ctx.NameEquals("me");

View file

@ -2,7 +2,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class BanListCommand(MisuzuClient msz) : IChatCommand {
public class BanListCommand(MisuzuClient msz) : ChatCommand {
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
public bool IsMatch(ChatCommandContext ctx) {

View file

@ -2,7 +2,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class BroadcastCommand : IChatCommand {
public class BroadcastCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("say")
|| ctx.NameEquals("broadcast");

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class CreateChannelCommand : IChatCommand {
public class CreateChannelCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("create");
}

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class DeleteChannelCommand : IChatCommand {
public class DeleteChannelCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("delchan") || (
ctx.NameEquals("delete")

View file

@ -3,7 +3,7 @@ using SharpChat.S2CPackets;
namespace SharpChat.Commands
{
public class DeleteMessageCommand : IChatCommand {
public class DeleteMessageCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("delmsg") || (
ctx.NameEquals("delete")

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class JoinChannelCommand : IChatCommand {
public class JoinChannelCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("join");
}

View file

@ -2,7 +2,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class KickBanCommand(MisuzuClient msz) : IChatCommand {
public class KickBanCommand(MisuzuClient msz) : ChatCommand {
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
public bool IsMatch(ChatCommandContext ctx) {

View file

@ -3,7 +3,7 @@ using System.Globalization;
using System.Text;
namespace SharpChat.Commands {
public class NickCommand : IChatCommand {
public class NickCommand : ChatCommand {
private const int MAX_GRAPHEMES = 16;
private const int MAX_BYTES = MAX_GRAPHEMES * 10;

View file

@ -3,7 +3,7 @@ using SharpChat.S2CPackets;
using System.Net;
namespace SharpChat.Commands {
public class PardonAddressCommand(MisuzuClient msz) : IChatCommand {
public class PardonAddressCommand(MisuzuClient msz) : ChatCommand {
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
public bool IsMatch(ChatCommandContext ctx) {

View file

@ -2,7 +2,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class PardonUserCommand(MisuzuClient msz) : IChatCommand {
public class PardonUserCommand(MisuzuClient msz) : ChatCommand {
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
public bool IsMatch(ChatCommandContext ctx) {

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class PasswordChannelCommand : IChatCommand {
public class PasswordChannelCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("pwd")
|| ctx.NameEquals("password");

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class RankChannelCommand : IChatCommand {
public class RankChannelCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("rank")
|| ctx.NameEquals("privilege")

View file

@ -2,7 +2,7 @@
using System.Net;
namespace SharpChat.Commands {
public class RemoteAddressCommand : IChatCommand {
public class RemoteAddressCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("ip")
|| ctx.NameEquals("whois");

View file

@ -1,7 +1,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class ShutdownRestartCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : IChatCommand {
public class ShutdownRestartCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : ChatCommand {
private readonly ManualResetEvent WaitHandle = waitHandle ?? throw new ArgumentNullException(nameof(waitHandle));
private readonly Func<bool> ShutdownCheck = shutdownCheck ?? throw new ArgumentNullException(nameof(shutdownCheck));

View file

@ -2,7 +2,7 @@
using SharpChat.S2CPackets;
namespace SharpChat.Commands {
public class WhisperCommand : IChatCommand {
public class WhisperCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("whisper")
|| ctx.NameEquals("msg");

View file

@ -2,7 +2,7 @@
using System.Text;
namespace SharpChat.Commands {
public class WhoCommand : IChatCommand {
public class WhoCommand : ChatCommand {
public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("who");
}

View file

@ -1,6 +1,6 @@
namespace SharpChat.Config {
public class CachedValue<T>(IConfig config, string name, TimeSpan lifetime, T? fallback) {
private IConfig Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
public class CachedValue<T>(Config config, string name, TimeSpan lifetime, T? fallback) {
private Config Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
private string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
private object ConfigAccess { get; } = new();

View file

@ -1,9 +1,9 @@
namespace SharpChat.Config {
public interface IConfig : IDisposable {
public interface Config : IDisposable {
/// <summary>
/// Creates a proxy object that forces all names to start with the given prefix.
/// </summary>
IConfig ScopeTo(string prefix);
Config ScopeTo(string prefix);
/// <summary>
/// Reads a raw (string) value from the config.

View file

@ -1,6 +1,6 @@
namespace SharpChat.Config {
public class ScopedConfig(IConfig config, string prefix) : IConfig {
private IConfig Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
public class ScopedConfig(Config config, string prefix) : Config {
private Config Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
private string Prefix { get; } = prefix ?? throw new ArgumentNullException(nameof(prefix));
private string GetName(string name) {
@ -19,7 +19,7 @@
return Config.SafeReadValue(GetName(name), fallback);
}
public IConfig ScopeTo(string prefix) {
public Config ScopeTo(string prefix) {
return Config.ScopeTo(GetName(prefix));
}

View file

@ -1,7 +1,7 @@
using System.Text;
namespace SharpChat.Config {
public class StreamConfig : IConfig {
public class StreamConfig : Config {
private Stream Stream { get; }
private StreamReader StreamReader { get; }
private Mutex Lock { get; }
@ -82,7 +82,7 @@ namespace SharpChat.Config {
}
}
public IConfig ScopeTo(string prefix) {
public Config ScopeTo(string prefix) {
if(string.IsNullOrWhiteSpace(prefix))
throw new ArgumentException("Prefix must exist.", nameof(prefix));
if(prefix[^1] != ':')

View file

@ -1,5 +1,5 @@
namespace SharpChat.EventStorage {
public interface IEventStorage {
public interface EventStorage {
void AddEvent(
long id,
string type,

View file

@ -3,7 +3,7 @@ using System.Text;
using System.Text.Json;
namespace SharpChat.EventStorage {
public partial class MariaDBEventStorage(string connString) : IEventStorage {
public partial class MariaDBEventStorage(string connString) : EventStorage {
private string ConnectionString { get; } = connString ?? throw new ArgumentNullException(nameof(connString));
public void AddEvent(

View file

@ -3,7 +3,7 @@ using SharpChat.Config;
namespace SharpChat.EventStorage {
public partial class MariaDBEventStorage {
public static string BuildConnString(IConfig config) {
public static string BuildConnString(Config.Config config) {
return BuildConnString(
config.ReadValue("host", "localhost"),
config.ReadValue("user", string.Empty),

View file

@ -1,7 +1,7 @@
using System.Text.Json;
namespace SharpChat.EventStorage {
public class VirtualEventStorage : IEventStorage {
public class VirtualEventStorage : EventStorage {
private readonly Dictionary<long, StoredEventInfo> Events = [];
public void AddEvent(

View file

@ -0,0 +1,3 @@
namespace SharpChat.Events {
public interface ChatEvent {}
}

View file

@ -1,3 +0,0 @@
namespace SharpChat.Events {
public interface IChatEvent {}
}

View file

@ -13,7 +13,7 @@
bool isPrivate,
bool isAction,
bool isBroadcast
) : IChatEvent {
) : ChatEvent {
public long MessageId { get; } = msgId;
public string ChannelName { get; } = channelName;
public long SenderId { get; } = senderId;

View file

@ -28,7 +28,7 @@ namespace SharpChat.Misuzu {
private CachedValue<string> BaseURL { get; }
private CachedValue<string> SecretKey { get; }
public MisuzuClient(HttpClient httpClient, IConfig config) {
public MisuzuClient(HttpClient httpClient, Config.Config config) {
ArgumentNullException.ThrowIfNull(config);
HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));

View file

@ -56,7 +56,7 @@ namespace SharpChat {
if(hasCancelled) return;
IEventStorage evtStore;
EventStorage.EventStorage evtStore;
if(string.IsNullOrWhiteSpace(config.SafeReadValue("mariadb:host", string.Empty))) {
evtStore = new VirtualEventStorage();
} else {

View file

@ -35,7 +35,7 @@ namespace SharpChat {
private ChatChannel DefaultChannel { get; set; }
public SockChatServer(HttpClient httpClient, MisuzuClient msz, IEventStorage evtStore, IConfig config) {
public SockChatServer(HttpClient httpClient, MisuzuClient msz, EventStorage.EventStorage evtStore, Config.Config config) {
Logger.Write("Initialising Sock Chat server...");
HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
@ -51,7 +51,7 @@ namespace SharpChat {
string[]? channelNames = config.ReadValue("channels", DEFAULT_CHANNELS);
if(channelNames is not null)
foreach(string channelName in channelNames) {
IConfig channelCfg = config.ScopeTo($"channels:{channelName}");
Config.Config channelCfg = config.ScopeTo($"channels:{channelName}");
string name = channelCfg.SafeReadValue("name", string.Empty)!;
if(string.IsNullOrWhiteSpace(name))