From a0fbcf83c57d15bf4bbdd2f18a8b8d539e3e4a1a Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 14 May 2020 02:10:04 -0400 Subject: [PATCH] Implement lots of things --- IrcStates/ChannelUser.cs | 22 +++- IrcStates/Extensions.cs | 8 ++ IrcStates/ISupport.cs | 10 +- IrcStates/ISupportPrefix.cs | 12 ++- IrcStates/Server.cs | 206 ++++++++++++++++++++++++++++++++---- IrcStates/Tests/Channel.cs | 12 +-- 6 files changed, 231 insertions(+), 39 deletions(-) diff --git a/IrcStates/ChannelUser.cs b/IrcStates/ChannelUser.cs index 220a9c4..fab881c 100644 --- a/IrcStates/ChannelUser.cs +++ b/IrcStates/ChannelUser.cs @@ -4,11 +4,29 @@ namespace IrcStates { public class ChannelUser { - public List Modes { get; set; } - public ChannelUser() { Modes = new List(); } + + public List Modes { get; set; } + + protected bool Equals(ChannelUser other) + { + return other != null && Equals(Modes, other.Modes); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ChannelUser) obj); + } + + public override int GetHashCode() + { + return Modes != null ? Modes.GetHashCode() : 0; + } } } diff --git a/IrcStates/Extensions.cs b/IrcStates/Extensions.cs index dde0edb..e604928 100644 --- a/IrcStates/Extensions.cs +++ b/IrcStates/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -28,5 +29,12 @@ namespace IrcStates ? Delegate.CreateDelegate(getType(types.ToArray()), methodInfo) : Delegate.CreateDelegate(getType(types.ToArray()), target, methodInfo); } + + public static void Update(this Dictionary dict, Dictionary other) + { + if (dict == null || other == null || !other.Any()) return; + + foreach (var (key, value) in other) dict[key] = value; + } } } diff --git a/IrcStates/ISupport.cs b/IrcStates/ISupport.cs index 1e89b6d..2fc27dc 100644 --- a/IrcStates/ISupport.cs +++ b/IrcStates/ISupport.cs @@ -14,7 +14,10 @@ namespace IrcStates Raw = new Dictionary(); Modes = 3; CaseMapping = Casemap.CaseMapping.Rfc1459; - // TODO: add remaining defaults, change properties to normal members as needed + Prefix = new ISupportPrefix("(ov)@+"); + ChanModes = new ISupportChanModes("b,k,l,imnpst"); + StatusMsg = new List(); + Whox = false; } public Dictionary Raw { get; set; } @@ -35,7 +38,7 @@ namespace IrcStates public void Parse(IEnumerable tokens) { if (tokens == null) return; - + // remove first and last tokens = tokens.Skip(1).SkipLast(1); @@ -71,8 +74,7 @@ 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 b535212..fb5114b 100644 --- a/IrcStates/ISupportPrefix.cs +++ b/IrcStates/ISupportPrefix.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; namespace IrcStates { @@ -11,8 +12,10 @@ namespace IrcStates 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]}; + Modes = new List(); + Modes.AddRange(split[0].Select(c => c.ToString(CultureInfo.InvariantCulture))); + Prefixes = new List(); + Prefixes.AddRange(split[1].Select(c => c.ToString(CultureInfo.InvariantCulture))); } public List Modes { get; set; } @@ -22,9 +25,10 @@ namespace IrcStates { return FromMode(mode.ToString(CultureInfo.InvariantCulture)); } + public string FromMode(string mode) { - return Modes.Contains(mode) ? Modes[Modes.IndexOf(mode)] : null; + return Modes.Contains(mode) ? Prefixes[Modes.IndexOf(mode)] : null; } public string FromPrefix(char prefix) @@ -34,7 +38,7 @@ namespace IrcStates public string FromPrefix(string prefix) { - return Prefixes.Contains(prefix) ? Prefixes[Prefixes.IndexOf(prefix)] : null; + return Prefixes.Contains(prefix) ? Modes[Prefixes.IndexOf(prefix)] : null; } } } diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs index ea7dd9e..d9022a0 100644 --- a/IrcStates/Server.cs +++ b/IrcStates/Server.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Runtime.Serialization; using IrcTokens; namespace IrcStates @@ -210,32 +209,161 @@ namespace IrcStates private Emit HandleSetname(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var realname = line.Params[0]; + var nicknameLower = CaseFold(line.Hostmask.NickName); + + if (nicknameLower == NickNameLower) + { + emit.Self = true; + RealName = realname; + } + + if (Users.ContainsKey(nicknameLower)) + { + var user = Users[nicknameLower]; + emit.User = user; + user.RealName = realname; + } + + return emit; } private Emit HandleAway(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var away = line.Params.FirstOrDefault(); + var nicknameLower = CaseFold(line.Hostmask.NickName); + + if (nicknameLower == NickNameLower) + { + emit.Self = true; + Away = away; + } + + if (Users.ContainsKey(nicknameLower)) + { + var user = Users[nicknameLower]; + emit.User = user; + user.Away = away; + } + + return emit; } private Emit HandleAccount(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var account = line.Params[0].Trim('*'); + var nicknameLower = CaseFold(line.Hostmask.NickName); + + if (nicknameLower == NickNameLower) + { + emit.Self = true; + Account = account; + } + + if (Users.ContainsKey(nicknameLower)) + { + var user = Users[nicknameLower]; + emit.User = user; + user.Account = account; + } + + return emit; } private Emit HandleCap(Line line) { - throw new NotImplementedException(); + HasCap = true; + var subcommand = line.Params[1].ToUpperInvariant(); + var multiline = line.Params[2] == "*"; + var caps = line.Params[multiline ? 3 : 2]; + + var tokens = new Dictionary(); + var tokensStr = new List(); + foreach (var cap in caps.Split(' ', StringSplitOptions.RemoveEmptyEntries)) + { + tokensStr.Add(cap); + var kv = cap.Split('=', 2); + tokens[kv[0]] = kv.Length > 1 ? kv[1] : string.Empty; + } + + var emit = new Emit(); + emit.Subcommand = subcommand; + emit.Finished = !multiline; + emit.Tokens = tokensStr; + + switch (subcommand) + { + case "LS": + TempCaps = tokens; + if (!multiline) + { + AvailableCaps = TempCaps; + TempCaps.Clear(); + } + + break; + case "NEW": + AvailableCaps.Update(tokens); + break; + case "DEL": + foreach (var key in tokens.Keys.Where(key => AvailableCaps.ContainsKey(key))) + { + AvailableCaps.Remove(key); + if (AgreedCaps.Contains(key)) AgreedCaps.Remove(key); + } + + break; + case "ACK": + foreach (var key in tokens.Keys) + if (key.StartsWith('-')) + { + var k = key.Substring(1); + if (AgreedCaps.Contains(k)) AgreedCaps.Remove(k); + } + else if (!AgreedCaps.Contains(key) && !AvailableCaps.ContainsKey(key)) + { + AgreedCaps.Add(key); + } + + break; + } + + return emit; } private Emit HandleLoggedIn(Line line) { - throw new NotImplementedException(); + SelfHostmask(new Hostmask(line.Params[1])); + Account = line.Params[2]; + return new Emit(); } private Emit HandleChghost(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var username = line.Params[0]; + var hostname = line.Params[1]; + var nicknameLower = CaseFold(line.Hostmask.NickName); + + if (nicknameLower == NickNameLower) + { + emit.Self = true; + UserName = username; + HostName = hostname; + } + + if (Users.ContainsKey(nicknameLower)) + { + var user = Users[nicknameLower]; + emit.User = user; + user.UserName = username; + user.HostName = hostname; + } + + return emit; } private Emit HandleWhoIsUser(Line line) @@ -280,22 +408,63 @@ namespace IrcStates private Emit HandleTopicTime(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var channelLower = CaseFold(line.Params[1]); + if (Channels.ContainsKey(channelLower)) + { + var channel = Channels[channelLower]; + emit.Channel = channel; + channel.TopicSetter = line.Params[2]; + channel.TopicTime = DateTimeOffset + .FromUnixTimeSeconds(int.Parse(line.Params[3], CultureInfo.InvariantCulture)).DateTime; + } + + return emit; } private Emit HandleTopicNumeric(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var channelLower = CaseFold(line.Params[1]); + if (Channels.ContainsKey(channelLower)) + { + var channel = Channels[channelLower]; + emit.Channel = channel; + Channels[channelLower].Topic = line.Params[2]; + } + + return emit; } private Emit HandleTopic(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var channelLower = CaseFold(line.Params[0]); + if (Channels.ContainsKey(channelLower)) + { + var channel = Channels[channelLower]; + emit.Channel = channel; + channel.Topic = line.Params[1]; + channel.TopicSetter = line.Hostmask.ToString(); + channel.TopicTime = DateTime.UtcNow; + } + + return emit; } private Emit HandleCreationTime(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var channelLower = CaseFold(line.Params[1]); + if (Channels.ContainsKey(channelLower)) + { + var channel = Channels[channelLower]; + emit.Channel = channel; + channel.Created = DateTimeOffset + .FromUnixTimeSeconds(int.Parse(line.Params[2], CultureInfo.InvariantCulture)).DateTime; + } + + return emit; } private Emit HandleNames(Line line) @@ -324,10 +493,7 @@ namespace IrcStates var hostmask = new Hostmask(nick.Substring(modes.Length)); var nickLower = CaseFold(hostmask.NickName); - if (!Users.ContainsKey(nickLower)) - { - AddUser(hostmask.NickName, nickLower); - } + if (!Users.ContainsKey(nickLower)) AddUser(hostmask.NickName, nickLower); var user = Users[nickLower]; users.Add(user); @@ -339,12 +505,8 @@ namespace IrcStates if (nickLower == NickNameLower) SelfHostmask(hostmask); foreach (var mode in modes.Select(c => c.ToString(CultureInfo.InvariantCulture))) - { if (!channelUser.Modes.Contains(mode)) - { channelUser.Modes.Add(mode); - } - } } return emit; @@ -375,9 +537,7 @@ namespace IrcStates Users.Remove(nickLower); emit.User = user; foreach (var channel in user.Channels.Select(c => Channels[c])) - { channel.Users.Remove(user.NickNameLower); - } } return emit; @@ -385,7 +545,9 @@ namespace IrcStates private Emit HandleLoggedOut(Line line) { - throw new NotImplementedException(); + Account = null; + SelfHostmask(new Hostmask(line.Params[1])); + return new Emit(); } private Emit HandleKick(Line line) diff --git a/IrcStates/Tests/Channel.cs b/IrcStates/Tests/Channel.cs index fea4ddb..64a845c 100644 --- a/IrcStates/Tests/Channel.cs +++ b/IrcStates/Tests/Channel.cs @@ -169,11 +169,9 @@ namespace IrcStates.Tests var chanUser1 = channel.Users[user.NickNameLower]; var chanUser2 = channel.Users[_server.NickNameLower]; - CollectionAssert.AreEqual( - new Dictionary - { - {user.NickNameLower, chanUser1}, {_server.NickNameLower, chanUser2} - }, channel.Users); + Assert.AreEqual(2, channel.Users.Count); + CollectionAssert.AreEqual(chanUser1.Modes, channel.Users[user.NickNameLower].Modes); + CollectionAssert.AreEqual(chanUser2.Modes, channel.Users[_server.NickNameLower].Modes); CollectionAssert.AreEqual(new List {"o", "v"}, chanUser1.Modes); Assert.AreEqual(channel.NameLower, user.Channels.Single()); } @@ -181,12 +179,12 @@ namespace IrcStates.Tests [TestMethod] public void UserhostInNames() { - _server.Parse(new Line("353 * * #chan :nickname!user@host other@user2@host2")); + _server.Parse(new Line("353 * * #chan :nickname!user@host other!user2@host2")); Assert.AreEqual("user", _server.UserName); Assert.AreEqual("host", _server.HostName); var user = _server.Users["other"]; - Assert.AreEqual("user2", user.HostName); + Assert.AreEqual("user2", user.UserName); Assert.AreEqual("host2", user.HostName); }