about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBen Harris <ben@tilde.team>2020-05-14 17:13:46 -0400
committerBen Harris <ben@tilde.team>2020-05-14 17:13:46 -0400
commitaef26a40cbc6835aae0d293a523f5a7681b9a436 (patch)
tree38bce3840a24d3a83e3243a64462ccab99bfdf76
parenta0fbcf83c57d15bf4bbdd2f18a8b8d539e3e4a1a (diff)
wow lots of tests passing!
-rw-r--r--IrcStates/Channel.cs2
-rw-r--r--IrcStates/Extensions.cs2
-rw-r--r--IrcStates/ISupport.cs23
-rw-r--r--IrcStates/ISupportChanModes.cs18
-rw-r--r--IrcStates/Server.cs390
-rw-r--r--IrcStates/Tests/Cap.cs5
-rw-r--r--IrcStates/Tests/Mode.cs6
7 files changed, 352 insertions, 94 deletions
diff --git a/IrcStates/Channel.cs b/IrcStates/Channel.cs
index 16b4654..21ebb25 100644
--- a/IrcStates/Channel.cs
+++ b/IrcStates/Channel.cs
@@ -40,7 +40,7 @@ namespace IrcStates
             {
                 if (!ListModes.ContainsKey(ch)) ListModes[ch] = new List<string>();
 
-                if (ListModes[ch].Contains(param)) ListModes[ch].Add(param ?? string.Empty);
+                if (!ListModes[ch].Contains(param)) ListModes[ch].Add(param ?? string.Empty);
             }
             else
             {
diff --git a/IrcStates/Extensions.cs b/IrcStates/Extensions.cs
index e604928..181ac80 100644
--- a/IrcStates/Extensions.cs
+++ b/IrcStates/Extensions.cs
@@ -30,7 +30,7 @@ namespace IrcStates
                 : Delegate.CreateDelegate(getType(types.ToArray()), target, methodInfo);
         }
 
-        public static void Update<TKey, TValue>(this Dictionary<TKey, TValue> dict, Dictionary<TKey, TValue> other)
+        public static void UpdateWith<TKey, TValue>(this Dictionary<TKey, TValue> dict, Dictionary<TKey, TValue> other)
         {
             if (dict == null || other == null || !other.Any()) return;
 
diff --git a/IrcStates/ISupport.cs b/IrcStates/ISupport.cs
index 2fc27dc..db2f537 100644
--- a/IrcStates/ISupport.cs
+++ b/IrcStates/ISupport.cs
@@ -16,6 +16,7 @@ namespace IrcStates
             CaseMapping = Casemap.CaseMapping.Rfc1459;
             Prefix      = new ISupportPrefix("(ov)@+");
             ChanModes   = new ISupportChanModes("b,k,l,imnpst");
+            ChanTypes   = new List<string> {"#"};
             StatusMsg   = new List<string>();
             Whox        = false;
         }
@@ -46,10 +47,14 @@ namespace IrcStates
             {
                 var split = token.Split('=', 2);
                 var key   = split[0];
-                var value = split[1];
-
-                if (split.Length > 1) Raw[key] = value;
 
+                var value = string.Empty;
+                if (split.Length > 1)
+                {
+                    value = split[1];
+                    Raw[key] = value;
+                }
+                
                 switch (split[0])
                 {
                     case "NETWORK":
@@ -62,7 +67,8 @@ namespace IrcStates
                         Prefix = new ISupportPrefix(value);
                         break;
                     case "STATUSMSG":
-                        StatusMsg = new List<string> {value};
+                        StatusMsg = new List<string>();
+                        StatusMsg.AddRange(value.Select(c => c.ToString(CultureInfo.InvariantCulture)));
                         break;
                     case "MODES":
                         Modes = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
@@ -77,16 +83,17 @@ namespace IrcStates
                         if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) CaseMapping = caseMapping;
                         break;
                     case "CHANTYPES":
-                        ChanTypes = new List<string> {value};
+                        ChanTypes = new List<string>();
+                        ChanTypes.AddRange(value.Select(c => c.ToString(CultureInfo.InvariantCulture)));
                         break;
                     case "CALLERID":
-                        CallerId = string.IsNullOrEmpty(value) ? value : "g";
+                        CallerId = string.IsNullOrEmpty(value) ? "g" : value;
                         break;
                     case "EXCEPTS":
-                        Excepts = string.IsNullOrEmpty(value) ? value : "e";
+                        Excepts = string.IsNullOrEmpty(value) ? "e" : value;
                         break;
                     case "INVEX":
-                        Invex = string.IsNullOrEmpty(value) ? value : "I";
+                        Invex = string.IsNullOrEmpty(value) ? "I" : value;
                         break;
                     case "WHOX":
                         Whox = true;
diff --git a/IrcStates/ISupportChanModes.cs b/IrcStates/ISupportChanModes.cs
index 9bf565c..74a0579 100644
--- a/IrcStates/ISupportChanModes.cs
+++ b/IrcStates/ISupportChanModes.cs
@@ -1,4 +1,6 @@
 using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
 
 namespace IrcStates
 {
@@ -9,10 +11,18 @@ namespace IrcStates
             if (splitVal == null) return;
 
             var split = splitVal.Split(',', 4);
-            ListModes     = new List<string> {split[0]};
-            SettingBModes = new List<string> {split[1]};
-            SettingCModes = new List<string> {split[2]};
-            SettingDModes = new List<string> {split[3]};
+            
+            ListModes = new List<string>();
+            ListModes.AddRange(split[0].Select(c => c.ToString(CultureInfo.InvariantCulture)));
+            
+            SettingBModes = new List<string>();
+            SettingBModes.AddRange(split[1].Select(c => c.ToString(CultureInfo.InvariantCulture)));
+            
+            SettingCModes = new List<string>();
+            SettingCModes.AddRange(split[2].Select(c => c.ToString(CultureInfo.InvariantCulture)));
+            
+            SettingDModes = new List<string>();
+            SettingDModes.AddRange(split[3].Select(c => c.ToString(CultureInfo.InvariantCulture)));
         }
 
         public List<string> ListModes { get; set; }
diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs
index d9022a0..ca812f6 100644
--- a/IrcStates/Server.cs
+++ b/IrcStates/Server.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.Design;
 using System.Globalization;
 using System.Linq;
 using IrcTokens;
@@ -59,25 +60,26 @@ namespace IrcStates
             return Casemap.CaseFold(ISupport.CaseMapping, str);
         }
 
-        public bool CaseFoldEquals(string s1, string s2)
+        private bool CaseFoldEquals(string s1, string s2)
         {
             return CaseFold(s1) == CaseFold(s2);
         }
 
-        public bool IsMe(string nickname)
+        private bool IsMe(string nickname)
         {
             return CaseFold(nickname) == NickNameLower;
         }
 
-        public bool HasUser(string nickname)
+        private bool HasUser(string nickname)
         {
             return Users.ContainsKey(CaseFold(nickname));
         }
 
-        private void AddUser(string nickname, string nicknameLower)
+        private User AddUser(string nickname, string nicknameLower)
         {
             var user = CreateUser(nickname, nicknameLower);
             Users[nicknameLower] = user;
+            return user;
         }
 
         private User CreateUser(string nickname, string nicknameLower)
@@ -87,18 +89,18 @@ namespace IrcStates
             return user;
         }
 
-        public bool IsChannel(string target)
+        private bool IsChannel(string target)
         {
             return !string.IsNullOrEmpty(target) &&
                    ISupport.ChanTypes.Contains(target[0].ToString(CultureInfo.InvariantCulture));
         }
 
-        public bool HasChannel(string name)
+        private bool HasChannel(string name)
         {
             return Channels.ContainsKey(CaseFold(name));
         }
 
-        public Channel GetChannel(string name)
+        private Channel GetChannel(string name)
         {
             return HasChannel(name) ? Channels[name] : null;
         }
@@ -138,7 +140,7 @@ namespace IrcStates
                     if (!user.Channels.Any()) Users.Remove(nickLower);
                 }
 
-                if (nickLower == NickNameLower)
+                if (IsMe(nickName))
                 {
                     Channels.Remove(channelLower);
                     foreach (var userToRemove in channel.Users.Keys.Select(u => Users[u]))
@@ -152,6 +154,53 @@ namespace IrcStates
             return (emit, user);
         }
 
+        private void SetChannelModes(Channel channel, IEnumerable<(bool, string)> modes, IList<string> parameters)
+        {
+            foreach (var (add, c) in modes)
+            {
+                var listMode = ISupport.ChanModes.ListModes.Contains(c);
+                if (ISupport.Prefix.Modes.Contains(c))
+                {
+                    var nicknameLower = CaseFold(parameters.First());
+                    parameters.RemoveAt(0);
+                    if (!HasUser(nicknameLower)) continue;
+
+                    var channelUser = channel.Users[nicknameLower];
+                    if (add)
+                    {
+                        if (!channelUser.Modes.Contains(c))
+                        {
+                            channelUser.Modes.Add(c);
+                        }
+                    }
+                    else if (channelUser.Modes.Contains(c))
+                    {
+                        channelUser.Modes.Remove(c);
+                    }
+                }
+                else if (add && (listMode ||
+                                 ISupport.ChanModes.SettingBModes.Contains(c) ||
+                                 ISupport.ChanModes.SettingCModes.Contains(c)))
+                {
+                    channel.AddMode(c, parameters.First(), listMode);
+                    parameters.RemoveAt(0);
+                }
+                else if (!add && (listMode || ISupport.ChanModes.SettingBModes.Contains(c)))
+                {
+                    channel.RemoveMode(c, parameters.First());
+                    parameters.RemoveAt(0);
+                }
+                else if (add)
+                {
+                    channel.AddMode(c, null, false);
+                }
+                else
+                {
+                    channel.RemoveMode(c, null);
+                }
+            }
+        }
+
         public List<(Line, Emit)> Recv(byte[] data)
         {
             if (data == null) return null;
@@ -166,45 +215,49 @@ namespace IrcStates
         {
             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);
-            }
+            var emit = line.Command switch
+            {
+                Numeric.RPL_WELCOME       => HandleWelcome(line),
+                Numeric.RPL_ISUPPORT      => HandleISupport(line),
+                Numeric.RPL_MOTDSTART     => HandleMotd(line),
+                Numeric.RPL_MOTD          => HandleMotd(line),
+                Commands.Nick             => HandleNick(line),
+                Commands.Join             => HandleJoin(line),
+                Commands.Part             => HandlePart(line),
+                Commands.Kick             => HandleKick(line),
+                Commands.Quit             => HandleQuit(line),
+                Commands.Error            => HandleError(line),
+                Numeric.RPL_NAMREPLY      => HandleNames(line),
+                Numeric.RPL_CREATIONTIME  => HandleCreationTime(line),
+                Commands.Topic            => HandleTopic(line),
+                Numeric.RPL_TOPIC         => HandleTopicNumeric(line),
+                Numeric.RPL_TOPICWHOTIME  => HandleTopicTime(line),
+                Commands.Mode             => HandleMode(line),
+                Numeric.RPL_CHANNELMODEIS => HandleChannelModeIs(line),
+                Numeric.RPL_UMODEIS       => HandleUModeIs(line),
+                Commands.Privmsg          => HandleMessage(line),
+                Commands.Notice           => HandleMessage(line),
+                Commands.Tagmsg           => HandleMessage(line),
+                Numeric.RPL_VISIBLEHOST   => HandleVisibleHost(line),
+                Numeric.RPL_WHOREPLY      => HandleWhoReply(line),
+                Numeric.RPL_WHOSPCRPL     => HandleWhox(line),
+                Numeric.RPL_WHOISUSER     => HandleWhoIsUser(line),
+                Commands.Chghost          => HandleChghost(line),
+                Commands.Setname          => HandleSetname(line),
+                Commands.Away             => HandleAway(line),
+                Commands.Account          => HandleAccount(line),
+                Commands.Cap              => HandleCap(line),
+                Numeric.RPL_LOGGEDIN      => HandleLoggedIn(line),
+                Numeric.RPL_LOGGEDOUT     => HandleLoggedOut(line),
+                _                         => null
+            };
+
+            if (emit != null)
+                emit.Command = line.Command;
+            else
+                emit = new Emit();
 
-            return new Emit();
+            return emit;
         }
 
         private Emit HandleSetname(Line line)
@@ -213,7 +266,7 @@ namespace IrcStates
             var realname      = line.Params[0];
             var nicknameLower = CaseFold(line.Hostmask.NickName);
 
-            if (nicknameLower == NickNameLower)
+            if (IsMe(nicknameLower))
             {
                 emit.Self = true;
                 RealName  = realname;
@@ -235,7 +288,7 @@ namespace IrcStates
             var away          = line.Params.FirstOrDefault();
             var nicknameLower = CaseFold(line.Hostmask.NickName);
 
-            if (nicknameLower == NickNameLower)
+            if (IsMe(nicknameLower))
             {
                 emit.Self = true;
                 Away      = away;
@@ -257,7 +310,7 @@ namespace IrcStates
             var account       = line.Params[0].Trim('*');
             var nicknameLower = CaseFold(line.Hostmask.NickName);
 
-            if (nicknameLower == NickNameLower)
+            if (IsMe(nicknameLower))
             {
                 emit.Self = true;
                 Account   = account;
@@ -289,24 +342,21 @@ namespace IrcStates
                 tokens[kv[0]] = kv.Length > 1 ? kv[1] : string.Empty;
             }
 
-            var emit = new Emit();
-            emit.Subcommand = subcommand;
-            emit.Finished   = !multiline;
-            emit.Tokens     = tokensStr;
+            var emit = new Emit {Subcommand = subcommand, Finished = !multiline, Tokens = tokensStr};
 
             switch (subcommand)
             {
                 case "LS":
-                    TempCaps = tokens;
+                    TempCaps.UpdateWith(tokens);
                     if (!multiline)
                     {
-                        AvailableCaps = TempCaps;
+                        AvailableCaps.UpdateWith(TempCaps);
                         TempCaps.Clear();
                     }
 
                     break;
                 case "NEW":
-                    AvailableCaps.Update(tokens);
+                    AvailableCaps.UpdateWith(tokens);
                     break;
                 case "DEL":
                     foreach (var key in tokens.Keys.Where(key => AvailableCaps.ContainsKey(key)))
@@ -323,7 +373,7 @@ namespace IrcStates
                             var k = key.Substring(1);
                             if (AgreedCaps.Contains(k)) AgreedCaps.Remove(k);
                         }
-                        else if (!AgreedCaps.Contains(key) && !AvailableCaps.ContainsKey(key))
+                        else if (!AgreedCaps.Contains(key) && AvailableCaps.ContainsKey(key))
                         {
                             AgreedCaps.Add(key);
                         }
@@ -348,7 +398,7 @@ namespace IrcStates
             var hostname      = line.Params[1];
             var nicknameLower = CaseFold(line.Hostmask.NickName);
 
-            if (nicknameLower == NickNameLower)
+            if (IsMe(nicknameLower))
             {
                 emit.Self = true;
                 UserName  = username;
@@ -368,42 +418,234 @@ namespace IrcStates
 
         private Emit HandleWhoIsUser(Line line)
         {
-            throw new NotImplementedException();
+            var emit     = new Emit();
+            var nickname = line.Params[1];
+            var username = line.Params[2];
+            var hostname = line.Params[3];
+            var realname = line.Params[5];
+
+            if (IsMe(nickname))
+            {
+                emit.Self = true;
+                UserName  = username;
+                HostName  = hostname;
+                RealName  = realname;
+            }
+
+            if (HasUser(nickname))
+            {
+                var user = Users[CaseFold(nickname)];
+                emit.User     = user;
+                user.UserName = username;
+                user.HostName = hostname;
+                user.RealName = realname;
+            }
+
+            return emit;
         }
 
         private Emit HandleWhox(Line line)
         {
-            throw new NotImplementedException();
+            var emit = new Emit();
+            if (line.Params[1] == WhoType && line.Params.Count == 8)
+            {
+                var nickname = line.Params[5];
+                var username = line.Params[2];
+                var hostname = line.Params[4];
+                var realname = line.Params[7];
+                var account  = line.Params[6] == "0" ? null : line.Params[6];
+
+                if (IsMe(nickname))
+                {
+                    emit.Self = true;
+                    UserName  = username;
+                    HostName  = hostname;
+                    RealName  = realname;
+                    Account   = account;
+                }
+
+                if (HasUser(nickname))
+                {
+                    var user = Users[CaseFold(nickname)];
+                    emit.User     = user;
+                    user.UserName = username;
+                    user.HostName = hostname;
+                    user.RealName = realname;
+                    user.Account  = account;
+                }
+            }
+
+            return emit;
         }
 
         private Emit HandleWhoReply(Line line)
         {
-            throw new NotImplementedException();
+            var emit     = new Emit {Target = line.Params[1]};
+            var nickname = line.Params[5];
+            var username = line.Params[2];
+            var hostname = line.Params[3];
+            var realname = line.Params[7].Split(' ', 2)[1];
+
+            if (IsMe(nickname))
+            {
+                emit.Self = true;
+                UserName  = username;
+                HostName  = hostname;
+                RealName  = realname;
+            }
+
+            if (HasUser(nickname))
+            {
+                var user = Users[CaseFold(nickname)];
+                emit.User     = user;
+                user.UserName = username;
+                user.HostName = hostname;
+                user.RealName = realname;
+            }
+
+            return emit;
         }
 
         private Emit HandleVisibleHost(Line line)
         {
-            throw new NotImplementedException();
+            var split = line.Params[1].Split('@', 2);
+            switch (split.Length)
+            {
+                case 1:
+                    HostName = split[0];
+                    break;
+                case 2:
+                    HostName = split[1];
+                    UserName = split[0];
+                    break;
+            }
+
+            return new Emit();
         }
 
         private Emit HandleMessage(Line line)
         {
-            throw new NotImplementedException();
+            var emit                       = new Emit();
+            var message                    = line.Params.Count > 1 ? line.Params[1] : null;
+            if (message != null) emit.Text = message;
+
+            var nickLower = CaseFold(line.Hostmask.NickName);
+            if (IsMe(nickLower))
+            {
+                emit.SelfSource = true;
+                SelfHostmask(line.Hostmask);
+            }
+
+            var user = HasUser(nickLower)
+                ? Users[nickLower]
+                : AddUser(line.Hostmask.NickName, nickLower);
+            emit.User = user;
+
+            if (line.Hostmask.UserName != null) user.UserName = line.Hostmask.UserName;
+            if (line.Hostmask.HostName != null) user.HostName = line.Hostmask.HostName;
+
+            var target    = line.Params[0];
+            var statusMsg = new List<string>();
+            while (target.Length > 0)
+            {
+                var t = target[0].ToString(CultureInfo.InvariantCulture);
+                if (ISupport.StatusMsg.Contains(t))
+                {
+                    statusMsg.Add(t);
+                    target = target.Substring(1);
+                }
+                else
+                    break;
+            }
+
+            emit.Target = line.Params[0];
+
+            if (IsChannel(target) && HasChannel(target))
+            {
+                emit.Channel = Channels[CaseFold(target)];
+            }
+            else if (IsMe(target))
+            {
+                emit.SelfTarget = true;
+            }
+
+            return emit;
         }
 
         private Emit HandleUModeIs(Line line)
         {
-            throw new NotImplementedException();
+            foreach (var c in line.Params[1]
+                .TrimStart('+')
+                .Select(m => m.ToString(CultureInfo.InvariantCulture))
+                .Where(m => !Modes.Contains(m)))
+            {
+                Modes.Add(c);
+            }
+
+            return new Emit();
         }
 
         private Emit HandleChannelModeIs(Line line)
         {
-            throw new NotImplementedException();
+            var emit = new Emit();
+            if (HasChannel(line.Params[1]))
+            {
+                var channel = Channels[CaseFold(line.Params[1])];
+                emit.Channel = channel;
+                var modes = line.Params[2]
+                    .TrimStart('+')
+                    .Select(p => (true, p.ToString(CultureInfo.InvariantCulture)));
+                var parameters = line.Params.Skip(3).ToList();
+                SetChannelModes(channel, modes, parameters);
+            }
+
+            return emit;
         }
 
         private Emit HandleMode(Line line)
         {
-            throw new NotImplementedException();
+            var emit       = new Emit();
+            var target     = line.Params[0];
+            var modeString = line.Params[1];
+            var parameters = line.Params.Skip(2).ToList();
+
+            var modifier = '+';
+            var modes    = new List<(bool, string)>();
+            var tokens   = new List<string>();
+
+            foreach (var c in modeString)
+            {
+                if (new[] {'+', '-'}.Contains(c))
+                {
+                    modifier = c;
+                }
+                else
+                {
+                    modes.Add((modifier == '+', c.ToString(CultureInfo.InvariantCulture)));
+                    tokens.Add($"{modifier}{c}");
+                }
+            }
+
+            emit.Tokens = tokens;
+
+            if (IsMe(target))
+            {
+                emit.SelfTarget = true;
+                foreach (var (add, c) in modes)
+                {
+                    if (add && !Modes.Contains(c))
+                        Modes.Add(c);
+                    else if (Modes.Contains(c)) Modes.Remove(c);
+                }
+            }
+            else if (HasChannel(target))
+            {
+                var channel = GetChannel(CaseFold(target));
+                emit.Channel = channel;
+                SetChannelModes(channel, modes, parameters);
+            }
+
+            return emit;
         }
 
         private Emit HandleTopicTime(Line line)
@@ -502,7 +744,7 @@ namespace IrcStates
                 if (hostmask.UserName != null) user.UserName = hostmask.UserName;
                 if (hostmask.HostName != null) user.HostName = hostmask.HostName;
 
-                if (nickLower == NickNameLower) SelfHostmask(hostmask);
+                if (IsMe(nickLower)) SelfHostmask(hostmask);
 
                 foreach (var mode in modes.Select(c => c.ToString(CultureInfo.InvariantCulture)))
                     if (!channelUser.Modes.Contains(mode))
@@ -525,7 +767,7 @@ namespace IrcStates
             var nickLower                    = CaseFold(line.Hostmask.NickName);
             if (line.Params.Any()) emit.Text = line.Params[0];
 
-            if (nickLower == NickNameLower || line.Source == null)
+            if (IsMe(nickLower) || line.Source == null)
             {
                 emit.Self = true;
                 Users.Clear();
@@ -556,10 +798,10 @@ namespace IrcStates
             if (kicked != null)
             {
                 emit.UserTarget = kicked;
-                if (kicked.NickNameLower == NickNameLower) emit.Self = true;
+                if (IsMe(kicked.NickName)) emit.Self = true;
 
-                var kickerLower                                   = CaseFold(line.Hostmask.NickName);
-                if (kickerLower == NickNameLower) emit.SelfSource = true;
+                var kickerLower                        = CaseFold(line.Hostmask.NickName);
+                if (IsMe(kickerLower)) emit.SelfSource = true;
 
                 emit.UserSource = Users.ContainsKey(kickerLower)
                     ? Users[kickerLower]
@@ -575,7 +817,7 @@ namespace IrcStates
             if (user != null)
             {
                 emit.User = user;
-                if (user.NickNameLower == NickNameLower) emit.Self = true;
+                if (IsMe(user.NickName)) emit.Self = true;
             }
 
             return emit;
@@ -593,7 +835,7 @@ namespace IrcStates
             var nickLower    = CaseFold(line.Hostmask.NickName);
 
             // handle own join
-            if (nickLower == NickNameLower)
+            if (IsMe(nickLower))
             {
                 emit.Self = true;
                 if (!HasChannel(channelLower))
@@ -661,7 +903,7 @@ namespace IrcStates
                 }
             }
 
-            if (nickLower == NickNameLower)
+            if (IsMe(nickLower))
             {
                 emit.Self     = true;
                 NickName      = nick;
diff --git a/IrcStates/Tests/Cap.cs b/IrcStates/Tests/Cap.cs
index 4f63c55..3ce52f8 100644
--- a/IrcStates/Tests/Cap.cs
+++ b/IrcStates/Tests/Cap.cs
@@ -30,8 +30,9 @@ namespace IrcStates.Tests
             _server.Parse(new Line("CAP * LS * :a b"));
             CollectionAssert.AreEqual(new Dictionary<string, string>(), _server.AvailableCaps);
             _server.Parse(new Line("CAP * LS :c"));
-            CollectionAssert.AreEqual(new Dictionary<string, string> {{"a", ""}, {"b", ""}, {"c", ""}},
-                _server.AvailableCaps);
+            Assert.IsTrue(_server.AvailableCaps.ContainsKey("a"));
+            Assert.IsTrue(_server.AvailableCaps.ContainsKey("b"));
+            Assert.IsTrue(_server.AvailableCaps.ContainsKey("c"));
         }
 
         [TestMethod]
diff --git a/IrcStates/Tests/Mode.cs b/IrcStates/Tests/Mode.cs
index ba05976..799afd6 100644
--- a/IrcStates/Tests/Mode.cs
+++ b/IrcStates/Tests/Mode.cs
@@ -63,8 +63,7 @@ namespace IrcStates.Tests
             _server.Parse(new Line("MODE #chan +b asd!*@*"));
 
             var channel = _server.Channels["#chan"];
-            CollectionAssert.AreEqual(new Dictionary<string, List<string>> {{"b", new List<string> {"asd!*@*"}}},
-                channel.ListModes);
+            CollectionAssert.AreEqual(new List<string> {"asd!*@*"}, channel.ListModes["b"]);
         }
 
         [TestMethod]
@@ -150,8 +149,7 @@ namespace IrcStates.Tests
             var channel = _server.Channels["#chan"];
             CollectionAssert.AreEqual(new Dictionary<string, string> {{"k", "pass"}, {"l", "10"}, {"i", null}},
                 channel.Modes);
-            CollectionAssert.AreEqual(new Dictionary<string, List<string>> {{"b", new List<string> {"*!*@*"}}},
-                channel.ListModes);
+            CollectionAssert.AreEqual(new List<string> {"*!*@*"}, channel.ListModes["b"]);
         }
 
         [TestMethod]