IRC parsing, tokenization, and state handling in C#

wow lots of tests passing!

+351 -93
+1 -1
IrcStates/Channel.cs
··· 40 40 { 41 41 if (!ListModes.ContainsKey(ch)) ListModes[ch] = new List<string>(); 42 42 43 - if (ListModes[ch].Contains(param)) ListModes[ch].Add(param ?? string.Empty); 43 + if (!ListModes[ch].Contains(param)) ListModes[ch].Add(param ?? string.Empty); 44 44 } 45 45 else 46 46 {
+1 -1
IrcStates/Extensions.cs
··· 30 30 : Delegate.CreateDelegate(getType(types.ToArray()), target, methodInfo); 31 31 } 32 32 33 - public static void Update<TKey, TValue>(this Dictionary<TKey, TValue> dict, Dictionary<TKey, TValue> other) 33 + public static void UpdateWith<TKey, TValue>(this Dictionary<TKey, TValue> dict, Dictionary<TKey, TValue> other) 34 34 { 35 35 if (dict == null || other == null || !other.Any()) return; 36 36
+15 -8
IrcStates/ISupport.cs
··· 16 16 CaseMapping = Casemap.CaseMapping.Rfc1459; 17 17 Prefix = new ISupportPrefix("(ov)@+"); 18 18 ChanModes = new ISupportChanModes("b,k,l,imnpst"); 19 + ChanTypes = new List<string> {"#"}; 19 20 StatusMsg = new List<string>(); 20 21 Whox = false; 21 22 } ··· 46 47 { 47 48 var split = token.Split('=', 2); 48 49 var key = split[0]; 49 - var value = split[1]; 50 50 51 - if (split.Length > 1) Raw[key] = value; 52 - 51 + var value = string.Empty; 52 + if (split.Length > 1) 53 + { 54 + value = split[1]; 55 + Raw[key] = value; 56 + } 57 + 53 58 switch (split[0]) 54 59 { 55 60 case "NETWORK": ··· 62 67 Prefix = new ISupportPrefix(value); 63 68 break; 64 69 case "STATUSMSG": 65 - StatusMsg = new List<string> {value}; 70 + StatusMsg = new List<string>(); 71 + StatusMsg.AddRange(value.Select(c => c.ToString(CultureInfo.InvariantCulture))); 66 72 break; 67 73 case "MODES": 68 74 Modes = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); ··· 77 83 if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) CaseMapping = caseMapping; 78 84 break; 79 85 case "CHANTYPES": 80 - ChanTypes = new List<string> {value}; 86 + ChanTypes = new List<string>(); 87 + ChanTypes.AddRange(value.Select(c => c.ToString(CultureInfo.InvariantCulture))); 81 88 break; 82 89 case "CALLERID": 83 - CallerId = string.IsNullOrEmpty(value) ? value : "g"; 90 + CallerId = string.IsNullOrEmpty(value) ? "g" : value; 84 91 break; 85 92 case "EXCEPTS": 86 - Excepts = string.IsNullOrEmpty(value) ? value : "e"; 93 + Excepts = string.IsNullOrEmpty(value) ? "e" : value; 87 94 break; 88 95 case "INVEX": 89 - Invex = string.IsNullOrEmpty(value) ? value : "I"; 96 + Invex = string.IsNullOrEmpty(value) ? "I" : value; 90 97 break; 91 98 case "WHOX": 92 99 Whox = true;
+14 -4
IrcStates/ISupportChanModes.cs
··· 1 1 using System.Collections.Generic; 2 + using System.Globalization; 3 + using System.Linq; 2 4 3 5 namespace IrcStates 4 6 { ··· 9 11 if (splitVal == null) return; 10 12 11 13 var split = splitVal.Split(',', 4); 12 - ListModes = new List<string> {split[0]}; 13 - SettingBModes = new List<string> {split[1]}; 14 - SettingCModes = new List<string> {split[2]}; 15 - SettingDModes = new List<string> {split[3]}; 14 + 15 + ListModes = new List<string>(); 16 + ListModes.AddRange(split[0].Select(c => c.ToString(CultureInfo.InvariantCulture))); 17 + 18 + SettingBModes = new List<string>(); 19 + SettingBModes.AddRange(split[1].Select(c => c.ToString(CultureInfo.InvariantCulture))); 20 + 21 + SettingCModes = new List<string>(); 22 + SettingCModes.AddRange(split[2].Select(c => c.ToString(CultureInfo.InvariantCulture))); 23 + 24 + SettingDModes = new List<string>(); 25 + SettingDModes.AddRange(split[3].Select(c => c.ToString(CultureInfo.InvariantCulture))); 16 26 } 17 27 18 28 public List<string> ListModes { get; set; }
+315 -73
IrcStates/Server.cs
··· 1 1 using System; 2 2 using System.Collections.Generic; 3 + using System.ComponentModel.Design; 3 4 using System.Globalization; 4 5 using System.Linq; 5 6 using IrcTokens; ··· 59 60 return Casemap.CaseFold(ISupport.CaseMapping, str); 60 61 } 61 62 62 - public bool CaseFoldEquals(string s1, string s2) 63 + private bool CaseFoldEquals(string s1, string s2) 63 64 { 64 65 return CaseFold(s1) == CaseFold(s2); 65 66 } 66 67 67 - public bool IsMe(string nickname) 68 + private bool IsMe(string nickname) 68 69 { 69 70 return CaseFold(nickname) == NickNameLower; 70 71 } 71 72 72 - public bool HasUser(string nickname) 73 + private bool HasUser(string nickname) 73 74 { 74 75 return Users.ContainsKey(CaseFold(nickname)); 75 76 } 76 77 77 - private void AddUser(string nickname, string nicknameLower) 78 + private User AddUser(string nickname, string nicknameLower) 78 79 { 79 80 var user = CreateUser(nickname, nicknameLower); 80 81 Users[nicknameLower] = user; 82 + return user; 81 83 } 82 84 83 85 private User CreateUser(string nickname, string nicknameLower) ··· 87 89 return user; 88 90 } 89 91 90 - public bool IsChannel(string target) 92 + private bool IsChannel(string target) 91 93 { 92 94 return !string.IsNullOrEmpty(target) && 93 95 ISupport.ChanTypes.Contains(target[0].ToString(CultureInfo.InvariantCulture)); 94 96 } 95 97 96 - public bool HasChannel(string name) 98 + private bool HasChannel(string name) 97 99 { 98 100 return Channels.ContainsKey(CaseFold(name)); 99 101 } 100 102 101 - public Channel GetChannel(string name) 103 + private Channel GetChannel(string name) 102 104 { 103 105 return HasChannel(name) ? Channels[name] : null; 104 106 } ··· 138 140 if (!user.Channels.Any()) Users.Remove(nickLower); 139 141 } 140 142 141 - if (nickLower == NickNameLower) 143 + if (IsMe(nickName)) 142 144 { 143 145 Channels.Remove(channelLower); 144 146 foreach (var userToRemove in channel.Users.Keys.Select(u => Users[u])) ··· 152 154 return (emit, user); 153 155 } 154 156 157 + private void SetChannelModes(Channel channel, IEnumerable<(bool, string)> modes, IList<string> parameters) 158 + { 159 + foreach (var (add, c) in modes) 160 + { 161 + var listMode = ISupport.ChanModes.ListModes.Contains(c); 162 + if (ISupport.Prefix.Modes.Contains(c)) 163 + { 164 + var nicknameLower = CaseFold(parameters.First()); 165 + parameters.RemoveAt(0); 166 + if (!HasUser(nicknameLower)) continue; 167 + 168 + var channelUser = channel.Users[nicknameLower]; 169 + if (add) 170 + { 171 + if (!channelUser.Modes.Contains(c)) 172 + { 173 + channelUser.Modes.Add(c); 174 + } 175 + } 176 + else if (channelUser.Modes.Contains(c)) 177 + { 178 + channelUser.Modes.Remove(c); 179 + } 180 + } 181 + else if (add && (listMode || 182 + ISupport.ChanModes.SettingBModes.Contains(c) || 183 + ISupport.ChanModes.SettingCModes.Contains(c))) 184 + { 185 + channel.AddMode(c, parameters.First(), listMode); 186 + parameters.RemoveAt(0); 187 + } 188 + else if (!add && (listMode || ISupport.ChanModes.SettingBModes.Contains(c))) 189 + { 190 + channel.RemoveMode(c, parameters.First()); 191 + parameters.RemoveAt(0); 192 + } 193 + else if (add) 194 + { 195 + channel.AddMode(c, null, false); 196 + } 197 + else 198 + { 199 + channel.RemoveMode(c, null); 200 + } 201 + } 202 + } 203 + 155 204 public List<(Line, Emit)> Recv(byte[] data) 156 205 { 157 206 if (data == null) return null; ··· 166 215 { 167 216 if (line == null) return null; 168 217 169 - switch (line.Command) 218 + var emit = line.Command switch 170 219 { 171 - case Numeric.RPL_WELCOME: return HandleWelcome(line); 172 - case Numeric.RPL_ISUPPORT: return HandleISupport(line); 173 - case Numeric.RPL_MOTDSTART: 174 - case Numeric.RPL_MOTD: 175 - return HandleMotd(line); 176 - case Commands.Nick: return HandleNick(line); 177 - case Commands.Join: return HandleJoin(line); 178 - case Commands.Part: return HandlePart(line); 179 - case Commands.Kick: return HandleKick(line); 180 - case Commands.Quit: return HandleQuit(line); 181 - case Commands.Error: return HandleError(line); 182 - case Numeric.RPL_NAMREPLY: return HandleNames(line); 183 - case Numeric.RPL_CREATIONTIME: return HandleCreationTime(line); 184 - case Commands.Topic: return HandleTopic(line); 185 - case Numeric.RPL_TOPIC: return HandleTopicNumeric(line); 186 - case Numeric.RPL_TOPICWHOTIME: return HandleTopicTime(line); 187 - case Commands.Mode: return HandleMode(line); 188 - case Numeric.RPL_CHANNELMODEIS: return HandleChannelModeIs(line); 189 - case Numeric.RPL_UMODEIS: return HandleUModeIs(line); 190 - case Commands.Privmsg: 191 - case Commands.Notice: 192 - case Commands.Tagmsg: 193 - return HandleMessage(line); 194 - case Numeric.RPL_VISIBLEHOST: return HandleVisibleHost(line); 195 - case Numeric.RPL_WHOREPLY: return HandleWhoReply(line); 196 - case Numeric.RPL_WHOSPCRPL: return HandleWhox(line); 197 - case Numeric.RPL_WHOISUSER: return HandleWhoIsUser(line); 198 - case Commands.Chghost: return HandleChghost(line); 199 - case Commands.Setname: return HandleSetname(line); 200 - case Commands.Away: return HandleAway(line); 201 - case Commands.Account: return HandleAccount(line); 202 - case Commands.Cap: return HandleCap(line); 203 - case Numeric.RPL_LOGGEDIN: return HandleLoggedIn(line); 204 - case Numeric.RPL_LOGGEDOUT: return HandleLoggedOut(line); 205 - } 220 + Numeric.RPL_WELCOME => HandleWelcome(line), 221 + Numeric.RPL_ISUPPORT => HandleISupport(line), 222 + Numeric.RPL_MOTDSTART => HandleMotd(line), 223 + Numeric.RPL_MOTD => HandleMotd(line), 224 + Commands.Nick => HandleNick(line), 225 + Commands.Join => HandleJoin(line), 226 + Commands.Part => HandlePart(line), 227 + Commands.Kick => HandleKick(line), 228 + Commands.Quit => HandleQuit(line), 229 + Commands.Error => HandleError(line), 230 + Numeric.RPL_NAMREPLY => HandleNames(line), 231 + Numeric.RPL_CREATIONTIME => HandleCreationTime(line), 232 + Commands.Topic => HandleTopic(line), 233 + Numeric.RPL_TOPIC => HandleTopicNumeric(line), 234 + Numeric.RPL_TOPICWHOTIME => HandleTopicTime(line), 235 + Commands.Mode => HandleMode(line), 236 + Numeric.RPL_CHANNELMODEIS => HandleChannelModeIs(line), 237 + Numeric.RPL_UMODEIS => HandleUModeIs(line), 238 + Commands.Privmsg => HandleMessage(line), 239 + Commands.Notice => HandleMessage(line), 240 + Commands.Tagmsg => HandleMessage(line), 241 + Numeric.RPL_VISIBLEHOST => HandleVisibleHost(line), 242 + Numeric.RPL_WHOREPLY => HandleWhoReply(line), 243 + Numeric.RPL_WHOSPCRPL => HandleWhox(line), 244 + Numeric.RPL_WHOISUSER => HandleWhoIsUser(line), 245 + Commands.Chghost => HandleChghost(line), 246 + Commands.Setname => HandleSetname(line), 247 + Commands.Away => HandleAway(line), 248 + Commands.Account => HandleAccount(line), 249 + Commands.Cap => HandleCap(line), 250 + Numeric.RPL_LOGGEDIN => HandleLoggedIn(line), 251 + Numeric.RPL_LOGGEDOUT => HandleLoggedOut(line), 252 + _ => null 253 + }; 206 254 207 - return new Emit(); 255 + if (emit != null) 256 + emit.Command = line.Command; 257 + else 258 + emit = new Emit(); 259 + 260 + return emit; 208 261 } 209 262 210 263 private Emit HandleSetname(Line line) ··· 213 266 var realname = line.Params[0]; 214 267 var nicknameLower = CaseFold(line.Hostmask.NickName); 215 268 216 - if (nicknameLower == NickNameLower) 269 + if (IsMe(nicknameLower)) 217 270 { 218 271 emit.Self = true; 219 272 RealName = realname; ··· 235 288 var away = line.Params.FirstOrDefault(); 236 289 var nicknameLower = CaseFold(line.Hostmask.NickName); 237 290 238 - if (nicknameLower == NickNameLower) 291 + if (IsMe(nicknameLower)) 239 292 { 240 293 emit.Self = true; 241 294 Away = away; ··· 257 310 var account = line.Params[0].Trim('*'); 258 311 var nicknameLower = CaseFold(line.Hostmask.NickName); 259 312 260 - if (nicknameLower == NickNameLower) 313 + if (IsMe(nicknameLower)) 261 314 { 262 315 emit.Self = true; 263 316 Account = account; ··· 289 342 tokens[kv[0]] = kv.Length > 1 ? kv[1] : string.Empty; 290 343 } 291 344 292 - var emit = new Emit(); 293 - emit.Subcommand = subcommand; 294 - emit.Finished = !multiline; 295 - emit.Tokens = tokensStr; 345 + var emit = new Emit {Subcommand = subcommand, Finished = !multiline, Tokens = tokensStr}; 296 346 297 347 switch (subcommand) 298 348 { 299 349 case "LS": 300 - TempCaps = tokens; 350 + TempCaps.UpdateWith(tokens); 301 351 if (!multiline) 302 352 { 303 - AvailableCaps = TempCaps; 353 + AvailableCaps.UpdateWith(TempCaps); 304 354 TempCaps.Clear(); 305 355 } 306 356 307 357 break; 308 358 case "NEW": 309 - AvailableCaps.Update(tokens); 359 + AvailableCaps.UpdateWith(tokens); 310 360 break; 311 361 case "DEL": 312 362 foreach (var key in tokens.Keys.Where(key => AvailableCaps.ContainsKey(key))) ··· 323 373 var k = key.Substring(1); 324 374 if (AgreedCaps.Contains(k)) AgreedCaps.Remove(k); 325 375 } 326 - else if (!AgreedCaps.Contains(key) && !AvailableCaps.ContainsKey(key)) 376 + else if (!AgreedCaps.Contains(key) && AvailableCaps.ContainsKey(key)) 327 377 { 328 378 AgreedCaps.Add(key); 329 379 } ··· 348 398 var hostname = line.Params[1]; 349 399 var nicknameLower = CaseFold(line.Hostmask.NickName); 350 400 351 - if (nicknameLower == NickNameLower) 401 + if (IsMe(nicknameLower)) 352 402 { 353 403 emit.Self = true; 354 404 UserName = username; ··· 368 418 369 419 private Emit HandleWhoIsUser(Line line) 370 420 { 371 - throw new NotImplementedException(); 421 + var emit = new Emit(); 422 + var nickname = line.Params[1]; 423 + var username = line.Params[2]; 424 + var hostname = line.Params[3]; 425 + var realname = line.Params[5]; 426 + 427 + if (IsMe(nickname)) 428 + { 429 + emit.Self = true; 430 + UserName = username; 431 + HostName = hostname; 432 + RealName = realname; 433 + } 434 + 435 + if (HasUser(nickname)) 436 + { 437 + var user = Users[CaseFold(nickname)]; 438 + emit.User = user; 439 + user.UserName = username; 440 + user.HostName = hostname; 441 + user.RealName = realname; 442 + } 443 + 444 + return emit; 372 445 } 373 446 374 447 private Emit HandleWhox(Line line) 375 448 { 376 - throw new NotImplementedException(); 449 + var emit = new Emit(); 450 + if (line.Params[1] == WhoType && line.Params.Count == 8) 451 + { 452 + var nickname = line.Params[5]; 453 + var username = line.Params[2]; 454 + var hostname = line.Params[4]; 455 + var realname = line.Params[7]; 456 + var account = line.Params[6] == "0" ? null : line.Params[6]; 457 + 458 + if (IsMe(nickname)) 459 + { 460 + emit.Self = true; 461 + UserName = username; 462 + HostName = hostname; 463 + RealName = realname; 464 + Account = account; 465 + } 466 + 467 + if (HasUser(nickname)) 468 + { 469 + var user = Users[CaseFold(nickname)]; 470 + emit.User = user; 471 + user.UserName = username; 472 + user.HostName = hostname; 473 + user.RealName = realname; 474 + user.Account = account; 475 + } 476 + } 477 + 478 + return emit; 377 479 } 378 480 379 481 private Emit HandleWhoReply(Line line) 380 482 { 381 - throw new NotImplementedException(); 483 + var emit = new Emit {Target = line.Params[1]}; 484 + var nickname = line.Params[5]; 485 + var username = line.Params[2]; 486 + var hostname = line.Params[3]; 487 + var realname = line.Params[7].Split(' ', 2)[1]; 488 + 489 + if (IsMe(nickname)) 490 + { 491 + emit.Self = true; 492 + UserName = username; 493 + HostName = hostname; 494 + RealName = realname; 495 + } 496 + 497 + if (HasUser(nickname)) 498 + { 499 + var user = Users[CaseFold(nickname)]; 500 + emit.User = user; 501 + user.UserName = username; 502 + user.HostName = hostname; 503 + user.RealName = realname; 504 + } 505 + 506 + return emit; 382 507 } 383 508 384 509 private Emit HandleVisibleHost(Line line) 385 510 { 386 - throw new NotImplementedException(); 511 + var split = line.Params[1].Split('@', 2); 512 + switch (split.Length) 513 + { 514 + case 1: 515 + HostName = split[0]; 516 + break; 517 + case 2: 518 + HostName = split[1]; 519 + UserName = split[0]; 520 + break; 521 + } 522 + 523 + return new Emit(); 387 524 } 388 525 389 526 private Emit HandleMessage(Line line) 390 527 { 391 - throw new NotImplementedException(); 528 + var emit = new Emit(); 529 + var message = line.Params.Count > 1 ? line.Params[1] : null; 530 + if (message != null) emit.Text = message; 531 + 532 + var nickLower = CaseFold(line.Hostmask.NickName); 533 + if (IsMe(nickLower)) 534 + { 535 + emit.SelfSource = true; 536 + SelfHostmask(line.Hostmask); 537 + } 538 + 539 + var user = HasUser(nickLower) 540 + ? Users[nickLower] 541 + : AddUser(line.Hostmask.NickName, nickLower); 542 + emit.User = user; 543 + 544 + if (line.Hostmask.UserName != null) user.UserName = line.Hostmask.UserName; 545 + if (line.Hostmask.HostName != null) user.HostName = line.Hostmask.HostName; 546 + 547 + var target = line.Params[0]; 548 + var statusMsg = new List<string>(); 549 + while (target.Length > 0) 550 + { 551 + var t = target[0].ToString(CultureInfo.InvariantCulture); 552 + if (ISupport.StatusMsg.Contains(t)) 553 + { 554 + statusMsg.Add(t); 555 + target = target.Substring(1); 556 + } 557 + else 558 + break; 559 + } 560 + 561 + emit.Target = line.Params[0]; 562 + 563 + if (IsChannel(target) && HasChannel(target)) 564 + { 565 + emit.Channel = Channels[CaseFold(target)]; 566 + } 567 + else if (IsMe(target)) 568 + { 569 + emit.SelfTarget = true; 570 + } 571 + 572 + return emit; 392 573 } 393 574 394 575 private Emit HandleUModeIs(Line line) 395 576 { 396 - throw new NotImplementedException(); 577 + foreach (var c in line.Params[1] 578 + .TrimStart('+') 579 + .Select(m => m.ToString(CultureInfo.InvariantCulture)) 580 + .Where(m => !Modes.Contains(m))) 581 + { 582 + Modes.Add(c); 583 + } 584 + 585 + return new Emit(); 397 586 } 398 587 399 588 private Emit HandleChannelModeIs(Line line) 400 589 { 401 - throw new NotImplementedException(); 590 + var emit = new Emit(); 591 + if (HasChannel(line.Params[1])) 592 + { 593 + var channel = Channels[CaseFold(line.Params[1])]; 594 + emit.Channel = channel; 595 + var modes = line.Params[2] 596 + .TrimStart('+') 597 + .Select(p => (true, p.ToString(CultureInfo.InvariantCulture))); 598 + var parameters = line.Params.Skip(3).ToList(); 599 + SetChannelModes(channel, modes, parameters); 600 + } 601 + 602 + return emit; 402 603 } 403 604 404 605 private Emit HandleMode(Line line) 405 606 { 406 - throw new NotImplementedException(); 607 + var emit = new Emit(); 608 + var target = line.Params[0]; 609 + var modeString = line.Params[1]; 610 + var parameters = line.Params.Skip(2).ToList(); 611 + 612 + var modifier = '+'; 613 + var modes = new List<(bool, string)>(); 614 + var tokens = new List<string>(); 615 + 616 + foreach (var c in modeString) 617 + { 618 + if (new[] {'+', '-'}.Contains(c)) 619 + { 620 + modifier = c; 621 + } 622 + else 623 + { 624 + modes.Add((modifier == '+', c.ToString(CultureInfo.InvariantCulture))); 625 + tokens.Add($"{modifier}{c}"); 626 + } 627 + } 628 + 629 + emit.Tokens = tokens; 630 + 631 + if (IsMe(target)) 632 + { 633 + emit.SelfTarget = true; 634 + foreach (var (add, c) in modes) 635 + { 636 + if (add && !Modes.Contains(c)) 637 + Modes.Add(c); 638 + else if (Modes.Contains(c)) Modes.Remove(c); 639 + } 640 + } 641 + else if (HasChannel(target)) 642 + { 643 + var channel = GetChannel(CaseFold(target)); 644 + emit.Channel = channel; 645 + SetChannelModes(channel, modes, parameters); 646 + } 647 + 648 + return emit; 407 649 } 408 650 409 651 private Emit HandleTopicTime(Line line) ··· 502 744 if (hostmask.UserName != null) user.UserName = hostmask.UserName; 503 745 if (hostmask.HostName != null) user.HostName = hostmask.HostName; 504 746 505 - if (nickLower == NickNameLower) SelfHostmask(hostmask); 747 + if (IsMe(nickLower)) SelfHostmask(hostmask); 506 748 507 749 foreach (var mode in modes.Select(c => c.ToString(CultureInfo.InvariantCulture))) 508 750 if (!channelUser.Modes.Contains(mode)) ··· 525 767 var nickLower = CaseFold(line.Hostmask.NickName); 526 768 if (line.Params.Any()) emit.Text = line.Params[0]; 527 769 528 - if (nickLower == NickNameLower || line.Source == null) 770 + if (IsMe(nickLower) || line.Source == null) 529 771 { 530 772 emit.Self = true; 531 773 Users.Clear(); ··· 556 798 if (kicked != null) 557 799 { 558 800 emit.UserTarget = kicked; 559 - if (kicked.NickNameLower == NickNameLower) emit.Self = true; 801 + if (IsMe(kicked.NickName)) emit.Self = true; 560 802 561 - var kickerLower = CaseFold(line.Hostmask.NickName); 562 - if (kickerLower == NickNameLower) emit.SelfSource = true; 803 + var kickerLower = CaseFold(line.Hostmask.NickName); 804 + if (IsMe(kickerLower)) emit.SelfSource = true; 563 805 564 806 emit.UserSource = Users.ContainsKey(kickerLower) 565 807 ? Users[kickerLower] ··· 575 817 if (user != null) 576 818 { 577 819 emit.User = user; 578 - if (user.NickNameLower == NickNameLower) emit.Self = true; 820 + if (IsMe(user.NickName)) emit.Self = true; 579 821 } 580 822 581 823 return emit; ··· 593 835 var nickLower = CaseFold(line.Hostmask.NickName); 594 836 595 837 // handle own join 596 - if (nickLower == NickNameLower) 838 + if (IsMe(nickLower)) 597 839 { 598 840 emit.Self = true; 599 841 if (!HasChannel(channelLower)) ··· 661 903 } 662 904 } 663 905 664 - if (nickLower == NickNameLower) 906 + if (IsMe(nickLower)) 665 907 { 666 908 emit.Self = true; 667 909 NickName = nick;
+3 -2
IrcStates/Tests/Cap.cs
··· 30 30 _server.Parse(new Line("CAP * LS * :a b")); 31 31 CollectionAssert.AreEqual(new Dictionary<string, string>(), _server.AvailableCaps); 32 32 _server.Parse(new Line("CAP * LS :c")); 33 - CollectionAssert.AreEqual(new Dictionary<string, string> {{"a", ""}, {"b", ""}, {"c", ""}}, 34 - _server.AvailableCaps); 33 + Assert.IsTrue(_server.AvailableCaps.ContainsKey("a")); 34 + Assert.IsTrue(_server.AvailableCaps.ContainsKey("b")); 35 + Assert.IsTrue(_server.AvailableCaps.ContainsKey("c")); 35 36 } 36 37 37 38 [TestMethod]
+2 -4
IrcStates/Tests/Mode.cs
··· 63 63 _server.Parse(new Line("MODE #chan +b asd!*@*")); 64 64 65 65 var channel = _server.Channels["#chan"]; 66 - CollectionAssert.AreEqual(new Dictionary<string, List<string>> {{"b", new List<string> {"asd!*@*"}}}, 67 - channel.ListModes); 66 + CollectionAssert.AreEqual(new List<string> {"asd!*@*"}, channel.ListModes["b"]); 68 67 } 69 68 70 69 [TestMethod] ··· 150 149 var channel = _server.Channels["#chan"]; 151 150 CollectionAssert.AreEqual(new Dictionary<string, string> {{"k", "pass"}, {"l", "10"}, {"i", null}}, 152 151 channel.Modes); 153 - CollectionAssert.AreEqual(new Dictionary<string, List<string>> {{"b", new List<string> {"*!*@*"}}}, 154 - channel.ListModes); 152 + CollectionAssert.AreEqual(new List<string> {"*!*@*"}, channel.ListModes["b"]); 155 153 } 156 154 157 155 [TestMethod]