diff --git a/IrcStates/Commands.cs b/IrcStates/Commands.cs new file mode 100644 index 0000000..b5bc358 --- /dev/null +++ b/IrcStates/Commands.cs @@ -0,0 +1,22 @@ +namespace IrcStates +{ + public static class Commands + { + public const string Nick = "NICK"; + public const string Join = "JOIN"; + public const string Mode = "MODE"; + public const string Part = "PART"; + public const string Kick = "KICK"; + public const string Quit = "QUIT"; + public const string Error = "ERROR"; + public const string Topic = "TOPIC"; + public const string Privmsg = "PRIVMSG"; + public const string Notice = "NOTICE"; + public const string Tagmsg = "TAGMSG"; + public const string Chghost = "CHGHOST"; + public const string Setname = "SETNAME"; + public const string Away = "AWAY"; + public const string Account = "ACCOUNT"; + public const string Cap = "CAP"; + } +} diff --git a/IrcStates/Emit.cs b/IrcStates/Emit.cs index 3867a61..9ca9cb5 100644 --- a/IrcStates/Emit.cs +++ b/IrcStates/Emit.cs @@ -12,13 +12,13 @@ namespace IrcStates public bool Self { get; set; } public bool SelfSource { get; set; } public bool SelfTarget { get; set; } - public Tests.User User { get; set; } - public Tests.User UserSource { get; set; } - public Tests.User UserTarget { get; set; } - public List Users { get; set; } - public Tests.Channel Channel { get; set; } - public Tests.Channel ChannelSource { get; set; } - public Tests.Channel ChannelTarget { get; set; } + public User User { get; set; } + public User UserSource { get; set; } + public User UserTarget { get; set; } + public List Users { get; set; } + public Channel Channel { get; set; } + public Channel ChannelSource { get; set; } + public Channel ChannelTarget { get; set; } public string Target { get; set; } } } diff --git a/IrcStates/HandlesAttribute.cs b/IrcStates/HandlesAttribute.cs deleted file mode 100644 index 8f3b271..0000000 --- a/IrcStates/HandlesAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace IrcStates -{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] - internal class HandlesAttribute : Attribute - { - private string Command { get; } - - public HandlesAttribute(string command) - { - Command = command; - } - } -} diff --git a/IrcStates/ISupport.cs b/IrcStates/ISupport.cs index b9ad502..afb633d 100644 --- a/IrcStates/ISupport.cs +++ b/IrcStates/ISupport.cs @@ -31,8 +31,10 @@ namespace IrcStates public int? Watch { get; set; } public bool Whox { get; set; } - public void Parse(IList tokens) + public void Parse(IEnumerable tokens) { + if (tokens == null) return; + foreach (var token in tokens) { var split = token.Split('=', 2); @@ -65,7 +67,8 @@ namespace IrcStates Watch = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); break; case "CASEMAPPING": - if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) CaseMapping = caseMapping; + if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) + CaseMapping = caseMapping; break; case "CHANTYPES": ChanTypes = new List {value}; diff --git a/IrcStates/ISupportPrefix.cs b/IrcStates/ISupportPrefix.cs index 94668e3..b319e04 100644 --- a/IrcStates/ISupportPrefix.cs +++ b/IrcStates/ISupportPrefix.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace IrcStates { @@ -6,6 +7,8 @@ namespace IrcStates { public ISupportPrefix(string splitVal) { + if (splitVal == null) throw new ArgumentNullException(nameof(splitVal)); + var split = splitVal.Substring(1).Split(')', 2); Modes = new List {split[0]}; Prefixes = new List {split[1]}; diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs index 582237d..a552890 100644 --- a/IrcStates/Server.cs +++ b/IrcStates/Server.cs @@ -1,9 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Reflection; -using System.Reflection.Metadata.Ecma335; using IrcTokens; namespace IrcStates @@ -29,12 +27,6 @@ namespace IrcStates TempCaps = new Dictionary(); AvailableCaps = new Dictionary(); AgreedCaps = new List(); - RegisterHandlersByAttribute(); - } - - private void RegisterHandlersByAttribute() - { - throw new NotImplementedException(); } public string Name { get; set; } @@ -57,42 +49,11 @@ namespace IrcStates public ISupport ISupport { get; set; } public bool HasCap { get; set; } - public delegate Emit MessageHandler(Server server, Line line); - private Dictionary> Handlers { get; set; } - public override string ToString() { return $"Server(name={Name})"; } - public List<(Line, Emit)> Recv(byte[] data) - { - if (data == null) return null; - - var lines = _decoder.Push(data, data.Length); - if (lines == null) throw new ServerDisconnectedException(); - - return lines.Select(l => (l, ParseTokens(l))).ToList(); - } - - public Emit Parse(Line line) - { - if (line == null || !Handlers.ContainsKey(line.Command)) return null; - var ret = new Emit(); - - var handlers = Handlers[line.Command] - .Select(callback => callback(this, line)) - .Where(emit => emit != null); - - foreach (var emit in handlers) - { - emit.Command = line.Command; - ret = emit; - } - - return ret; - } - public string CaseFold(string str) { return Casemap.CaseFold(ISupport.CaseMapping, str); @@ -142,14 +103,247 @@ namespace IrcStates return HasChannel(name) ? Channels[name] : null; } - [Handles(Numeric.RPL_WELCOME)] - private Emit HandleWelcome(Line line) + public List<(Line, Emit)> Recv(byte[] data) { - NickName = line.Params[0]; - NickNameLower = CaseFold(line.Params[0]); - Registered = true; + if (data == null) return null; + + var lines = _decoder.Push(data, data.Length); + if (lines == null) throw new ServerDisconnectedException(); + + return lines.Select(l => (l, Parse(l))).ToList(); + } + + public Emit Parse(Line line) + { + if (line == null) return null; + + switch (line.Command) + { + case Numeric.RPL_WELCOME: return HandleWelcome(line); + case Numeric.RPL_ISUPPORT: return HandleISupport(line); + case Numeric.RPL_MOTDSTART: + case Numeric.RPL_MOTD: + return HandleMotd(line); + case Commands.Nick: return HandleNick(line); + case Commands.Join: return HandleJoin(line); + case Commands.Part: return HandlePart(line); + case Commands.Kick: return HandleKick(line); + case Commands.Quit: return HandleQuit(line); + case Commands.Error: return HandleError(line); + case Numeric.RPL_NAMREPLY: return HandleNames(line); + case Numeric.RPL_CREATIONTIME: return HandleCreationTime(line); + case Commands.Topic: return HandleTopic(line); + case Numeric.RPL_TOPIC: return HandleTopicNumeric(line); + case Numeric.RPL_TOPICWHOTIME: return HandleTopicTime(line); + case Commands.Mode: return HandleMode(line); + case Numeric.RPL_CHANNELMODEIS: return HandleChannelModeIs(line); + case Numeric.RPL_UMODEIS: return HandleUModeIs(line); + case Commands.Privmsg: + case Commands.Notice: + case Commands.Tagmsg: + return HandleMessage(line); + case Numeric.RPL_VISIBLEHOST: return HandleVisibleHost(line); + case Numeric.RPL_WHOREPLY: return HandleWhoReply(line); + case Numeric.RPL_WHOSPCRPL: return HandleWhox(line); + case Numeric.RPL_WHOISUSER: return HandleWhoIsUser(line); + case Commands.Chghost: return HandleChghost(line); + case Commands.Setname: return HandleSetname(line); + case Commands.Away: return HandleAway(line); + case Commands.Account: return HandleAccount(line); + case Commands.Cap: return HandleCap(line); + case Numeric.RPL_LOGGEDIN: return HandleLoggedIn(line); + case Numeric.RPL_LOGGEDOUT: return HandleLoggedOut(line); + } + return new Emit(); } + private Emit HandleSetname(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleAway(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleAccount(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleCap(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleLoggedIn(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleChghost(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleWhoIsUser(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleWhox(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleWhoReply(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleVisibleHost(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleMessage(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleUModeIs(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleChannelModeIs(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleMode(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleTopicTime(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleTopicNumeric(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleTopic(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleCreationTime(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleNames(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleError(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleQuit(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleLoggedOut(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleKick(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandlePart(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleJoin(Line line) + { + throw new NotImplementedException(); + } + + private Emit HandleNick(Line line) + { + var nick = line.Params[0]; + var nickLower = CaseFold(line.Hostmask.NickName); + + var emit = new Emit(); + + if (Users.ContainsKey(nickLower)) + { + var user = Users[nick]; + emit.User = user; + + var oldNickLower = user.NickNameLower; + var newNickLower = CaseFold(nick); + user.SetNickName(nick, newNickLower); + Users[newNickLower] = user; + foreach (var channelLower in user.Channels) + { + var channel = Channels[channelLower]; + var channelUser = channel.Users[oldNickLower]; + channel.Users.Remove(oldNickLower); + channel.Users[newNickLower] = channelUser; + } + } + + if (nickLower == NickNameLower) + { + emit.Self = true; + NickName = nick; + NickNameLower = CaseFold(nick); + } + + return emit; + } + + private Emit HandleMotd(Line line) + { + if (line.Command == Numeric.RPL_MOTDSTART) + { + Motd.Clear(); + } + + var emit = new Emit {Text = line.Params[1]}; + Motd.Add(line.Params[1]); + return emit; + } + + private Emit HandleISupport(Line line) + { + ISupport = new ISupport(); + ISupport.Parse(line.Params.Skip(1).SkipLast(1)); + return new Emit(); + } + + + private Emit HandleWelcome(Line line) + { + NickName = line.Params[0]; + NickNameLower = CaseFold(line.Params[0]); + Registered = true; + return new Emit(); + } } }