Browse Source

Added message parsing. Added Message events. Added basic interface. Separated core services into separate modules.

tags/3.0.0
Teknikode 4 years ago
parent
commit
ca82f763d0

+ 0
- 26
Bot.cs View File

@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot
{
class Bot
{
public event Action<BotError> ErrorEvent;

public Config Config;

public IRCService IRCService;

internal Bot()
{
IRCService = new IRCService(this);

Message msg = new Message();
Nick nick = new Nick();
msg.Sender = nick;
}
}
}

BIN
Bot.ico View File


+ 16
- 1
Combot.sln View File

@@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Combot", "Combot.csproj", "{23E4C371-16E4-4FAC-8B11-44288399BB55}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interface", "Interface\Interface.csproj", "{AE4AFD7A-CAA4-46A0-9E00-2B3D53F8D0AB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Combot", "Combot\Combot.csproj", "{23E4C371-16E4-4FAC-8B11-44288399BB55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRCServices", "IRCServices\IRCServices.csproj", "{65FCBF1C-8C9E-4688-BECC-185D9030899F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,12 +15,23 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AE4AFD7A-CAA4-46A0-9E00-2B3D53F8D0AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE4AFD7A-CAA4-46A0-9E00-2B3D53F8D0AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE4AFD7A-CAA4-46A0-9E00-2B3D53F8D0AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE4AFD7A-CAA4-46A0-9E00-2B3D53F8D0AB}.Release|Any CPU.Build.0 = Release|Any CPU
{23E4C371-16E4-4FAC-8B11-44288399BB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23E4C371-16E4-4FAC-8B11-44288399BB55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23E4C371-16E4-4FAC-8B11-44288399BB55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23E4C371-16E4-4FAC-8B11-44288399BB55}.Release|Any CPU.Build.0 = Release|Any CPU
{65FCBF1C-8C9E-4688-BECC-185D9030899F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65FCBF1C-8C9E-4688-BECC-185D9030899F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65FCBF1C-8C9E-4688-BECC-185D9030899F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65FCBF1C-8C9E-4688-BECC-185D9030899F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal

+ 64
- 0
Combot/Bot.cs View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using Combot.IRCServices;

namespace Combot
{
public class Bot
{
public event Action<BotError> ErrorEvent;
public Config Config;
public IRC IRC;
public bool Connected = false;

public Bot()
{
Config = new Config();
IRC = new IRC();

IRC.DisconnectEvent += HandleDisconnectEvent;
}

public bool Connect()
{
int i = 0;
do
{
if (Config.Server.Hosts.Count > i)
{
Connected = IRC.Connect(Config.Server.Hosts[i].Address, Config.Server.Hosts[i].Port, 5000);
i++;
}
else
{
break;
}
}
while (!Connected);

return Connected;
}

public bool Disconnect()
{
IRC.Disconnect();
Connected = false;

return Connected;
}

public void Login()
{
IRC.Login(Config.Server.Name, new Nick() { Nickname = Config.Nick, Host = Dns.GetHostName(), Realname = Config.Realname });
}

private void HandleDisconnectEvent()
{
Connected = false;
}
}
}

Combot.csproj → Combot/Combot.csproj View File

@@ -39,24 +39,22 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Access.cs" />
<Compile Include="Bot.cs" />
<Compile Include="Config.cs" />
<Compile Include="IRC\Channel.cs" />
<Compile Include="IRC\IRCMethods.cs" />
<Compile Include="IRC\Nick.cs" />
<Compile Include="IRC\IRCService.cs" />
<Compile Include="IRC\IRCTypes.cs" />
<Compile Include="IRC\IRCMessages.cs" />
<Compile Include="Types.cs" />
<Compile Include="Methods.cs" />
<Compile Include="Module.cs" />
<Compile Include="Modules\Module.cs" />
<Compile Include="Properties.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TCP\TCPInterface.cs" />
<Compile Include="TCP\TCPTypes.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Modules\" />
<ProjectReference Include="..\IRCServices\IRCServices.csproj">
<Project>{65fcbf1c-8c9e-4688-becc-185d9030899f}</Project>
<Name>IRCServices</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

Config.cs → Combot/Config.cs View File

@@ -7,44 +7,44 @@ using System.Net;

namespace Combot
{
class Config
public class Config
{
private string _name;
public string Name
private Server _server;
public Server Server
{
get
{
return _name;
return _server;
}

set
{
if (value != _name)
if (value != _server)
{
_name = value;
_server = value;
}
}
}

private IPEndPoint _host;
public IPEndPoint Host
private string _realname;
public string Realname
{
get
{
return _host;
return _realname;
}

set
{
if (value != _host)
if (value != _realname)
{
_host = value;
_realname = value;
}
}
}

private Nick _nick;
public Nick Nick
private string _nick;
public string Nick
{
get
{

Methods.cs → Combot/Methods.cs View File


Module.cs → Combot/Modules/Module.cs View File

@@ -4,9 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot
namespace Combot.Modules
{
class Module
public class Module
{
internal virtual void Initialize() { }
}

Properties.cs → Combot/Properties.cs View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;

namespace Combot
{
class Properties
public class Properties
{
}
}

Properties/AssemblyInfo.cs → Combot/Properties/AssemblyInfo.cs View File

@@ -6,9 +6,9 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Combot")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyDescription("The friendly IRC Bot")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("Teknik")]
[assembly: AssemblyProduct("Combot")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]

+ 42
- 0
Combot/Types.cs View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Net;

namespace Combot
{
public enum ErrorType
{
Bot = 0,
TCP = 1,
IRC = 2,
Framework = 3
}

public enum MessageType
{
Service = 0,
Channel = 1,
Query = 2,
Notice = 3,
CTCP = 4
}

public class BotError
{
public ErrorType Type { get; set; }
public string Message { get; set; }
}

public class Server
{
public string Name { get; set; }
public List<IPEndPoint> Hosts { get; set; }
public List<string> Channels { get; set; }
public bool AutoConnect { get; set; }
}
}

+ 0
- 93
IRC/Channel.cs View File

@@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot
{
public class Channel
{
public string Name { get; set; }
public string Topic { get; set; }
public string Key { get; set; }
public bool AutoJoin { get; set; }
public DateTime Registration { get; set; }
public List<ChannelMode> Modes { get; set; }
public List<Nick> Nicks { get; set; }

public Channel()
{
Name = string.Empty;
Topic = string.Empty;
Key = string.Empty;
AutoJoin = false;
Registration = DateTime.Now;
Modes = new List<ChannelMode>();
Nicks = new List<Nick>();
}

public Channel(string name, string topic, string key, bool autojoin, DateTime registration, List<ChannelMode> modes, List<Nick> nicks)
{
Name = name;
Topic = topic;
Key = key;
AutoJoin = autojoin;
Registration = registration;
Modes = modes;
Nicks = nicks;
}

public void AddNick(Nick nick)
{
Nicks.Add(nick);
}

public void AddNicks(List<Nick> nicks)
{
Nicks.AddRange(nicks);
}

public void RemoveNick(Nick nick)
{
if (Nicks.Contains(nick))
{
Nicks.Remove(nick);
}
}

public void RemoveNicks(List<Nick> nicks)
{
foreach (Nick nick in nicks)
{
RemoveNick(nick);
}
}

public void AddMode(ChannelMode mode)
{
Modes.Add(mode);
}

public void AddModes(List<ChannelMode> modes)
{
Modes.AddRange(modes);
}

public void RemoveMode(ChannelMode mode)
{
if (Modes.Contains(mode))
{
Modes.Remove(mode);
}
}

public void RemoveModes(List<ChannelMode> modes)
{
foreach (ChannelMode mode in modes)
{
RemoveMode(mode);
}
}
}
}

+ 0
- 24
IRC/IRCMessages.cs View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot
{
internal partial class IRCService
{
public event Action<Message> MessageReceivedEvent;

internal void ParseTCPMessage(string message)
{
Message parsedMessage = new Message();

Action<Message> localEvent = MessageReceivedEvent;
if (localEvent != null)
{
localEvent(parsedMessage);
}
}
}
}

+ 0
- 1155
IRC/IRCMethods.cs
File diff suppressed because it is too large
View File


+ 0
- 85
IRC/IRCService.cs View File

@@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;

namespace Combot
{
internal partial class IRCService
{
protected Bot _Bot;
protected TCPInterface _TCP;
protected Thread TCPReader;
protected event Action<string> TCPMessageEvent;

internal IRCService(Bot bot)
{
_Bot = bot;
_TCP = new TCPInterface();
TCPMessageEvent += ParseTCPMessage;
}

internal bool Connect(IPAddress IP, int port, int readTimeout, int allowedFailedCount = 0)
{
bool result = false;
if (!_TCP.Connected)
{
result = _TCP.Connect(IP, port, readTimeout, allowedFailedCount);
if (result)
{
TCPReader = new Thread(ReadTCPMessages);
TCPReader.IsBackground = true;
TCPReader.Start();
}
}

return result;
}

internal bool Disconnect()
{
bool result = false;

if (_TCP.Connected)
{
_TCP.Disconnect();
}

return result;
}

protected void ReadTCPMessages()
{
while (_TCP.Connected)
{
string response = ReadTCPMessage();
if (TCPMessageEvent != null && response != null && response != string.Empty)
{
TCPMessageEvent(response);
}

Thread.Sleep(10);
}
}

protected string ReadTCPMessage()
{
if (_TCP.Connected)
{
return _TCP.Read();
}
return null;
}

protected void SendTCPMessage(string message)
{
if (_TCP.Connected)
{
_TCP.Write(message);
}
}
}
}

+ 0
- 379
IRC/IRCTypes.cs View File

@@ -1,379 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Net;

namespace Combot
{
public enum ErrorType
{
Bot = 0,
TCP = 1,
IRC = 2,
Framework = 3
}

public enum MessageType
{
Service = 0,
Channel = 1,
Query = 2,
Notice = 3,
CTCP = 4
}

public class ChannelModeInfo
{
public ChannelMode Mode { get; set; }
public bool Set { get; set; }
public string Parameter { get; set; }
}

public class UserModeInfo
{
public UserMode Mode { get; set; }
public bool Set { get; set; }
public string Parameter { get; set; }
}

public enum ChannelMode
{
[Description("Admin")]
a,
[Description("Admin Only")]
A,
[Description("Ban")]
b,
[Description("Colourless")]
c,
[Description("No CTCP")]
C,
[Description("Ban Exempt")]
e,
[Description("Flood Protection")]
f,
[Description("HalfOp")]
h,
[Description("Invite Only")]
i,
[Description("Invite Exempt")]
I,
[Description("Join Throttling")]
j,
[Description("Key")]
k,
[Description("No KNOCK")]
K,
[Description("Limit")]
l,
[Description("Link")]
L,
[Description("Moderated")]
m,
[Description("Registered Nicks May Talk")]
M,
[Description("No External Messages")]
n,
[Description("No Nickname Changes")]
N,
[Description("Operator")]
o,
[Description("Oper Only")]
O,
[Description("Private")]
p,
[Description("Owner")]
q,
[Description("No Kicks Allowed")]
Q,
[Description("Registered")]
r,
[Description("Registered Only")]
R,
[Description("Secret")]
s,
[Description("Strip Mirc Colors")]
S,
[Description("+hoaq Only Topic Change")]
t,
[Description("No Notices")]
T,
[Description("Auditorium")]
u,
[Description("Voice")]
v,
[Description("No Invite")]
V,
[Description("Secure Only")]
z
}

public enum UserMode
{
[Description("Service Administrator")]
a,
[Description("Service Administrator")]
A,
[Description("Bot")]
B,
[Description("Connection Notice")]
c,
[Description("C-oAdministrator")]
C,
[Description("Deaf")]
d,
[Description("View 'Eyes' Server Messages")]
e,
[Description("Flood Alerts")]
f,
[Description("Remote Server Connection Notice")]
F,
[Description("GlobOp and LocOp Messages")]
g,
[Description("Censored")]
G,
[Description("Helpful")]
h,
[Description("Hide IRCop Status")]
H,
[Description("Invisible")]
i,
[Description("Junk Messages")]
j,
[Description("Kill Messages")]
k,
[Description("Nick Change Notice")]
n,
[Description("Network Administrator")]
N,
[Description("Global Operator")]
o,
[Description("Local Operator")]
O,
[Description("Hide All Channels")]
p,
[Description("U:Lines Only")]
q,
[Description("Registered")]
r,
[Description("Registered Messages Only")]
R,
[Description("Server Notices")]
s,
[Description("Services Only")]
S,
[Description("VHost")]
t,
[Description("No CTCP")]
T,
[Description("Receive Bad DCC")]
v,
[Description("WebTV User")]
V,
[Description("Wallops")]
w,
[Description("Whois Notice")]
W,
[Description("Hidden Hostname")]
x,
[Description("Secure Connection")]
z
}

public enum ServerStat
{
[Description("List of Servers that allow Server Connection")]
c,
[Description("List of Server Hubs")]
h,
[Description("List of Hosts that allow Client Connection")]
i,
[Description("List of banned user/hostname of Server")]
k,
[Description("Lists Server Connections")]
l,
[Description("Commands Supported")]
m,
[Description("Lists Hosts that allow Normal Operators")]
o,
[Description("List Class lines from Server Config")]
y,
[Description("Server Uptime")]
u
}

public class BotError
{
public ErrorType Type { get; set; }
public string Message { get; set; }
}

public class Server
{
public string Name { get; set; }
public List<IPEndPoint> Hosts { get; set; }
public List<Channel> Channels { get; set; }
public bool AutoConnect { get; set; }
public Nick Nick { get; set; }
}

public class Message
{
public Server Server { get; set; }
public Channel Channel { get; set; }
public Nick Sender { get; set; }
public Nick Recipient { get; set; }
public string Parameters { get; set; }
}

// IRC Reply Codes //
public enum IRCReplyCode
{
RPL_WELCOME = 1,
RPL_YOURHOST = 2,
RPL_CREATED = 3,
RPL_MYINFO = 4,
RPL_BOUNCE = 5,
RPL_TRACELINK = 200,
RPL_TRACECONNECTING = 201,
RPL_TRACEHANDSHAKE = 202,
RPL_TRACEUNKNOWN = 203,
RPL_TRACEOPERATOR = 204,
RPL_TRACEUSER = 205,
RPL_TRACESERVER = 206,
RPL_TRACESERVICE = 207,
RPL_TRACENEWTYPE = 208,
RPL_TRACECLASS = 209,
RPL_TRACERECONNECT = 210,
RPL_STATSLINKINFO = 211,
RPL_STATSCOMMANDS = 212,
RPL_ENDOFSTATS = 219,
RPL_UMODEIS = 221,
RPL_SERVLIST = 234,
RPL_SERVLISTEND = 235,
RPL_STATSUPTIME = 242,
RPL_STATSOLINE = 243,
RPL_LUSERCLIENT = 251,
RPL_LUSEROP = 252,
RPL_LUSERUNKNOWN = 253,
RPL_LUSERCHANNELS = 254,
RPL_LUSERME = 255,
RPL_ADMINME = 256,
RPL_ADMINLOC1 = 257,
RPL_ADMINLOC2 = 258,
RPL_ADMINEMAIL = 259,
RPL_TRACELOG = 261,
RPL_TRACEEND = 262,
RPL_TRYAGAIN = 263,
RPL_AWAY = 301,
RPL_USERHOST = 302,
RPL_ISON = 303,
RPL_UNAWAY = 305,
RPL_NOWAWAY = 306,
RPL_WHOISUSER = 311,
RPL_WHOISSERVER = 312,
RPL_WHOISOPERATOR = 313,
RPL_WHOWASUSER = 314,
RPL_ENDOFWHO = 315,
RPL_WHOISIDLE = 317,
RPL_ENDOFWHOIS = 318,
RPL_WHOISCHANNELS = 319,
RPL_LISTSTART = 321,
RPL_LIST = 322,
RPL_LISTEND = 323,
RPL_CHANNELMODEIS = 324,
RPL_UNIQOPIS = 325,
RPL_NOTOPIC = 331,
RPL_TOPIC = 332,
RPL_INVITING = 341,
RPL_SUMMONING = 342,
RPL_INVITELIST = 346,
RPL_ENDOFINVITELIST = 347,
RPL_EXCEPTLIST = 348,
RPL_ENDOFEXCEPTLIST = 349,
RPL_VERSION = 351,
RPL_WHOREPLY = 352,
RPL_NAMREPLY = 353,
RPL_LINKS = 364,
RPL_ENDOFLINKS = 365,
RPL_ENDOFNAMES = 366,
RPL_BANLIST = 367,
RPL_ENDOFBANLIST = 368,
RPL_ENDOFWHOWAS = 369,
RPL_INFO = 371,
RPL_MOTD = 372,
RPL_ENDOFINFO = 374,
RPL_MOTDSTART = 375,
RPL_ENDOFMOTD = 376,
RPL_YOUREOPER = 381,
RPL_REHASHING = 382,
RPL_YOURESERVICE = 383,
RPL_TIME = 391,
RPL_USERSSTART = 392,
RPL_USERS = 393,
RPL_ENDOFUSERS = 394,
RPL_NOUSERS = 395
}

// IRC Error Codes //
public enum IRCErrorCode
{
ERR_NOSUCHNICK = 401,
ERR_NOSUCHSERVER = 402,
ERR_NOSUCHCHANNEL = 403,
ERR_CANNOTSENDTOCHAN = 404,
ERR_TOOMANYCHANNELS = 405,
ERR_WASNOSUCHNICK = 406,
ERR_TOOMANYTARGETS = 407,
ERR_NOSUCHSERVICE = 408,
ERR_NOORIGIN = 409,
ERR_NORECIPIENT = 411,
ERR_NOTEXTTOSEND = 412,
ERR_NOTOPLEVEL = 413,
ERR_WILDTOPLEVEL = 414,
ERR_BADMASK = 415,
ERR_UNKNOWNCOMMAND = 421,
ERR_NOMOTD = 422,
ERR_NOADMININFO = 423,
ERR_FILEERROR = 424,
ERR_NONICKNAMEGIVEN = 431,
ERR_ERRONEUSNICKNAME = 432,
ERR_NICKNAMEINUSE = 433,
ERR_NICKCOLLISION = 436,
ERR_UNAVAILRESOURCE = 437,
ERR_USERNOTINCHANNEL = 441,
ERR_NOTONCHANNEL = 442,
ERR_USERONCHANNEL = 443,
ERR_NOLOGIN = 444,
ERR_SUMMONDISABLED = 445,
ERR_USERSDISABLED = 446,
ERR_NOTREGISTERED = 451,
ERR_NEEDMOREPARAMS = 461,
ERR_ALREADYREGISTRED = 462,
ERR_NOPERMFORHOST = 463,
ERR_PASSWDMISMATCH = 464,
ERR_YOUREBANNEDCREEP = 465,
ERR_YOUWILLBEBANNED = 466,
ERR_KEYSET = 467,
ERR_CHANNELISFULL = 471,
ERR_UNKNOWNMODE = 472,
ERR_INVITEONLYCHAN = 473,
ERR_BANNEDFROMCHAN = 474,
ERR_BADCHANNELKEY = 475,
ERR_BADCHANMASK = 476,
ERR_NOCHANMODES = 477,
ERR_BANLISTFULL = 478,
ERR_NOPRIVILEGES = 481,
ERR_CHANOPRIVSNEEDED = 482,
ERR_CANTKILLSERVER = 483,
ERR_RESTRICTED = 484,
ERR_UNIQOPPRIVSNEEDED = 485,
ERR_NOOPERHOST = 491,
ERR_UMODEUNKNOWNFLAG = 501,
ERR_USERSDONTMATCH = 502
}
}

Access.cs → IRCServices/AccessControl/Access.cs View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot
namespace Combot.IRCServices.AccessControl
{
class Access
{

+ 138
- 0
IRCServices/Channel.cs View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot.IRCServices
{
public class Channel
{
public string Name { get; set; }
public string Topic { get; set; }
public string Key { get; set; }
public bool AutoJoin { get; set; }
public bool Joined { get; set; }
public DateTime Registration { get; set; }
public List<ChannelMode> Modes { get; set; }
public List<Nick> Nicks { get; set; }

private IRC _IRC;

public Channel(IRC irc)
{
Name = string.Empty;
Topic = string.Empty;
Key = string.Empty;
AutoJoin = false;
Joined = false;
Registration = DateTime.Now;
Modes = new List<ChannelMode>();
Nicks = new List<Nick>();
_IRC = irc;
}

public void AddNick(Nick nick)
{
Nicks.Add(nick);
}

public void AddNicks(List<Nick> nicks)
{
Nicks.AddRange(nicks);
}

public void RemoveNick(Nick nick)
{
if (Nicks.Contains(nick))
{
Nicks.Remove(nick);
}
}

public void RemoveNicks(List<Nick> nicks)
{
foreach (Nick nick in nicks)
{
RemoveNick(nick);
}
}

public void AddMode(ChannelMode mode)
{
if (!Modes.Contains(mode))
{
ChannelModeInfo modeInfo = new ChannelModeInfo();
modeInfo.Mode = mode;
modeInfo.Set = true;
_IRC.IRCSendMode(Name, modeInfo);
Modes.Add(mode);
}
}

public void AddModes(List<ChannelMode> modes)
{
if (!modes.TrueForAll(mode => Modes.Contains(mode)))
{
List<ChannelModeInfo> modeInfos = new List<ChannelModeInfo>();
modes.ForEach(mode => modeInfos.Add(new ChannelModeInfo() { Mode = mode, Set = true }));
_IRC.IRCSendMode(Name, modeInfos);
Modes.AddRange(modes);
}
}

public void RemoveMode(ChannelMode mode)
{
if (Modes.Contains(mode))
{
ChannelModeInfo modeInfo = new ChannelModeInfo();
modeInfo.Mode = mode;
modeInfo.Set = false;
_IRC.IRCSendMode(Name, modeInfo);
Modes.Remove(mode);
}
}

public void RemoveModes(List<ChannelMode> modes)
{
List<ChannelMode> validModes = Modes.FindAll(mode => mode == modes.Find(m => m == mode));
List<ChannelModeInfo> modeInfos = new List<ChannelModeInfo>();
validModes.ForEach(mode => modeInfos.Add(new ChannelModeInfo() { Mode = mode, Set = true }));
validModes.ForEach(mode => Modes.Remove(mode));
_IRC.IRCSendMode(Name, modeInfos);
}

public void Join()
{
if (!Joined)
{
_IRC.IRCSendJoin(Name, Key);
Joined = true;
}
}

public void Part()
{
if (Joined)
{
_IRC.IRCSendPart(Name);
Joined = false;
}
}

public void SetTopic(string topic)
{

if (Joined)
{
_IRC.IRCSendTopic(Name, topic);
}
}

public string GetTopic()
{

return string.Empty;
}
}
}

+ 115
- 0
IRCServices/IRC.cs View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using Combot.IRCServices.Messaging;
using Combot.IRCServices.TCP;

namespace Combot.IRCServices
{
public partial class IRC
{
public bool Connected;
public List<Channel> Channels = new List<Channel>();
public Messages Message;
public event Action DisconnectEvent;

private TCPInterface _TCP;
private Thread TCPReader;
private event Action<string> TCPMessageEvent;

public IRC()
{
Connected = false;
_TCP = new TCPInterface();
Message = new Messages(this);
TCPMessageEvent += Message.ParseTCPMessage;
Message.ErrorMessageEvent += HandleErrorMessage;
Message.PingEvent += HandlePing;
}

public bool Connect(IPAddress IP, int port, int readTimeout = 5000, int allowedFailedCount = 0)
{
bool result = false;
if (!_TCP.Connected)
{
result = _TCP.Connect(IP, port, readTimeout, allowedFailedCount);
if (result)
{
TCPReader = new Thread(ReadTCPMessages);
TCPReader.IsBackground = true;
TCPReader.Start();
}
}

return result;
}

public bool Disconnect()
{
bool result = false;

if (_TCP.Connected)
{
_TCP.Disconnect();
}

if (DisconnectEvent != null)
{
DisconnectEvent();
}

return result;
}

public void Login(string serverName, Nick nick)
{
IRCSendNick(nick.Nickname);
IRCSendUser(nick.Nickname, nick.Host, serverName, nick.Realname);
}

private void ReadTCPMessages()
{
while (_TCP.Connected)
{
string response = ReadTCPMessage();
if (TCPMessageEvent != null && response != null && response != string.Empty)
{
TCPMessageEvent(response);
}

Thread.Sleep(10);
}
}

private string ReadTCPMessage()
{
if (_TCP.Connected)
{
return _TCP.Read();
}
return null;
}

private void SendTCPMessage(string message)
{
if (_TCP.Connected)
{
_TCP.Write(message);
}
}

private void HandleErrorMessage(object sender, IRCServices.Messaging.ErrorMessage e)
{
Disconnect();
}

private void HandlePing(object sender, IRCServices.Messaging.PingInfo e)
{
IRCSendPong(e.Message);
}
}
}

+ 63
- 0
IRCServices/IRCServices.csproj View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{65FCBF1C-8C9E-4688-BECC-185D9030899F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IRCServices</RootNamespace>
<AssemblyName>IRCServices</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AccessControl\Access.cs" />
<Compile Include="Channel.cs" />
<Compile Include="IRC.cs" />
<Compile Include="Messaging\GetReply.cs" />
<Compile Include="Messaging\Messages.cs" />
<Compile Include="Messaging\MessageTypes.cs" />
<Compile Include="Methods.cs" />
<Compile Include="Nick.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TCP\TCPInterface.cs" />
<Compile Include="TCP\TCPTypes.cs" />
<Compile Include="Types.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 18
- 0
IRCServices/Messaging/GetReply.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Combot.IRCServices.Messaging
{
public class GetReply
{
public bool Reattach = true;
public bool Result = false;
public ManualResetEventSlim Ready = new ManualResetEventSlim(false);
public List<IRCReplyCode> Replies = new List<IRCReplyCode>();
public List<IRCErrorCode> Errors = new List<IRCErrorCode>();
}
}

+ 267
- 0
IRCServices/Messaging/MessageTypes.cs View File

@@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Combot.IRCServices.Messaging
{
abstract public class IMessage : EventArgs
{
public DateTime TimeStamp { get; set; }
}

abstract public class IReply : IMessage
{
public string Message { get; set; }
}

public class ServerReplyMessage : IReply
{
public IRCReplyCode ReplyCode { get; set; }
}

public class ServerErrorMessage : IReply
{
public IRCErrorCode ErrorCode { get; set; }
}

public class ErrorMessage : IMessage
{
public string Message { get; set; }
}

public class ChannelMessage : IMessage
{
public Channel Channel { get; set; }
public Nick Sender { get; set; }
public string Message { get; set; }
}

public class PrivateMessage : IMessage
{
public Nick Sender { get; set; }
public string Message { get; set; }
}

public class ServerNotice : IMessage
{
public string Message { get; set; }
}

public class ChannelNotice : IMessage
{
public Channel Channel { get; set; }
public Nick Sender { get; set; }
public string Message { get; set; }
}

public class PrivateNotice : IMessage
{
public Nick Sender { get; set; }
public string Message { get; set; }
}

public class TopicChangeInfo : IMessage
{
public Channel Channel { get; set; }
public Nick Nick { get; set; }
public string Topic { get; set; }
}

public class ChannelModeChangeInfo : IMessage
{
public Channel Channel { get; set; }
public Nick Nick { get; set; }
public List<ChannelModeInfo> Modes { get; set; }
}

public class UserModeChangeInfo : IMessage
{
public Nick Nick { get; set; }
public List<UserModeInfo> Modes { get; set; }
}

public class JoinChannelInfo : IMessage
{
public Channel Channel { get; set; }
public Nick Nick { get; set; }
}

public class PartChannelInfo : IMessage
{
public Channel Channel { get; set; }
public Nick Nick { get; set; }
}

public class KickInfo : IMessage
{
public Channel Channel { get; set; }
public Nick Nick { get; set; }
public Nick KickedNick { get; set; }
public string Reason { get; set; }
}

public class QuitInfo : IMessage
{
public Nick Nick { get; set; }
public string Message { get; set; }
}

public class PingInfo : IMessage
{
public string Message { get; set; }
}

public class PongInfo : IMessage
{
public string Message { get; set; }
}

// IRC Reply Codes //
public enum IRCReplyCode
{
RPL_WELCOME = 1,
RPL_YOURHOST = 2,
RPL_CREATED = 3,
RPL_MYINFO = 4,
RPL_BOUNCE = 5,
RPL_TRACELINK = 200,
RPL_TRACECONNECTING = 201,
RPL_TRACEHANDSHAKE = 202,
RPL_TRACEUNKNOWN = 203,
RPL_TRACEOPERATOR = 204,
RPL_TRACEUSER = 205,
RPL_TRACESERVER = 206,
RPL_TRACESERVICE = 207,
RPL_TRACENEWTYPE = 208,
RPL_TRACECLASS = 209,
RPL_TRACERECONNECT = 210,
RPL_STATSLINKINFO = 211,
RPL_STATSCOMMANDS = 212,
RPL_ENDOFSTATS = 219,
RPL_UMODEIS = 221,
RPL_SERVLIST = 234,
RPL_SERVLISTEND = 235,
RPL_STATSUPTIME = 242,
RPL_STATSOLINE = 243,
RPL_LUSERCLIENT = 251,
RPL_LUSEROP = 252,
RPL_LUSERUNKNOWN = 253,
RPL_LUSERCHANNELS = 254,
RPL_LUSERME = 255,
RPL_ADMINME = 256,
RPL_ADMINLOC1 = 257,
RPL_ADMINLOC2 = 258,
RPL_ADMINEMAIL = 259,
RPL_TRACELOG = 261,
RPL_TRACEEND = 262,
RPL_TRYAGAIN = 263,
RPL_AWAY = 301,
RPL_USERHOST = 302,
RPL_ISON = 303,
RPL_UNAWAY = 305,
RPL_NOWAWAY = 306,
RPL_WHOISUSER = 311,
RPL_WHOISSERVER = 312,
RPL_WHOISOPERATOR = 313,
RPL_WHOWASUSER = 314,
RPL_ENDOFWHO = 315,
RPL_WHOISIDLE = 317,
RPL_ENDOFWHOIS = 318,
RPL_WHOISCHANNELS = 319,
RPL_LISTSTART = 321,
RPL_LIST = 322,
RPL_LISTEND = 323,
RPL_CHANNELMODEIS = 324,
RPL_UNIQOPIS = 325,
RPL_NOTOPIC = 331,
RPL_TOPIC = 332,
RPL_INVITING = 341,
RPL_SUMMONING = 342,
RPL_INVITELIST = 346,
RPL_ENDOFINVITELIST = 347,
RPL_EXCEPTLIST = 348,
RPL_ENDOFEXCEPTLIST = 349,
RPL_VERSION = 351,
RPL_WHOREPLY = 352,
RPL_NAMREPLY = 353,
RPL_LINKS = 364,
RPL_ENDOFLINKS = 365,
RPL_ENDOFNAMES = 366,
RPL_BANLIST = 367,
RPL_ENDOFBANLIST = 368,
RPL_ENDOFWHOWAS = 369,
RPL_INFO = 371,
RPL_MOTD = 372,
RPL_ENDOFINFO = 374,
RPL_MOTDSTART = 375,
RPL_ENDOFMOTD = 376,
RPL_YOUREOPER = 381,
RPL_REHASHING = 382,
RPL_YOURESERVICE = 383,
RPL_TIME = 391,
RPL_USERSSTART = 392,
RPL_USERS = 393,
RPL_ENDOFUSERS = 394,
RPL_NOUSERS = 395
}

// IRC Error Codes //
public enum IRCErrorCode
{
ERR_NOSUCHNICK = 401,
ERR_NOSUCHSERVER = 402,
ERR_NOSUCHCHANNEL = 403,
ERR_CANNOTSENDTOCHAN = 404,
ERR_TOOMANYCHANNELS = 405,
ERR_WASNOSUCHNICK = 406,
ERR_TOOMANYTARGETS = 407,
ERR_NOSUCHSERVICE = 408,
ERR_NOORIGIN = 409,
ERR_NORECIPIENT = 411,
ERR_NOTEXTTOSEND = 412,
ERR_NOTOPLEVEL = 413,
ERR_WILDTOPLEVEL = 414,
ERR_BADMASK = 415,
ERR_UNKNOWNCOMMAND = 421,
ERR_NOMOTD = 422,
ERR_NOADMININFO = 423,
ERR_FILEERROR = 424,
ERR_NONICKNAMEGIVEN = 431,
ERR_ERRONEUSNICKNAME = 432,
ERR_NICKNAMEINUSE = 433,
ERR_NICKCOLLISION = 436,
ERR_UNAVAILRESOURCE = 437,
ERR_USERNOTINCHANNEL = 441,
ERR_NOTONCHANNEL = 442,
ERR_USERONCHANNEL = 443,
ERR_NOLOGIN = 444,
ERR_SUMMONDISABLED = 445,
ERR_USERSDISABLED = 446,
ERR_NOTREGISTERED = 451,
ERR_NEEDMOREPARAMS = 461,
ERR_ALREADYREGISTRED = 462,
ERR_NOPERMFORHOST = 463,
ERR_PASSWDMISMATCH = 464,
ERR_YOUREBANNEDCREEP = 465,
ERR_YOUWILLBEBANNED = 466,
ERR_KEYSET = 467,
ERR_CHANNELISFULL = 471,
ERR_UNKNOWNMODE = 472,
ERR_INVITEONLYCHAN = 473,
ERR_BANNEDFROMCHAN = 474,
ERR_BADCHANNELKEY = 475,
ERR_BADCHANMASK = 476,
ERR_NOCHANMODES = 477,
ERR_BANLISTFULL = 478,
ERR_NOPRIVILEGES = 481,
ERR_CHANOPRIVSNEEDED = 482,
ERR_CANTKILLSERVER = 483,
ERR_RESTRICTED = 484,
ERR_UNIQOPPRIVSNEEDED = 485,
ERR_NOOPERHOST = 491,
ERR_UMODEUNKNOWNFLAG = 501,
ERR_USERSDONTMATCH = 502
}
}

+ 398
- 0
IRCServices/Messaging/Messages.cs View File

@@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Combot.IRCServices.Messaging
{
public class Messages
{
public event EventHandler<IReply> ServerReplyEvent;
public event EventHandler<ErrorMessage> ErrorMessageEvent;
public event EventHandler<ChannelMessage> ChannelMessageReceivedEvent;
public event EventHandler<PrivateMessage> PrivateMessageReceivedEvent;
public event EventHandler<ServerNotice> ServerNoticeReceivedEvent;
public event EventHandler<ChannelNotice> ChannelNoticeReceivedEvent;
public event EventHandler<PrivateNotice> PrivateNoticeReceivedEvent;
public event EventHandler<TopicChangeInfo> TopicChangeEvent;
public event EventHandler<ChannelModeChangeInfo> ChannelModeChangeEvent;
public event EventHandler<UserModeChangeInfo> UserModeChangeEvent;
public event EventHandler<JoinChannelInfo> JoinChannelEvent;
public event EventHandler<PartChannelInfo> PartChannelEvent;
public event EventHandler<KickInfo> KickEvent;
public event EventHandler<QuitInfo> QuitEvent;
public event EventHandler<PingInfo> PingEvent;
public event EventHandler<PongInfo> PongEvent;

private IRC _IRC;

internal Messages(IRC irc)
{
_IRC = irc;
}

internal void ParseTCPMessage(string tcpMessage)
{
DateTime messageTime = DateTime.Now;
Regex messageRegex = new Regex(@"^:(?<Sender>[^\s]+)\s(?<Type>[^\s]+)\s(?<Recipient>[^\s]+)\s(?<Args>.*)", RegexOptions.None);
Regex senderRegex = new Regex(@"^:(?<Nick>[^\s]+)!(?<Realname>[^\s]+)@(?<Host>[^\s]+)", RegexOptions.None);
Regex pingRegex = new Regex(@"^PING :(?<Message>.+)", RegexOptions.None);
Regex pongRegex = new Regex(@"^PONG :(?<Message>.+)", RegexOptions.None);
Regex errorRegex = new Regex(@"^ERROR :(?<Message>.+)", RegexOptions.None);

string[] messages = tcpMessage.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

foreach (string message in messages)
{
if (messageRegex.IsMatch(message))
{
Match match = messageRegex.Match(message);
string type = match.Groups["Type"].Value;
string sender = match.Groups["Sender"].Value;
string recipient = match.Groups["Recipient"].Value;
string args = match.Groups["Args"].Value;
Match senderMatch = senderRegex.Match(sender);
string senderNick = sender;
string senderRealname = sender;
string senderHost = sender;
if (senderMatch.Success)
{
senderNick = senderMatch.Groups["Nick"].Value;
senderRealname = senderMatch.Groups["Realname"].Value;
senderHost = senderMatch.Groups["Nick"].Value;
}

int replyCode;
if (int.TryParse(type, out replyCode))
{
// The message was a reply to a command sent
if (Enum.IsDefined(typeof(IRCReplyCode), replyCode))
{
if (ServerReplyEvent != null)
{
ServerReplyEvent(this, new ServerReplyMessage() { TimeStamp = messageTime, ReplyCode = (IRCReplyCode)replyCode, Message = args.Remove(0, 1) });
}
}
else if (Enum.IsDefined(typeof(IRCErrorCode), replyCode))
{
if (ServerReplyEvent != null)
{
ServerReplyEvent(this, new ServerErrorMessage() { TimeStamp = messageTime, ErrorCode = (IRCErrorCode)replyCode, Message = args.Remove(0, 1) });
}
}
}
else
{
switch (type)
{
case "PRIVMSG":
if (recipient.StartsWith("&") || recipient.StartsWith("#"))
{
ChannelMessage msg = new ChannelMessage();
msg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
if (msg.Channel != null && msg.Channel.Modes != null && msg.Channel.Modes.Contains(ChannelMode.n))
{
msg.Sender = msg.Channel.Nicks.Find(nick => nick.Nickname == senderNick);
}
else
{
msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
}
msg.Message = args.Remove(0, 1);

if (ChannelMessageReceivedEvent != null)
{
ChannelMessageReceivedEvent(this, msg);
}
}
else
{
PrivateMessage msg = new PrivateMessage();
msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
msg.Message = args.Remove(0, 1);

if (PrivateMessageReceivedEvent != null)
{
PrivateMessageReceivedEvent(this, msg);
}
}
break;
case "NOTICE":
if (recipient.StartsWith("&") || recipient.StartsWith("#"))
{
ChannelNotice msg = new ChannelNotice();
msg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
if (msg.Channel != null && msg.Channel.Modes != null && msg.Channel.Modes.Contains(ChannelMode.n))
{
msg.Sender = msg.Channel.Nicks.Find(nick => nick.Nickname == senderNick);
}
else
{
msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
}
msg.Message = args.Remove(0, 1);

if (ChannelNoticeReceivedEvent != null)
{
ChannelNoticeReceivedEvent(this, msg);
}
}
else
{
PrivateNotice msg = new PrivateNotice();
msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
msg.Message = args.Remove(0, 1);

if (PrivateNoticeReceivedEvent != null)
{
PrivateNoticeReceivedEvent(this, msg);
}
}
break;
case "MODE":
if (recipient.StartsWith("&") || recipient.StartsWith("#"))
{
ChannelModeChangeInfo modeMsg = new ChannelModeChangeInfo();
modeMsg.Modes = new List<ChannelModeInfo>();
modeMsg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
if (modeMsg.Channel != null && modeMsg.Channel.Nicks != null && modeMsg.Channel.Nicks.Exists(nick => nick.Nickname == senderNick))
{
modeMsg.Nick = modeMsg.Channel.Nicks.Find(nick => nick.Nickname == senderNick);
}
else
{
modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
}

string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
bool set = true;
int argIndex = 1;
foreach (char mode in modeInfo)
{
if (mode.Equals('-'))
{
set = false;
}
else if (mode.Equals('+'))
{
set = true;
}
else
{
ChannelModeInfo newMode = new ChannelModeInfo();
newMode.Set = set;
newMode.Mode = (ChannelMode)Enum.Parse(typeof(ChannelMode), mode.ToString());
if (modeArgs.GetUpperBound(0) > argIndex)
{
switch (newMode.Mode)
{
case ChannelMode.k:
case ChannelMode.l:
case ChannelMode.v:
case ChannelMode.h:
case ChannelMode.o:
case ChannelMode.a:
case ChannelMode.q:
case ChannelMode.b:
case ChannelMode.e:
case ChannelMode.I:
newMode.Parameter = modeArgs[argIndex];
argIndex++;
break;
default:
newMode.Parameter = string.Empty;
break;
}
}
else
{
newMode.Parameter = string.Empty;
}
modeMsg.Modes.Add(newMode);
}
}

if (ChannelModeChangeEvent != null)
{
ChannelModeChangeEvent(this, modeMsg);
}
}
else
{
UserModeChangeInfo modeMsg = new UserModeChangeInfo();
modeMsg.Modes = new List<UserModeInfo>();

string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
bool set = true;
foreach (char mode in modeInfo)
{
if (mode.Equals('-'))
{
set = false;
}
else if (mode.Equals('+'))
{
set = true;
}
else
{
UserModeInfo newMode = new UserModeInfo();
newMode.Set = set;
newMode.Mode = (UserMode)Enum.Parse(typeof(UserMode), mode.ToString());
modeMsg.Modes.Add(newMode);
}
}

if (UserModeChangeEvent != null)
{
UserModeChangeEvent(this, modeMsg);
}
}
break;
case "TOPIC":
TopicChangeInfo topicMsg = new TopicChangeInfo();
topicMsg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
if (topicMsg.Channel != null && topicMsg.Channel.Nicks != null && topicMsg.Channel.Nicks.Exists(nick => nick.Nickname == senderNick))
{
topicMsg.Nick = topicMsg.Channel.Nicks.Find(nick => nick.Nickname == senderNick);
}
else
{
topicMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
}
topicMsg.Topic = args.Remove(0, 1);

if (TopicChangeEvent != null)
{
TopicChangeEvent(this, topicMsg);
}
break;
case "JOIN":
JoinChannelInfo joinMsg = new JoinChannelInfo();
joinMsg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient.TrimStart(':'));
joinMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };

if (JoinChannelEvent != null)
{
JoinChannelEvent(this, joinMsg);
}
break;
case "PART":
PartChannelInfo partMsg = new PartChannelInfo();
partMsg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
partMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };

if (PartChannelEvent != null)
{
PartChannelEvent(this, partMsg);
}
break;
case "KICK":
KickInfo kickMsg = new KickInfo();
kickMsg.Channel = _IRC.Channels.Find(channel => channel.Name == recipient);
kickMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
string[] argSplit = args.Split(new char[] { ' ' }, StringSplitOptions.None);
kickMsg.KickedNick = kickMsg.Channel.Nicks.Find(nick => nick.Nickname == argSplit[0]);
List<string> reasonArgs = argSplit.ToList<string>();
reasonArgs.RemoveAt(0);
kickMsg.Reason = string.Join(" ", reasonArgs.ToArray()).Remove(0, 1);

if (KickEvent != null)
{
KickEvent(this, kickMsg);
}
break;
case "QUIT":
QuitInfo quitMsg = new QuitInfo();
quitMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
quitMsg.Message = recipient.Remove(0, 1);

if (QuitEvent != null)
{
QuitEvent(this, quitMsg);
}
break;
default:
break;
}
}
}
else if (pingRegex.IsMatch(message))
{
Match match = pingRegex.Match(message);
PingInfo ping = new PingInfo();
ping.Message = match.Groups["Message"].Value;

if (PingEvent != null)
{
PingEvent(this, ping);
}
}
else if (pongRegex.IsMatch(message))
{
Match match = pongRegex.Match(message);
PongInfo pong = new PongInfo();
pong.Message = match.Groups["Message"].Value;

if (PongEvent != null)
{
PongEvent(this, pong);
}
}
else if (errorRegex.IsMatch(message))
{
Match match = errorRegex.Match(message);
ErrorMessage error = new ErrorMessage();
error.Message = match.Groups["Message"].Value;

if (ErrorMessageEvent != null)
{
ErrorMessageEvent(this, error);
}
}
}
}

internal bool GetReply(List<IRCReplyCode> ReplyCodes, List<IRCErrorCode> ErrorCodes)
{
GetReply reply = new GetReply();
reply.Replies = ReplyCodes;
reply.Errors = ErrorCodes;
ServerReplyEvent += (sender, e) => HandleReply(sender, e, reply);
reply.Ready.Wait(TimeSpan.FromMilliseconds(5000));
reply.Reattach = false;
return reply.Result;
}

private void HandleReply(object sender, IReply message, GetReply reply)
{
bool replyFound = false;
if (message.GetType() == typeof(ServerReplyMessage))
{
ServerReplyMessage msg = (ServerReplyMessage)message;
replyFound = reply.Replies.Contains(msg.ReplyCode);
}
else
{
ServerErrorMessage msg = (ServerErrorMessage)message;
replyFound = reply.Errors.Contains(msg.ErrorCode);
}
if (replyFound)
{
reply.Result = replyFound;
reply.Ready.Set();
}
else
{
if (reply.Reattach)
{
ServerReplyEvent += (obj, e) => HandleReply(obj, e, reply);
}
}
}
}
}

+ 566
- 0
IRCServices/Methods.cs View File

@@ -0,0 +1,566 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;

namespace Combot.IRCServices
{
public partial class IRC
{
/// <summary>
/// Sends a private message to a nick or channel
/// </summary>
/// <param name="nick"></param>
/// <param name="message"></param>
public void IRCSendPrivMessage(string recipient, string message)
{
SendTCPMessage(string.Format("PRIVMSG {0} :{1}", recipient, message));
}

public void IRCSendPrivMessage(List<string> recipients, string message)
{
string recipient_list = string.Empty;
foreach (string recipient in recipients)
{
recipient_list += recipient + ",";
}

SendTCPMessage(string.Format("PRIVMSG {0} :{1}", recipient_list.TrimEnd(','), message));
}

/// <summary>
/// Sends a Notice to either a nick or channel
/// </summary>
/// <param name="nick"></param>
/// <param name="message"></param>
public void IRCSendNotice(string recipient, string message)
{
SendTCPMessage(string.Format("NOTICE {0} :{1}", recipient, message));
}

public void IRCSendNotice(List<string> recipients, string message)
{
string recipient_list = string.Empty;
foreach (string recipient in recipients)
{
recipient_list += recipient + ",";
}

SendTCPMessage(string.Format("NOTICE {0} :{1}", recipient_list.TrimEnd(','), message));
}

/// <summary>
/// Sends the connection password
/// </summary>
/// <param name="password"></param>
public void IRCSendPassword(string password)
{
SendTCPMessage(string.Format("PASSWORD {0}", password));
}

/// <summary>
/// Sends a Nick command to set the nickname
/// </summary>
/// <param name="nick"></param>
public void IRCSendNick(string nick)
{
SendTCPMessage(string.Format("NICK {0}", nick));
}

/// <summary>
/// Sends the User command to set a user
/// </summary>
/// <param name="user"></param>
public void IRCSendUser(string username, string hostname, string servername, string realname)
{
SendTCPMessage(string.Format("USER {0} {1} {2} :{3}", username, hostname, servername, realname));
}

/// <summary>
/// Sends the Oper command to authorize the client as a newtork Oper
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
public void IRCSendOper(string username, string password)
{
SendTCPMessage(string.Format("OPER {0} {1}", username, password));
}

/// <summary>
/// Sends a Quit command to end the client session
/// </summary>
/// <param name="message"></param>
public void IRCSendQuit()
{
SendTCPMessage("QUIT");
}

public void IRCSendQuit(string message)
{
SendTCPMessage(string.Format("QUIT :{0}", message));
}

/// <summary>
/// Sends a Join command to join a channel
/// </summary>
/// <param name="channel"></param>
public void IRCSendJoin(string channel, string key = "")
{
string message = string.Empty;
message = (key != string.Empty) ? string.Format("{0}; {1}", channel, key) : channel;
SendTCPMessage(string.Format("JOIN {0}", message));
}

public void IRCSendJoin(List<string> channels, List<string> keys)
{
string message = string.Empty;
string channel_string = string.Empty;
string key_string = string.Empty;

foreach (string channel in channels)
{
channel_string += channel + ",";
}
foreach (string key in keys)
{
if (key != string.Empty)
{
key_string += key + ",";
}
}
channel_string = channel_string.TrimEnd(',');
key_string = key_string.TrimEnd(',');

message = (key_string != string.Empty) ? string.Format("{0}; {1}", channel_string, key_string) : channel_string;
SendTCPMessage(string.Format("JOIN {0}", message));
}

/// <summary>
/// Sends a Part command to leave a channel
/// </summary>
/// <param name="channel"></param>
public void IRCSendPart(string channel)
{
SendTCPMessage(string.Format("PART {0}", channel));
}

public void IRCSendPart(List<string> channels)
{
string channel_list = string.Empty;
foreach (string channel in channels)
{
channel_list += channel + ",";
}

SendTCPMessage(string.Format("PART {0}", channel_list.TrimEnd(',')));
}


/// <summary>
/// Sends a Mode command for either a channel mode or user mode
/// </summary>
/// <param name="channel"></param>
/// <param name="mode"></param>
public void IRCSendMode(string channel, ChannelModeInfo modeInfo)
{
string mode_set = modeInfo.Set ? "+" : "-";
SendTCPMessage(string.Format("MODE {0} {1} {2}", channel, mode_set + modeInfo.Mode.ToString(), modeInfo.Parameter));
}

public void IRCSendMode(string channel, List<ChannelModeInfo> modeInfos)
{
foreach (ChannelModeInfo modeInfo in modeInfos)
{
IRCSendMode(channel, modeInfo);
}
}
public void IRCSendMode(string nick, UserModeInfo modeInfo)
{
string mode_set = modeInfo.Set ? "+" : "-";
SendTCPMessage(string.Format("MODE {0} {1}", nick, mode_set + modeInfo.Mode.ToString()));
}

public void IRCSendMode(string nick, List<UserModeInfo> modeInfos)
{
foreach (UserModeInfo modeInfo in modeInfos)
{
IRCSendMode(nick, modeInfo);
}
}

/// <summary>
/// Sends a Topic command to change the channels topic or view the current one
/// </summary>
/// <param name="channel"></param>
public void IRCSendTopic(string channel)