implement some stuff
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Ben Harris 2020-05-13 23:34:33 -04:00
parent b93174070a
commit 023e29ab75
Signed by: ben
GPG Key ID: 4E0AF802FFF7960C
9 changed files with 267 additions and 17 deletions

View File

@ -6,6 +6,13 @@ namespace IrcStates
{ {
public class Channel public class Channel
{ {
public Channel()
{
Users = new Dictionary<string, ChannelUser>();
ListModes = new Dictionary<string, List<string>>();
Modes = new Dictionary<string, string>();
}
public string Name { get; set; } public string Name { get; set; }
public string NameLower { get; set; } public string NameLower { get; set; }
public Dictionary<string, ChannelUser> Users { get; set; } public Dictionary<string, ChannelUser> Users { get; set; }

View File

@ -5,5 +5,10 @@ namespace IrcStates
public class ChannelUser public class ChannelUser
{ {
public List<string> Modes { get; set; } public List<string> Modes { get; set; }
public ChannelUser()
{
Modes = new List<string>();
}
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
namespace IrcStates namespace IrcStates
{ {
@ -35,6 +36,9 @@ namespace IrcStates
{ {
if (tokens == null) return; if (tokens == null) return;
// remove first and last
tokens = tokens.Skip(1).SkipLast(1);
foreach (var token in tokens) foreach (var token in tokens)
{ {
var split = token.Split('=', 2); var split = token.Split('=', 2);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
namespace IrcStates namespace IrcStates
{ {
@ -17,11 +18,20 @@ namespace IrcStates
public List<string> Modes { get; set; } public List<string> Modes { get; set; }
public List<string> Prefixes { get; set; } public List<string> Prefixes { get; set; }
public string FromMode(char mode)
{
return FromMode(mode.ToString(CultureInfo.InvariantCulture));
}
public string FromMode(string mode) public string FromMode(string mode)
{ {
return Modes.Contains(mode) ? Modes[Modes.IndexOf(mode)] : null; return Modes.Contains(mode) ? Modes[Modes.IndexOf(mode)] : null;
} }
public string FromPrefix(char prefix)
{
return FromPrefix(prefix.ToString(CultureInfo.InvariantCulture));
}
public string FromPrefix(string prefix) public string FromPrefix(string prefix)
{ {
return Prefixes.Contains(prefix) ? Prefixes[Prefixes.IndexOf(prefix)] : null; return Prefixes.Contains(prefix) ? Prefixes[Prefixes.IndexOf(prefix)] : null;

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
using IrcTokens; using IrcTokens;
namespace IrcStates namespace IrcStates
@ -103,6 +104,55 @@ namespace IrcStates
return HasChannel(name) ? Channels[name] : null; return HasChannel(name) ? Channels[name] : null;
} }
private ChannelUser UserJoin(Channel channel, User user)
{
var channelUser = new ChannelUser();
user.Channels.Add(CaseFold(channel.Name));
channel.Users[user.NickNameLower] = channelUser;
return channelUser;
}
private void SelfHostmask(Hostmask hostmask)
{
NickName = hostmask.NickName;
if (hostmask.UserName != null) UserName = hostmask.UserName;
if (hostmask.HostName != null) HostName = hostmask.HostName;
}
private (Emit, User) UserPart(Line line, string nickName, string channelName, int reasonIndex)
{
var emit = new Emit();
var channelLower = CaseFold(channelName);
if (line.Params.Count >= reasonIndex + 1) emit.Text = line.Params[reasonIndex];
User user = null;
if (HasChannel(channelName))
{
var channel = Channels[channelLower];
emit.Channel = channel;
var nickLower = CaseFold(nickName);
if (HasUser(nickLower))
{
user = Users[nickLower];
user.Channels.Remove(channelLower);
channel.Users.Remove(nickLower);
if (!user.Channels.Any()) Users.Remove(nickLower);
}
if (nickLower == NickNameLower)
{
Channels.Remove(channelLower);
foreach (var userToRemove in channel.Users.Keys.Select(u => Users[u]))
{
userToRemove.Channels.Remove(channelLower);
if (!userToRemove.Channels.Any()) Users.Remove(userToRemove.NickNameLower);
}
}
}
return (emit, user);
}
public List<(Line, Emit)> Recv(byte[] data) public List<(Line, Emit)> Recv(byte[] data)
{ {
if (data == null) return null; if (data == null) return null;
@ -250,17 +300,87 @@ namespace IrcStates
private Emit HandleNames(Line line) private Emit HandleNames(Line line)
{ {
throw new NotImplementedException(); var emit = new Emit();
var channelLower = CaseFold(line.Params[2]);
if (!Channels.ContainsKey(channelLower)) return emit;
var channel = Channels[channelLower];
emit.Channel = channel;
var nicknames = line.Params[3].Split(' ', StringSplitOptions.RemoveEmptyEntries);
var users = new List<User>();
emit.Users = users;
foreach (var nick in nicknames)
{
var modes = "";
foreach (var c in nick)
{
var mode = ISupport.Prefix.FromPrefix(c);
if (mode != null)
modes += mode;
else
break;
}
var hostmask = new Hostmask(nick.Substring(modes.Length));
var nickLower = CaseFold(hostmask.NickName);
if (!Users.ContainsKey(nickLower))
{
AddUser(hostmask.NickName, nickLower);
}
var user = Users[nickLower];
users.Add(user);
var channelUser = UserJoin(channel, user);
if (hostmask.UserName != null) user.UserName = hostmask.UserName;
if (hostmask.HostName != null) user.HostName = hostmask.HostName;
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;
} }
private Emit HandleError(Line line) private Emit HandleError(Line line)
{ {
throw new NotImplementedException(); Users.Clear();
Channels.Clear();
return new Emit();
} }
private Emit HandleQuit(Line line) private Emit HandleQuit(Line line)
{ {
throw new NotImplementedException(); var emit = new Emit();
var nickLower = CaseFold(line.Hostmask.NickName);
if (line.Params.Any()) emit.Text = line.Params[0];
if (nickLower == NickNameLower || line.Source == null)
{
emit.Self = true;
Users.Clear();
Channels.Clear();
}
else if (Users.ContainsKey(nickLower))
{
var user = Users[nickLower];
Users.Remove(nickLower);
emit.User = user;
foreach (var channel in user.Channels.Select(c => Channels[c]))
{
channel.Users.Remove(user.NickNameLower);
}
}
return emit;
} }
private Emit HandleLoggedOut(Line line) private Emit HandleLoggedOut(Line line)
@ -270,29 +390,100 @@ namespace IrcStates
private Emit HandleKick(Line line) private Emit HandleKick(Line line)
{ {
throw new NotImplementedException(); var (emit, kicked) = UserPart(line, line.Params[1], line.Params[0], 2);
if (kicked != null)
{
emit.UserTarget = kicked;
if (kicked.NickNameLower == NickNameLower) emit.Self = true;
var kickerLower = CaseFold(line.Hostmask.NickName);
if (kickerLower == NickNameLower) emit.SelfSource = true;
emit.UserSource = Users.ContainsKey(kickerLower)
? Users[kickerLower]
: CreateUser(line.Hostmask.NickName, kickerLower);
}
return emit;
} }
private Emit HandlePart(Line line) private Emit HandlePart(Line line)
{ {
throw new NotImplementedException(); var (emit, user) = UserPart(line, line.Hostmask.NickName, line.Params[0], 1);
if (user != null)
{
emit.User = user;
if (user.NickNameLower == NickNameLower) emit.Self = true;
}
return emit;
} }
private Emit HandleJoin(Line line) private Emit HandleJoin(Line line)
{ {
throw new NotImplementedException(); var extended = line.Params.Count == 3;
var account = extended ? line.Params[1].Trim('*') : null;
var realname = extended ? line.Params[2] : null;
var emit = new Emit();
var channelLower = CaseFold(line.Params[0]);
var nickLower = CaseFold(line.Hostmask.NickName);
// handle own join
if (nickLower == NickNameLower)
{
emit.Self = true;
if (!HasChannel(channelLower))
{
var channel = new Channel();
channel.SetName(line.Params[0], channelLower);
Channels[channelLower] = channel;
}
SelfHostmask(line.Hostmask);
if (extended)
{
Account = account;
RealName = realname;
}
}
if (HasChannel(channelLower))
{
var channel = Channels[channelLower];
emit.Channel = channel;
if (!HasUser(nickLower)) AddUser(line.Hostmask.NickName, nickLower);
var user = Users[nickLower];
emit.User = user;
if (line.Hostmask.UserName != null) user.UserName = line.Hostmask.UserName;
if (line.Hostmask.HostName != null) user.HostName = line.Hostmask.HostName;
if (extended)
{
user.Account = account;
user.RealName = realname;
}
UserJoin(channel, user);
}
return emit;
} }
private Emit HandleNick(Line line) private Emit HandleNick(Line line)
{ {
var nick = line.Params[0]; var nick = line.Params[0];
var nickLower = CaseFold(line.Hostmask.NickName); var nickLower = CaseFold(line.Hostmask.NickName);
var emit = new Emit(); var emit = new Emit();
if (Users.ContainsKey(nickLower)) if (Users.ContainsKey(nickLower))
{ {
var user = Users[nick]; var user = Users[nickLower];
Users.Remove(nickLower);
emit.User = user; emit.User = user;
var oldNickLower = user.NickNameLower; var oldNickLower = user.NickNameLower;
@ -301,7 +492,7 @@ namespace IrcStates
Users[newNickLower] = user; Users[newNickLower] = user;
foreach (var channelLower in user.Channels) foreach (var channelLower in user.Channels)
{ {
var channel = Channels[channelLower]; var channel = Channels[channelLower];
var channelUser = channel.Users[oldNickLower]; var channelUser = channel.Users[oldNickLower];
channel.Users.Remove(oldNickLower); channel.Users.Remove(oldNickLower);
channel.Users[newNickLower] = channelUser; channel.Users[newNickLower] = channelUser;
@ -310,8 +501,8 @@ namespace IrcStates
if (nickLower == NickNameLower) if (nickLower == NickNameLower)
{ {
emit.Self = true; emit.Self = true;
NickName = nick; NickName = nick;
NickNameLower = CaseFold(nick); NickNameLower = CaseFold(nick);
} }
@ -320,10 +511,7 @@ namespace IrcStates
private Emit HandleMotd(Line line) private Emit HandleMotd(Line line)
{ {
if (line.Command == Numeric.RPL_MOTDSTART) if (line.Command == Numeric.RPL_MOTDSTART) Motd.Clear();
{
Motd.Clear();
}
var emit = new Emit {Text = line.Params[1]}; var emit = new Emit {Text = line.Params[1]};
Motd.Add(line.Params[1]); Motd.Add(line.Params[1]);
@ -333,7 +521,7 @@ namespace IrcStates
private Emit HandleISupport(Line line) private Emit HandleISupport(Line line)
{ {
ISupport = new ISupport(); ISupport = new ISupport();
ISupport.Parse(line.Params.Skip(1).SkipLast(1)); ISupport.Parse(line.Params);
return new Emit(); return new Emit();
} }

View File

@ -51,7 +51,7 @@ namespace IrcStates.Tests
Assert.IsTrue(server.Users.ContainsKey("newnickname")); Assert.IsTrue(server.Users.ContainsKey("newnickname"));
Assert.AreEqual("NewNickname", user.NickName); Assert.AreEqual("NewNickname", user.NickName);
Assert.AreEqual("newnickname", user.NickNameLower); Assert.AreEqual("newnickname", user.NickNameLower);
Assert.AreEqual("NewNickName", server.NickName); Assert.AreEqual("NewNickname", server.NickName);
Assert.AreEqual("newnickname", server.NickNameLower); Assert.AreEqual("newnickname", server.NickNameLower);
} }
} }

View File

@ -100,6 +100,30 @@ namespace IrcStates.Tests
channel.Users); channel.Users);
} }
[TestMethod]
public void QuitSelf()
{
_server.Parse(new Line("QUIT :i'm outta here"));
Assert.IsFalse(_server.Users.Any());
Assert.IsFalse(_server.Channels.Any());
}
[TestMethod]
public void QuitSelfWithSource()
{
_server.Parse(new Line(":nickname QUIT :i'm outta here"));
Assert.IsFalse(_server.Users.Any());
Assert.IsFalse(_server.Channels.Any());
}
[TestMethod]
public void QuitOther()
{
_server.Parse(new Line(":other JOIN #chan"));
_server.Parse(new Line(":other QUIT :see ya"));
Assert.IsFalse(_server.Users.ContainsKey("other"));
}
[TestMethod] [TestMethod]
public void TopicText() public void TopicText()
{ {

View File

@ -15,6 +15,13 @@ namespace IrcStates.Tests
_server.Parse(new Line("001 nickname")); _server.Parse(new Line("001 nickname"));
} }
[TestMethod]
public void Welcome()
{
Assert.AreEqual("test", _server.Name);
Assert.AreEqual("nickname", _server.NickName);
}
[TestMethod] [TestMethod]
public void NicknameChange() public void NicknameChange()
{ {

View File

@ -4,6 +4,11 @@ namespace IrcStates
{ {
public class User public class User
{ {
public User()
{
Channels = new HashSet<string>();
}
public string NickName { get; set; } public string NickName { get; set; }
public string NickNameLower { get; set; } public string NickNameLower { get; set; }