Browse Source

Added moderation module.

Added help module.
Added better access checking.
Added more channel/nick updating.
Added better control of commands.
pull/1/head
Teknikode 7 years ago
parent
commit
08923b3987
  1. 6
      Combot.sln
  2. 217
      Combot/Bot.cs
  3. 4
      Combot/Combot.csproj
  4. 20
      Combot/Configurations/ServerConfig.cs
  5. 18
      Combot/Help.cs
  6. 9
      Combot/MessageType.cs
  7. 7
      Combot/Modules/Command.cs
  8. 14
      Combot/Modules/CommandArgument.cs
  9. 13
      Combot/Modules/CommandMessage.cs
  10. 21
      Combot/Modules/Module.cs
  11. 151
      Combot/Modules/ModuleClasses/Help.cs
  12. 236
      Combot/Modules/ModuleClasses/Moderation.cs
  13. 18
      Combot/Modules/ModuleClasses/PingMe.cs
  14. 42
      Combot/Modules/ModuleClasses/Version.cs
  15. 9
      Combot/Types.cs
  16. 4
      IRCServices/Channel.cs
  17. 107
      IRCServices/IRC.cs
  18. 55
      IRCServices/IRCSend.cs
  19. 50
      IRCServices/Messaging/Messages.cs
  20. 6
      IRCServices/Nick.cs
  21. 2
      IRCServices/Types.cs
  22. 102
      Interface/ViewModels/MainViewModel.cs
  23. 637
      readme.md

6
Combot.sln

@ -9,6 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Combot", "Combot\Combot.csp @@ -9,6 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Combot", "Combot\Combot.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRCServices", "IRCServices\IRCServices.csproj", "{65FCBF1C-8C9E-4688-BECC-185D9030899F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B78E5A1-A302-4587-8004-3D41DCEF5CD2}"
ProjectSection(SolutionItems) = preProject
Bot.ico = Bot.ico
readme.md = readme.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

217
Combot/Bot.cs

@ -19,22 +19,29 @@ namespace Combot @@ -19,22 +19,29 @@ namespace Combot
public List<Module> Modules;
public bool Connected = false;
public bool LoggedIn = false;
public static Dictionary<PrivilegeMode, AccessType> AccessTypeMapping = new Dictionary<PrivilegeMode, AccessType>() { { PrivilegeMode.v, AccessType.Voice }, { PrivilegeMode.h, AccessType.HalfOperator }, { PrivilegeMode.o, AccessType.Operator }, { PrivilegeMode.a, AccessType.SuperOperator }, { PrivilegeMode.q, AccessType.Founder } };
public Dictionary<PrivilegeMode, AccessType> PrivilegeModeMapping = new Dictionary<PrivilegeMode, AccessType>() { { PrivilegeMode.v, AccessType.Voice }, { PrivilegeMode.h, AccessType.HalfOperator }, { PrivilegeMode.o, AccessType.Operator }, { PrivilegeMode.a, AccessType.SuperOperator }, { PrivilegeMode.q, AccessType.Founder } };
public Dictionary<ChannelMode, AccessType> ChannelModeMapping = new Dictionary<ChannelMode, AccessType>() { { ChannelMode.v, AccessType.Voice }, { ChannelMode.h, AccessType.HalfOperator }, { ChannelMode.o, AccessType.Operator }, { ChannelMode.a, AccessType.SuperOperator }, { ChannelMode.q, AccessType.Founder } };
private bool GhostSent;
private int CurNickChoice;
public Bot(ServerConfig serverConfig)
{
IRC = new IRC();
Modules = new List<Module>();
GhostSent = false;
CurNickChoice = 0;
ServerConfig = serverConfig;
IRC = new IRC(serverConfig.MaxMessageLength, serverConfig.MessageSendDelay);
IRC.ConnectEvent += HandleConnectEvent;
IRC.DisconnectEvent += HandleDisconnectEvent;
IRC.Message.ServerReplyEvent += HandleReplyEvent;
IRC.Message.ChannelMessageReceivedEvent += HandleChannelMessageReceivedEvent;
IRC.Message.PrivateMessageReceivedEvent += HandlePrivateMessageReceivedEvent;
IRC.Message.PrivateNoticeReceivedEvent += HandlePrivateNoticeReceivedEvent;
IRC.Message.JoinChannelEvent += HandleJoinEvent;
IRC.Message.KickEvent += HandleKickEvent;
IRC.Message.ChannelModeChangeEvent += HandleChannelModeChangeEvent;
LoadModules();
}
@ -45,6 +52,7 @@ namespace Combot @@ -45,6 +52,7 @@ namespace Combot
public void Connect()
{
GhostSent = false;
CurNickChoice = 0;
bool serverConnected = false;
int i = 0;
do
@ -54,7 +62,7 @@ namespace Combot @@ -54,7 +62,7 @@ namespace Combot
IPAddress[] ipList = Dns.GetHostAddresses(ServerConfig.Hosts[i].Host);
foreach (IPAddress ip in ipList)
{
serverConnected = IRC.Connect(ip, ServerConfig.Hosts[i].Port, 5000);
serverConnected = IRC.Connect(ip, ServerConfig.Hosts[i].Port);
if (serverConnected)
{
break;
@ -73,7 +81,7 @@ namespace Combot @@ -73,7 +81,7 @@ namespace Combot
{
IRC.Login(ServerConfig.Name, new Nick()
{
Nickname = ServerConfig.Nickname,
Nickname = ServerConfig.Nicknames[CurNickChoice],
Host = Dns.GetHostName(),
Realname = ServerConfig.Realname,
Username = ServerConfig.Username
@ -106,6 +114,80 @@ namespace Combot @@ -106,6 +114,80 @@ namespace Combot
}
}
public bool CheckChannelAccess(string channel, string nickname, AccessType access)
{
Channel foundChannel = IRC.Channels.Find(chan => chan.Name == channel);
if (foundChannel != null)
{
Nick foundNick = foundChannel.Nicks.Find(nick => nick.Nickname == nickname);
if (foundNick != null)
{
for (int i = 0; i < foundNick.Privileges.Count; i++)
{
switch (PrivilegeModeMapping[foundNick.Privileges[i]])
{
case AccessType.User:
if (access == AccessType.User)
{
return true;
}
break;
case AccessType.Voice:
if (access == AccessType.User || access == AccessType.Voice)
{
return true;
}
break;
case AccessType.HalfOperator:
if (access == AccessType.User || access == AccessType.Voice || access == AccessType.HalfOperator)
{
return true;
}
break;
case AccessType.Operator:
if (access == AccessType.User || access == AccessType.Voice || access == AccessType.HalfOperator || access == AccessType.Operator)
{
return true;
}
break;
case AccessType.SuperOperator:
if (access == AccessType.User || access == AccessType.Voice || access == AccessType.HalfOperator || access == AccessType.Operator || access == AccessType.SuperOperator)
{
return true;
}
break;
case AccessType.Founder:
if (access == AccessType.User || access == AccessType.Voice || access == AccessType.HalfOperator || access == AccessType.Operator || access == AccessType.SuperOperator || access == AccessType.Founder)
{
return true;
}
break;
case AccessType.Owner:
return true;
break;
}
}
}
}
return false;
}
public bool CheckChannelAccess(string channel, string nickname, List<AccessType> access)
{
bool hasAccess = false;
for (int i = 0; i < access.Count; i++)
{
hasAccess = CheckChannelAccess(channel, nickname, access[i]);
if (hasAccess)
{
break;
}
}
return hasAccess;
}
private void HandleConnectEvent()
{
Connected = true;
@ -139,7 +221,32 @@ namespace Combot @@ -139,7 +221,32 @@ namespace Combot
}
}
private async void HandleReplyEvent(object sender, IReply e)
private void HandleChannelModeChangeEvent(object sender, ChannelModeChangeInfo e)
{
ChannelConfig channel = ServerConfig.Channels.Find(chan => chan.Name == e.Channel);
if (channel != null)
{
foreach (ChannelModeInfo mode in e.Modes)
{
switch (mode.Mode)
{
case ChannelMode.k:
if (mode.Set)
{
channel.Key = mode.Parameter;
}
else
{
channel.Key = string.Empty;
}
ServerConfig.Save();
break;
}
}
}
}
private void HandleReplyEvent(object sender, IReply e)
{
if (e.GetType() == typeof(ServerReplyMessage))
{
@ -149,11 +256,11 @@ namespace Combot @@ -149,11 +256,11 @@ namespace Combot
case IRCReplyCode.RPL_WELCOME:
// If the reply is Welcome, that means we are fully connected to the server.
LoggedIn = true;
if (!GhostSent && IRC.Nickname != ServerConfig.Nickname)
if (!GhostSent && IRC.Nickname != ServerConfig.Nicknames[CurNickChoice])
{
IRC.SendPrivateMessage("NickServ", string.Format("GHOST {0} {1}", ServerConfig.Nickname, ServerConfig.Password));
IRC.SendPrivateMessage("NickServ", string.Format("GHOST {0} {1}", ServerConfig.Nicknames[CurNickChoice], ServerConfig.Password));
Thread.Sleep(1000);
IRC.SendNick(ServerConfig.Nickname);
IRC.SendNick(ServerConfig.Nicknames[CurNickChoice]);
GhostSent = true;
}
// Identify to NickServ if need be
@ -175,7 +282,7 @@ namespace Combot @@ -175,7 +282,7 @@ namespace Combot
switch (error.ErrorCode)
{
case IRCErrorCode.ERR_NOTREGISTERED:
if (ServerConfig.Password != string.Empty && ServerConfig.Email != string.Empty)
if (ServerConfig.AutoRegister && ServerConfig.Password != string.Empty && ServerConfig.Email != string.Empty)
{
IRC.SendPrivateMessage("NickServ", string.Format("REGISTER {0} {1}", ServerConfig.Password, ServerConfig.Email));
}
@ -184,14 +291,15 @@ namespace Combot @@ -184,14 +291,15 @@ namespace Combot
if (LoggedIn == false)
{
string nick = string.Empty;
if (IRC.Nickname == ServerConfig.Nickname && ServerConfig.SecondaryNickname != string.Empty)
if (IRC.Nickname == ServerConfig.Nicknames[CurNickChoice] && ServerConfig.Nicknames.Count > CurNickChoice + 1)
{
nick = ServerConfig.SecondaryNickname;
CurNickChoice++;
nick = ServerConfig.Nicknames[CurNickChoice];
}
else
{
Random rand = new Random();
nick = string.Format("{0}_{1}", ServerConfig.Nickname, rand.Next(100000).ToString());
nick = string.Format("{0}_{1}", ServerConfig.Nicknames.First(), rand.Next(100000).ToString());
}
IRC.Login(ServerConfig.Name, new Nick()
{
@ -215,12 +323,36 @@ namespace Combot @@ -215,12 +323,36 @@ namespace Combot
&& !ServerConfig.NickBlacklist.Contains(e.Sender.Nickname)
)
{
ParseCommandMessage(e.TimeStamp, e.Message, e.Sender, e.Channel, LocationType.Channel);
ParseCommandMessage(e.TimeStamp, e.Message, e.Sender, e.Channel, MessageType.Channel);
}
}
}
private void HandlePrivateMessageReceivedEvent(object sender, PrivateMessage e)
{
// The message was a command
if (e.Message.StartsWith(ServerConfig.CommandPrefix))
{
if (!ServerConfig.NickBlacklist.Contains(e.Sender.Nickname))
{
ParseCommandMessage(e.TimeStamp, e.Message, e.Sender, e.Sender.Nickname, MessageType.Query);
}
}
}
private void HandlePrivateNoticeReceivedEvent(object sender, PrivateNotice e)
{
// The notice was a command
if (e.Message.StartsWith(ServerConfig.CommandPrefix))
{
if (!ServerConfig.NickBlacklist.Contains(e.Sender.Nickname))
{
ParseCommandMessage(e.TimeStamp, e.Message, e.Sender, e.Sender.Nickname, MessageType.Notice);
}
}
}
private void ParseCommandMessage(DateTime timestamp, string message, Nick sender, string location, LocationType locationType)
private void ParseCommandMessage(DateTime timestamp, string message, Nick sender, string location, MessageType messageType)
{
// Extract command and arguments
string[] msgArgs = message.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
@ -228,13 +360,16 @@ namespace Combot @@ -228,13 +360,16 @@ namespace Combot
List<string> argsOnly = msgArgs.ToList();
argsOnly.RemoveAt(0);
// Find the module that contains the command
Module module = Modules.Find(mod => mod.Commands.Exists(c => c.Triggers.Contains(command)) && mod.Loaded);
if (module != null)
{
// Find the command
Command cmd = module.Commands.Find(c => c.Triggers.Contains(command));
if (cmd != null)
{
CommandMessage newCommand = new CommandMessage();
List<CommandArgument> validArguments = cmd.Arguments.FindAll(arg => arg.MessageTypes.Contains(messageType));
newCommand.Nick.Copy(sender);
IRC.Channels.ForEach(channel => channel.Nicks.ForEach(nick =>
{
@ -245,21 +380,61 @@ namespace Combot @@ -245,21 +380,61 @@ namespace Combot
}));
newCommand.TimeStamp = timestamp;
newCommand.Location = location;
newCommand.LocationType = locationType;
newCommand.MessageType = messageType;
newCommand.Command = command;
if (argsOnly.Count > 0)
{
string[] argSplit = argsOnly.First().Split(new[] {' '}, cmd.Arguments.Count + 1, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < cmd.Arguments.Count && i <= argSplit.GetUpperBound(0); i++)
string[] argSplit = argsOnly.First().Split(new[] { ' ' }, validArguments.Count, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < validArguments.Count && i <= argSplit.GetUpperBound(0); i++)
{
newCommand.Arguments.Add(cmd.Arguments[i].Name, argSplit[i]);
newCommand.Arguments.Add(validArguments[i].Name, argSplit[i]);
}
}
bool invalidArgs = false;
for (int i = 0; i < newCommand.Arguments.Count; i++)
{
if (validArguments[i].AllowedValues.Count > 0)
{
// Check to see if any of the arguments are invalid
string argVal = newCommand.Arguments[validArguments[i].Name];
if (!validArguments[i].AllowedValues.Exists(val => val.ToLower() == argVal.ToLower()))
{
invalidArgs = true;
string invalidMessage = string.Format("Invalid value for \u0002{0}\u000F in \u0002{1}{2} {3}\u000F. Valid options are \u0002{4}\u000F.", validArguments[i].Name, ServerConfig.CommandPrefix, command, string.Join(" ", validArguments.Select(arg => { if (arg.Required) { return "\u001F" + arg.Name + "\u000F\u0002"; } return "[\u001F" + arg.Name + "\u000F\u0002]"; })), string.Join(", ", validArguments[i].AllowedValues));
switch (messageType)
{
case MessageType.Channel:
case MessageType.Query:
IRC.SendPrivateMessage(location, invalidMessage);
break;
case MessageType.Notice:
IRC.SendNotice(location, invalidMessage);
break;
}
break;
}
}
}
if (cmd.Arguments.FindAll(arg => arg.Required).Count <= newCommand.Arguments.Count)
if (validArguments.FindAll(arg => arg.Required).Count <= newCommand.Arguments.Count)
{
if (CommandReceivedEvent != null)
if (!invalidArgs)
{
if (CommandReceivedEvent != null)
{
CommandReceivedEvent(newCommand);
}
}
}
else
{
string missingArgument = string.Format("Missing a required argument for \u0002{0}{1} {2}\u000F. The required arguments are \u0002{3}\u000F.", ServerConfig.CommandPrefix, command, string.Join(" ", validArguments.Select(arg => { if (arg.Required) { return "\u001F" + arg.Name + "\u000F\u0002"; } return "[\u001F" + arg.Name + "\u000F\u0002]"; })), string.Join(", ", validArguments.Where(arg => arg.Required).Select(arg => arg.Name)));
if (messageType == MessageType.Channel || messageType == MessageType.Query)
{
IRC.SendPrivateMessage(location, missingArgument);
}
else if (messageType == MessageType.Notice)
{
CommandReceivedEvent(newCommand);
IRC.SendNotice(location, missingArgument);
}
}
}

4
Combot/Combot.csproj

@ -60,16 +60,18 @@ @@ -60,16 +60,18 @@
<ItemGroup>
<Compile Include="AccessType.cs" />
<Compile Include="Bot.cs" />
<Compile Include="MessageType.cs" />
<Compile Include="Modules\Command.cs" />
<Compile Include="Configurations\ChannelConfig.cs" />
<Compile Include="Configurations\Config.cs" />
<Compile Include="Configurations\HostConfig.cs" />
<Compile Include="Configurations\JsonHelper.cs" />
<Compile Include="Configurations\ServerConfig.cs" />
<Compile Include="Help.cs" />
<Compile Include="Modules\CommandArgument.cs" />
<Compile Include="Modules\CommandMessage.cs" />
<Compile Include="Modules\ModuleClasses\Help.cs" />
<Compile Include="Modules\ModuleClasses\Invite.cs" />
<Compile Include="Modules\ModuleClasses\Moderation.cs" />
<Compile Include="Modules\ModuleClasses\PingMe.cs" />
<Compile Include="Modules\ModuleClasses\Version.cs" />
<Compile Include="Modules\Option.cs" />

20
Combot/Configurations/ServerConfig.cs

@ -12,8 +12,7 @@ namespace Combot.Configurations @@ -12,8 +12,7 @@ namespace Combot.Configurations
{
public event Action ModifyEvent;
public string Name { get; set; }
public string Nickname { get; set; }
public string SecondaryNickname { get; set; }
public List<string> Nicknames { get; set; }
public string Realname { get; set; }
public string Username { get; set; }
public string Password { get; set; }
@ -25,8 +24,11 @@ namespace Combot.Configurations @@ -25,8 +24,11 @@ namespace Combot.Configurations
public List<ChannelConfig> Channels { get; set; }
public List<Module> Modules { get; set; }
public bool AutoConnect { get; set; }
public bool AutoRegister { get; set; }
public string CommandPrefix { get; set; }
public int JoinDelay { get; set; }
public int MaxMessageLength { get; set; }
public int MessageSendDelay { get; set; }
public ServerConfig()
{
@ -36,21 +38,23 @@ namespace Combot.Configurations @@ -36,21 +38,23 @@ namespace Combot.Configurations
public void SetDefaults()
{
Name = string.Empty;
Nicknames = new List<string>();
Realname = string.Empty;
Username = string.Empty;
Password = string.Empty;
Email = string.Empty;
AutoConnect = false;
AutoRegister = false;
CommandPrefix = string.Empty;
JoinDelay = 0;
MaxMessageLength = 400;
MessageSendDelay = 0;
Owners = new List<string>();
ChannelBlacklist = new List<string>();
NickBlacklist = new List<string>();
Channels = new List<ChannelConfig>();
Modules = new List<Module>();
Hosts = new List<HostConfig>();
Nickname = string.Empty;
SecondaryNickname = string.Empty;
Realname = string.Empty;
Username = string.Empty;
Password = string.Empty;
Email = string.Empty;
}
public void Save()

18
Combot/Help.cs

@ -1,18 +0,0 @@ @@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Combot
{
public class Help
{
private Bot _Bot;
public Help(Bot bot)
{
_Bot = bot;
}
}
}

9
Combot/MessageType.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace Combot
{
public enum MessageType
{
Channel,
Query,
Notice
}
}

7
Combot/Modules/Command.cs

@ -11,6 +11,7 @@ namespace Combot.Modules @@ -11,6 +11,7 @@ namespace Combot.Modules
public List<string> NickBlacklist { get; set; }
public List<string> Triggers { get; set; }
public List<CommandArgument> Arguments { get; set; }
public List<MessageType> AllowedMessageTypes { get; set; }
public List<AccessType> AllowedAccess { get; set; }
public bool ShowHelp { get; set; }
public bool SpamCheck { get; set; }
@ -34,6 +35,7 @@ namespace Combot.Modules @@ -34,6 +35,7 @@ namespace Combot.Modules
ChannelBlacklist = new List<string>();
NickBlacklist = new List<string>();
Arguments = new List<CommandArgument>();
AllowedMessageTypes = new List<MessageType>();
AllowedAccess = new List<AccessType>();
ShowHelp = true;
SpamCheck = true;
@ -66,6 +68,11 @@ namespace Combot.Modules @@ -66,6 +68,11 @@ namespace Combot.Modules
newArg.Copy(arg);
Arguments.Add(newArg);
}
AllowedMessageTypes = new List<MessageType>();
foreach (MessageType messageType in command.AllowedMessageTypes)
{
AllowedMessageTypes.Add(messageType);
}
AllowedAccess = new List<AccessType>();
foreach (AccessType accessType in command.AllowedAccess)
{

14
Combot/Modules/CommandArgument.cs

@ -6,6 +6,8 @@ namespace Combot.Modules @@ -6,6 +6,8 @@ namespace Combot.Modules
{
public string Name { get; set; }
public string Description { get; set; }
public List<string> AllowedValues { get; set; }
public List<MessageType> MessageTypes { get; set; }
public bool Required { get; set; }
public CommandArgument()
@ -17,6 +19,8 @@ namespace Combot.Modules @@ -17,6 +19,8 @@ namespace Combot.Modules
{
Name = string.Empty;
Description = string.Empty;
AllowedValues = new List<string>();
MessageTypes = new List<MessageType>();
Required = false;
}
@ -24,6 +28,16 @@ namespace Combot.Modules @@ -24,6 +28,16 @@ namespace Combot.Modules
{
Name = argument.Name;
Description = argument.Description;
AllowedValues = new List<string>();
foreach (string value in argument.AllowedValues)
{
AllowedValues.Add(value);
}
MessageTypes = new List<MessageType>();
foreach (MessageType value in argument.MessageTypes)
{
MessageTypes.Add(value);
}
Required = argument.Required;
}
}

13
Combot/Modules/CommandMessage.cs

@ -7,27 +7,22 @@ namespace Combot.Modules @@ -7,27 +7,22 @@ namespace Combot.Modules
public class CommandMessage
{
public string Location { get; set; }
public LocationType LocationType { get; set; }
public MessageType MessageType { get; set; }
public Nick Nick { get; set; }
public DateTime TimeStamp { get; set; }
public string Command { get; set; }
public Dictionary<string, dynamic> Arguments { get; set; }
public List<AccessType> Access { get; set; }
public CommandMessage()
{
Location = string.Empty;
LocationType = LocationType.Channel;
MessageType = MessageType.Channel;
Nick = new Nick();
TimeStamp = DateTime.Now;
Command = string.Empty;
Arguments = new Dictionary<string, dynamic>();
Access = new List<AccessType>();
}
}
public enum LocationType
{
Channel,
Query,
Notice
}
}

21
Combot/Modules/Module.cs

@ -49,17 +49,34 @@ namespace Combot.Modules @@ -49,17 +49,34 @@ namespace Combot.Modules
List<AccessType> nickAccessTypes = new List<AccessType>() { AccessType.User };
foreach (PrivilegeMode privilege in command.Nick.Privileges)
{
nickAccessTypes.Add(Bot.AccessTypeMapping[privilege]);
nickAccessTypes.Add(Bot.PrivilegeModeMapping[privilege]);
}
if (Bot.ServerConfig.Owners.Contains(command.Nick.Nickname) && command.Nick.Identified)
if (Bot.ServerConfig.Owners.Contains(command.Nick.Nickname) && command.Nick.Modes.Contains(UserMode.r))
{
nickAccessTypes.Add(AccessType.Owner);
}
command.Access.AddRange(nickAccessTypes);
// If they have the correct access for the command, send it
if (cmd.AllowedAccess.Exists(access => nickAccessTypes.Contains(access)))
{
ParseCommand(command);
}
else
{
string noAccessMessage = string.Format("You do not have access to use \u0002{0}\u000F.", command.Command);
switch (command.MessageType)
{
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(command.Location, noAccessMessage);
break;
case MessageType.Query:
Bot.IRC.SendPrivateMessage(command.Nick.Nickname, noAccessMessage);
break;
case MessageType.Notice:
Bot.IRC.SendNotice(command.Nick.Nickname, noAccessMessage);
break;
}
}
}
}

151
Combot/Modules/ModuleClasses/Help.cs

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
namespace Combot.Modules.ModuleClasses
{
public class Help : Module
{
public override void Initialize()
{
Bot.CommandReceivedEvent += HandleCommandEvent;
}
public override void ParseCommand(CommandMessage command)
{
Command foundCommand = Commands.Find(c => c.Triggers.Contains(command.Command));
if (foundCommand.Name == "Help")
{
if (command.Arguments.Count == 0)
{
SendFullHelp(command.Nick.Nickname, command.Access);
}
else if (command.Arguments.ContainsKey("Command"))
{
SendCommandHelp(command.Nick.Nickname, command.Access, command.Arguments["Command"].ToString());
}
}
}
private void SendFullHelp(string recipient, List<AccessType> access)
{
Bot.IRC.SendNotice(recipient, string.Format("You have the following commands available to use. " +
"To use them either type \u0002{1}\u001Fcommand\u000F into a channel, send a private message by typing \u0002/msg {0} \u001Fcommand\u000F, or send a notice by typing \u0002/notice {0} \u001Fcommand\u000F. " +
"For more information on a specific command, type \u0002{1}help \u001Fcommand\u000F.",
Bot.IRC.Nickname, Bot.ServerConfig.CommandPrefix));
Bot.IRC.SendNotice(recipient, "\u200B");
foreach (Module module in Bot.Modules)
{
if (module.Commands.Exists(command => command.AllowedAccess.Exists(allowed => access.Contains(allowed)) && command.ShowHelp))
{
Bot.IRC.SendNotice(recipient, string.Format("\u0002\u001F{0} Module\u000F\u0002\u000F", module.Name));
}
module.Commands.ForEach(command =>
{
if (command.AllowedAccess.Exists(allowed => access.Contains(allowed)) && command.ShowHelp)
{
string commandDesc = string.Empty;
if (command.Description != string.Empty)
{
commandDesc = string.Format(" - {0}", command.Description);
}
Bot.IRC.SendNotice(recipient, string.Format("\t\t\u0002{0}\u000F{1}", command.Name, commandDesc));
}
});
}
}
private void SendCommandHelp(string recipient, List<AccessType> access, string command)
{
Module foundModule = Bot.Modules.Find(mod => mod.Commands.Exists(cmd => (cmd.Name == command || cmd.Triggers.Contains(command)) && cmd.ShowHelp));
if (foundModule != null)
{
Command foundCommand = foundModule.Commands.Find(cmd => (cmd.Name == command || cmd.Triggers.Contains(command)));
if (foundCommand != null)
{
if (foundCommand.AllowedAccess.Exists(allowed => access.Contains(allowed)))
{
Bot.IRC.SendNotice(recipient, string.Format("Help information for \u0002{0}\u000F", foundCommand.Name));
if (foundCommand.Description != string.Empty)
{
Bot.IRC.SendNotice(recipient, string.Format("{0}", foundCommand.Description));
}
Bot.IRC.SendNotice(recipient, "\u200B");
for (int i = 0; i < foundCommand.AllowedMessageTypes.Count; i++)
{
MessageType messageType = foundCommand.AllowedMessageTypes[i];
string messageSyntax = string.Empty;
switch (messageType)
{
case MessageType.Channel:
messageSyntax = "\u0002/msg \u001Fchannel\u000F";
break;
case MessageType.Query:
messageSyntax = string.Format("\u0002/msg {0}\u000F", Bot.IRC.Nickname);
break;
case MessageType.Notice:
messageSyntax = string.Format("\u0002/notice {0}\u000F", Bot.IRC.Nickname);
break;
}
List<CommandArgument> validArguments = foundCommand.Arguments.FindAll(arg => arg.MessageTypes.Contains(messageType));
string argHelp = string.Empty;
Bot.IRC.SendNotice(recipient, string.Format("Message Type: \u0002{0}\u000F", messageType.ToString()));
if (validArguments.Count > 0)
{
argHelp = string.Format(" \u0002\u001F{0}\u000F", string.Join("\u000F \u0002", validArguments.Select(arg =>
{
if (arg.Required)
{
return "\u001F" + arg.Name + "\u000F\u0002";
}
return "[\u001F" + arg.Name + "\u000F\u0002]";
})));
}
foundCommand.Triggers.ForEach(trigger =>
{
Bot.IRC.SendNotice(recipient, string.Format("\t\tSyntax: {0} {1}\u0002{2}\u000F{3}", messageSyntax, Bot.ServerConfig.CommandPrefix, trigger, argHelp));
});
if (validArguments.Count > 0)
{
Bot.IRC.SendNotice(recipient, "\u200B");
validArguments.ForEach(arg =>
{
string commandDesc = string.Empty;
if (arg.Description != string.Empty)
{
commandDesc = string.Format(" - {0}", arg.Description);
}
string required = string.Empty;
if (arg.Required)
{
required = " - Required";
}
Bot.IRC.SendNotice(recipient, string.Format("\t\t\u0002{0}{1}\u000F{2}", arg.Name, required, commandDesc));
if (arg.AllowedValues.Count > 0)
{
Bot.IRC.SendNotice(recipient, string.Format("\t\t\t\t- Allowed Values: \u0002{0}\u000F", string.Join(", ", arg.AllowedValues)));
}
});
}
Bot.IRC.SendNotice(recipient, "\u200B");
}
}
else
{
Bot.IRC.SendNotice(recipient, string.Format("You do not have access to view help on \u0002{0}\u000F.", command));
}
}
else
{
Bot.IRC.SendNotice(recipient, string.Format("The command \u0002{0}\u000F does not exist.", command));
}
}
else
{
Bot.IRC.SendNotice(recipient, string.Format("The command \u0002{0}\u000F does not exist.", command));
}
}
}
}

236
Combot/Modules/ModuleClasses/Moderation.cs

@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Combot.IRCServices;
using Combot.IRCServices.Messaging;
namespace Combot.Modules.ModuleClasses
{
public class Moderation : Module
{
public override void Initialize()
{
Bot.CommandReceivedEvent += HandleCommandEvent;
}
public override void ParseCommand(CommandMessage command)
{
Command foundCommand = Commands.Find(c => c.Triggers.Contains(command.Command));
switch (foundCommand.Name)
{
// Privilege Mode Commands
case "Founder":
ModifyUserPrivilege(true, command, ChannelMode.q);
break;
case "Remove Founder":
ModifyUserPrivilege(false, command, ChannelMode.q);
break;
case "SOP":
ModifyUserPrivilege(true, command, ChannelMode.a);
break;
case "Remove SOP":
ModifyUserPrivilege(false, command, ChannelMode.a);
break;
case "OP":
ModifyUserPrivilege(true, command, ChannelMode.o);
break;
case "Remove OP":
ModifyUserPrivilege(false, command, ChannelMode.o);
break;
case "HOP":
ModifyUserPrivilege(true, command, ChannelMode.h);
break;
case "Remove HOP":
ModifyUserPrivilege(false, command, ChannelMode.h);
break;
case "Voice":
ModifyUserPrivilege(true, command, ChannelMode.v);
break;
case "Remove Voice":
ModifyUserPrivilege(false, command, ChannelMode.v);
break;
// Auto Privilege Management
case "ASOP":
ModifyAutoUserPrivilege("SOP", command, ChannelMode.a);
break;
case "AOP":
ModifyAutoUserPrivilege("AOP", command, ChannelMode.o);
break;
case "AHOP":
ModifyAutoUserPrivilege("HOP", command, ChannelMode.h);
break;
case "AVoice":
ModifyAutoUserPrivilege("VOP", command, ChannelMode.v);
break;
case "Mode":
ModifyChannelModes(command);
break;
case "Topic":
ModifyChannelTopic(command);
break;
case "Invite":
break;
case "Ban":
break;
case "UnBan":
break;
case "Clear Ban":
break;
case "Kick Ban":
break;
case "Timed Ban":
break;
case "Timed Kick Ban":
break;
case "Kick":
break;
case "Kick Me":
break;
}
}
private void ModifyUserPrivilege(bool set, CommandMessage command, ChannelMode mode)
{
string channel = command.Arguments.ContainsKey("Channel") ? command.Arguments["Channel"] : command.Location;
if (Bot.CheckChannelAccess(channel, command.Nick.Nickname, Bot.ChannelModeMapping[mode]))
{
SetMode(set, channel, mode, command.Arguments["Nickname"]);
}
else
{
string noAccessMessage = string.Format("You do not have access to set mode \u0002+{0}\u000F for \u0002{1}\u000F on \u0002{2}\u000F.", mode, command.Arguments["Nickname"], channel);
switch (command.MessageType)
{
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(command.Location, noAccessMessage);
break;
case MessageType.Query:
Bot.IRC.SendPrivateMessage(command.Nick.Nickname, noAccessMessage);
break;
case MessageType.Notice:
Bot.IRC.SendNotice(command.Nick.Nickname, noAccessMessage);
break;
}
}
}
private void ModifyAutoUserPrivilege(string optionCommand, CommandMessage command, ChannelMode mode)
{
bool set = true;
if (command.Arguments["Option"].ToLower() == "del")
{
set = false;
}
string channel = command.Arguments.ContainsKey("Channel") ? command.Arguments["Channel"] : command.Location;
if (Bot.CheckChannelAccess(channel, command.Nick.Nickname, Bot.ChannelModeMapping[mode]))
{
SetMode(set, channel, mode, command.Arguments["Nickname"]);
Bot.IRC.SendPrivateMessage("ChanServ", string.Format("{0} {1} {2} {3}", optionCommand, channel, command.Arguments["Option"], command.Arguments["Nickname"]));
}
else
{
string noAccessMessage = string.Format("You do not have access to \u0002{0}\u000F \u0002{1}\u000F to the \u0002{2}\u000F list on \u0002{3}\u000F.", command.Arguments["Option"].ToLower(), command.Arguments["Nickname"], optionCommand, channel);
switch (command.MessageType)
{
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(command.Location, noAccessMessage);
break;
case MessageType.Query:
Bot.IRC.SendPrivateMessage(command.Nick.Nickname, noAccessMessage);
break;
case MessageType.Notice:
Bot.IRC.SendNotice(command.Nick.Nickname, noAccessMessage);
break;
}
}
}
private void ModifyChannelModes(CommandMessage command)
{
List<ChannelModeInfo> modeList = new List<ChannelModeInfo>();
if (command.Arguments.ContainsKey("Parameters"))
{
modeList = Bot.IRC.ParseChannelModeString(command.Arguments["Modes"], command.Arguments["Parameters"]);
}
else
{
modeList = Bot.IRC.ParseChannelModeString(command.Arguments["Modes"], string.Empty);
}
string channel = command.Arguments.ContainsKey("Channel") ? command.Arguments["Channel"] : command.Location;
bool allowedMode = true;
ChannelMode mode = ChannelMode.q;
for (int i = 0; i < modeList.Count; i++)
{
switch (modeList[i].Mode)
{
case ChannelMode.v:
case ChannelMode.h:
case ChannelMode.o:
case ChannelMode.a:
case ChannelMode.q:
allowedMode = Bot.CheckChannelAccess(channel, command.Nick.Nickname, Bot.ChannelModeMapping[modeList[i].Mode]);
if (!allowedMode)
{
mode = modeList[i].Mode;
}
break;
}
}
if (allowedMode)
{
Bot.IRC.SendMode(channel, modeList);
}
else
{
string noAccessMessage = string.Format("You do not have access to set mode \u0002+{0}\u000F on \u0002{1}\u000F.", mode, channel);
switch (command.MessageType)
{
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(command.Location, noAccessMessage);
break;
case MessageType.Query:
Bot.IRC.SendPrivateMessage(command.Nick.Nickname, noAccessMessage);
break;
case MessageType.Notice:
Bot.IRC.SendNotice(command.Nick.Nickname, noAccessMessage);
break;
}
}
}
private void SetMode(bool set, string channel, ChannelMode mode, string nickname)
{
ChannelModeInfo modeInfo = new ChannelModeInfo();
modeInfo.Mode = mode;
modeInfo.Parameter = nickname;
modeInfo.Set = set;
Bot.IRC.SendMode(channel, modeInfo);
}
private void ModifyChannelTopic(CommandMessage command)
{
string channel = command.Arguments.ContainsKey("Channel") ? command.Arguments["Channel"] : command.Location;
if (Bot.CheckChannelAccess(channel, command.Nick.Nickname, command.Access))
{
Bot.IRC.SendTopic(channel, command.Arguments["Message"]);
}
else
{
string noAccessMessage = string.Format("You do not have access to change the topic on \u0002{0}\u000F.", channel);
switch (command.MessageType)
{
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(command.Location, noAccessMessage);
break;
case MessageType.Query:
Bot.IRC.SendPrivateMessage(command.Nick.Nickname, noAccessMessage);
break;
case MessageType.Notice:
Bot.IRC.SendNotice(command.Nick.Nickname, noAccessMessage);
break;
}
}
}
}
}

18
Combot/Modules/ModuleClasses/PingMe.cs

@ -22,13 +22,15 @@ namespace Combot.Modules.ModuleClasses @@ -22,13 +22,15 @@ namespace Combot.Modules.ModuleClasses
public override void ParseCommand(CommandMessage command)
{
if (Commands.Find(cmd => cmd.Name == "Ping Me").Triggers.Contains(command.Command))
Command foundCommand = Commands.Find(c => c.Triggers.Contains(command.Command));
if (foundCommand.Name == "Ping Me")
{
int epoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
PingItem tmpItem = new PingItem();
tmpItem.Nick = command.Nick.Nickname;
tmpItem.Location = command.Location;
tmpItem.LocationType = command.LocationType;
tmpItem.MessageType = command.MessageType;
tmpItem.Timestamp = DateTime.Now;
listLock.EnterWriteLock();
if (pingList.Exists(item => item.Nick == command.Nick.Nickname))
@ -74,15 +76,15 @@ namespace Combot.Modules.ModuleClasses @@ -74,15 +76,15 @@ namespace Combot.Modules.ModuleClasses
{
timeString += difTime.Milliseconds.ToString() + " Milliseconds";
}
switch (pingItem.LocationType)
switch (pingItem.MessageType)
{
case LocationType.Channel:
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(pingItem.Location, string.Format("{0}, your ping is {1}", pingItem.Nick, timeString));
break;
case LocationType.Notice:
case MessageType.Notice:
Bot.IRC.SendNotice(pingItem.Nick, string.Format("Your ping is {0}", timeString));
break;
case LocationType.Query:
case MessageType.Query:
Bot.IRC.SendPrivateMessage(pingItem.Nick, string.Format("Your ping is {0}", timeString));
break;
}
@ -97,14 +99,14 @@ namespace Combot.Modules.ModuleClasses @@ -97,14 +99,14 @@ namespace Combot.Modules.ModuleClasses
{
public string Nick { get; set; }
public string Location { get; set; }
public LocationType LocationType { get; set; }
public MessageType MessageType { get; set; }
public DateTime Timestamp { get; set; }
public PingItem()
{
Nick = string.Empty;
Location = string.Empty;
LocationType = LocationType.Channel;
MessageType = MessageType.Channel;
Timestamp = DateTime.Now;
}
}

42
Combot/Modules/ModuleClasses/Version.cs

@ -22,11 +22,13 @@ namespace Combot.Modules.ModuleClasses @@ -22,11 +22,13 @@ namespace Combot.Modules.ModuleClasses
public override void ParseCommand(CommandMessage command)
{
if (Commands.Find(cmd => cmd.Name == "Version Check").Triggers.Contains(command.Command))
Command foundCommand = Commands.Find(c => c.Triggers.Contains(command.Command));
if (foundCommand.Name == "Version Check")
{
VersionItem tmpItem = new VersionItem();
tmpItem.Location = command.Location;
tmpItem.LocationType = command.LocationType;
tmpItem.MessageType = command.MessageType;
tmpItem.Nick = command.Arguments["Nickname"];
listLock.EnterWriteLock();
if (versionList.Exists(item => item.Nick == command.Arguments["Nickname"]))
@ -52,20 +54,20 @@ namespace Combot.Modules.ModuleClasses @@ -52,20 +54,20 @@ namespace Combot.Modules.ModuleClasses
if (message.Command == "VERSION")
{
listLock.EnterReadLock();
VersionItem versionItem = versionList.Find(item => item.Nick == message.Sender.Nickname);
VersionItem versionItem = versionList.Find(item => item.Nick.ToLower() == message.Sender.Nickname.ToLower());
listLock.ExitReadLock();
if (versionItem != null)
{
switch (versionItem.LocationType)
switch (versionItem.MessageType)
{
case LocationType.Channel:
case MessageType.Channel:
Bot.IRC.SendPrivateMessage(versionItem.Location, string.Format("[{0}] Using version: {1}", versionItem.Nick, message.Arguments));
break;
case LocationType.Query:
Bot.IRC.SendPrivateMessage(versionItem.Nick, string.Format("[{0}] Using version: {1}", versionItem.Nick, message.Arguments));
case MessageType.Query:
Bot.IRC.SendPrivateMessage(message.Sender.Nickname, string.Format("[{0}] Using version: {1}", versionItem.Nick, message.Arguments));
break;
case LocationType.Notice:
Bot.IRC.SendNotice(versionItem.Nick, string.Format("[{0}] Using version: {1}", versionItem.Nick, message.Arguments));
case MessageType.Notice:
Bot.IRC.SendNotice(message.Sender.Nickname, string.Format("[{0}] Using version: {1}", versionItem.Nick, message.Arguments));
break;
}
listLock.EnterWriteLock();
@ -74,19 +76,19 @@ namespace Combot.Modules.ModuleClasses @@ -74,19 +76,19 @@ namespace Combot.Modules.ModuleClasses
}
}
}
}
public class VersionItem
{
public string Nick { get; set; }
public string Location { get; set; }
public LocationType LocationType { get; set; }
public VersionItem()
private class VersionItem
{
Nick = string.Empty;
Location = string.Empty;
LocationType = LocationType.Channel;
public string Nick { get; set; }
public string Location { get; set; }
public MessageType MessageType { get; set; }
public VersionItem()
{
Nick = string.Empty;
Location = string.Empty;
MessageType = MessageType.Channel;
}
}
}
}

9
Combot/Types.cs

@ -17,15 +17,6 @@ namespace Combot @@ -17,15 +17,6 @@ namespace Combot
Framework = 3
}
public enum MessageType
{
Service = 0,
Channel = 1,
Query = 2,
Notice = 3,
CTCP = 4
}
public class BotError
{
public ErrorType Type { get; set; }

4
IRCServices/Channel.cs

@ -11,8 +11,6 @@ namespace Combot.IRCServices @@ -11,8 +11,6 @@ namespace Combot.IRCServices
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<string> Bans { get; set; }
public List<ChannelMode> Modes { get; set; }
@ -23,8 +21,6 @@ namespace Combot.IRCServices @@ -23,8 +21,6 @@ namespace Combot.IRCServices
Name = string.Empty;
Topic = string.Empty;
Key = string.Empty;
AutoJoin = false;
Joined = false;
Registration = DateTime.Now;
Bans = new List<string>();
Modes = new List<ChannelMode>();

107
IRCServices/IRC.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -17,20 +18,30 @@ namespace Combot.IRCServices @@ -17,20 +18,30 @@ namespace Combot.IRCServices
public event Action ConnectEvent;
public event Action DisconnectEvent;
public event Action<TCPError> TCPErrorEvent;
public string Nickname { get; set; }
public string Nickname;
public Dictionary<string, PrivilegeMode> PrivilegeMapping = new Dictionary<string, PrivilegeMode>() { { "+", PrivilegeMode.v }, { "%", PrivilegeMode.h }, { "@", PrivilegeMode.o }, { "&", PrivilegeMode.a }, { "~", PrivilegeMode.q } };
private int MaxMessageLength;
private int MessageSendDelay;
private DateTime LastMessageSend;
private int ReadTimeout;
private int AllowedFailedReads;
private Thread TCPReader;
private event Action<string> TCPMessageEvent;
private readonly TCPInterface _TCP;
private readonly ReaderWriterLockSlim ChannelRWLock;
public IRC()
public IRC(int maxMessageLength, int messageSendDelay = 0, int readTimeout = 5000, int allowedFailedReads = 0)
{
_TCP = new TCPInterface();
Message = new Messages(this);
Nickname = string.Empty;
ChannelRWLock = new ReaderWriterLockSlim();
LastMessageSend = DateTime.Now;
MaxMessageLength = maxMessageLength;
MessageSendDelay = messageSendDelay;
ReadTimeout = readTimeout;
AllowedFailedReads = allowedFailedReads;
TCPMessageEvent += Message.ParseTCPMessage;
_TCP.TCPConnectionEvent += HandleTCPConnection;
@ -55,12 +66,12 @@ namespace Combot.IRCServices @@ -55,12 +66,12 @@ namespace Combot.IRCServices
/// <param name="readTimeout">The timeout for read operations in milliseconds.</param>
/// <param name="allowedFailedCount">Number of times a read can fail before disconnecting.</param>
/// <returns></returns>
public bool Connect(IPAddress IP, int port, int readTimeout = 5000, int allowedFailedCount = 0)
public bool Connect(IPAddress IP, int port)
{
bool result = false;
if (!_TCP.Connected)
{
result = _TCP.Connect(IP, port, readTimeout, allowedFailedCount);
result = _TCP.Connect(IP, port, ReadTimeout, AllowedFailedReads);
if (result)
{
TCPReader = new Thread(ReadTCPMessages);
@ -114,6 +125,71 @@ namespace Combot.IRCServices @@ -114,6 +125,71 @@ namespace Combot.IRCServices
SendUser(nick.Username, nick.Host, serverName, nick.Realname);
}
/// <summary>
/// Parses a given mode and parameter string to generate a channel mode list.
/// </summary>
/// <param name="modeString">The mode string that contains the mode info.</param>
/// <param name="parameterString">The parameter string that is associated with the mode info.</param>
/// <returns></returns>
public List<ChannelModeInfo> ParseChannelModeString(string modeString, string parameterString)
{
string[] modeArgs = parameterString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
char[] modeInfo = modeString.ToCharArray();
bool set = true;
int argIndex = 0;
List<ChannelModeInfo> modeInfos = new List<ChannelModeInfo>();
foreach (char mode in modeInfo)
{
if (mode.Equals('-'))
{
set = false;
}
else if (mode.Equals('+'))
{
set = true;
}
else
{
ChannelModeInfo newMode = new ChannelModeInfo();
newMode.Set = set;
ChannelMode foundMode;
bool validMode = Enum.TryParse(mode.ToString(), false, out foundMode);
if (validMode)
{
newMode.Mode = foundMode;
if (modeArgs.GetUpperBound(0) >= argIndex)
{
switch (newMode.Mode)
{
case ChannelMode.k:
case ChannelMode.l:
case ChannelMode.b:
case ChannelMode.e:
case ChannelMode.I:
case ChannelMode.v:
case ChannelMode.h:
case ChannelMode.o:
case ChannelMode.a:
case ChannelMode.q:
newMode.Parameter = modeArgs[argIndex];
argIndex++;
break;
default:
newMode.Parameter = string.Empty;
break;
}
}
else
{
newMode.Parameter = string.Empty;
}
modeInfos.Add(newMode);
}
}
}
return modeInfos;
}
private void ReadTCPMessages()
{
while (_TCP.Connected)
@ -324,6 +400,7 @@ namespace Combot.IRCServices @@ -324,6 +400,7 @@ namespace Combot.IRCServices
break;
}
}
SendWho(channel.Name);
}
ChannelRWLock.ExitWriteLock();
}
@ -393,10 +470,6 @@ namespace Combot.IRCServices @@ -393,10 +470,6 @@ namespace Combot.IRCServices
{
Channel newChannel = new Channel();
newChannel.Name = e.Channel;
if (e.Nick.Nickname == Nickname)
{
newChannel.Joined = true;
}
newChannel.Nicks.Add(e.Nick);
Channels.Add(newChannel);
SendWho(newChannel.Name);
@ -415,7 +488,14 @@ namespace Combot.IRCServices @@ -415,7 +488,14 @@ namespace Combot.IRCServices
Channel channel = Channels.Find(chan => chan.Name == e.Channel);