Kaynağa Gözat

Added exception handling to many aspects and logging/displaying of exceptions.

master
Teknikode 3 yıl önce
ebeveyn
işleme
eebe09d84b

+ 491
- 381
Combot/Bot.cs
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 3
- 0
Combot/Combot.csproj Dosyayı Görüntüle

@@ -66,8 +66,10 @@
<Compile Include="AccessType.cs" />
<Compile Include="Bot.cs" />
<Compile Include="Configurations\DatabaseConfig.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Controller.cs" />
<Compile Include="Databases\Database.cs" />
<Compile Include="Logger.cs" />
<Compile Include="MessageType.cs" />
<Compile Include="Modules\Command.cs" />
<Compile Include="Configurations\ChannelConfig.cs" />
@@ -83,6 +85,7 @@
<Compile Include="Types.cs" />
<Compile Include="Modules\Module.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utility.cs" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />

+ 4
- 0
Combot/Configurations/ServerConfig.cs Dosyayı Görüntüle

@@ -30,6 +30,8 @@ namespace Combot.Configurations
public SpamSourceType SpamSourceType { get; set; }
public int SpamCountMax { get; set; }
public TimeSpan SpamSessionTime { get; set; }
public string LogFilePath { get; set; }
public int LogFileSizeMax { get; set; }

public ServerConfig()
{
@@ -53,6 +55,8 @@ namespace Combot.Configurations
SpamSourceType = SpamSourceType.Nick;
SpamCountMax = 5;
SpamSessionTime = new TimeSpan(0, 0, 1);
LogFilePath = Utility.GetAssemblyDirectory();
LogFileSizeMax = 10000;
ModuleLocation = string.Empty;
Owners = new List<string>();
ChannelBlacklist = new List<string>();

+ 13
- 0
Combot/Constants.cs Dosyayı Görüntüle

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

namespace Combot
{
public static class Constants
{
public const string LOGFILEEXT = ".log";
}
}

+ 48
- 0
Combot/Logger.cs Dosyayı Görüntüle

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

namespace Combot
{
public static class Logger
{
public static void LogToFile(string directory, string fileName, string message, int maxSize)
{
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);

string logFile = Path.Combine(directory, fileName + Constants.LOGFILEEXT);
// Check to see if we need to create a new log
if (File.Exists(logFile))
{
TrimLogFile(directory, fileName, maxSize);
}
// Write the log to the main log file
StreamWriter logWriter = File.AppendText(logFile);
logWriter.WriteLine(string.Format("[{0}] {1}", DateTime.Now.ToString("G"), message));
logWriter.Close();
}

private static void TrimLogFile(string logDir, string fileName, int maxSize)
{
string logFile = Path.Combine(logDir, fileName);
FileInfo file = new FileInfo(logFile);
long fileSize = file.Length;
if (fileSize > maxSize)
{
// The file is too large, we need to increment the file names of the log files
string[] files = Directory.GetFiles(logDir);
for (int i = files.GetUpperBound(0) - 1; i >= 0; i--)
{
string newFileName = fileName + "_" + (i + 1) + Constants.LOGFILEEXT;
string newFile = Path.Combine(logDir, newFileName);
File.Move(files[i], newFile);
}
}
}
}
}

+ 78
- 49
Combot/Modules/Module.cs Dosyayı Görüntüle

@@ -25,6 +25,8 @@ namespace Combot.Modules

public event EventHandler<string> ModuleErrorEvent;

public event Action<Exception> ExceptionThrown;

public bool Loaded { get; set; }
public bool ShouldSerializeLoaded()
{
@@ -56,45 +58,52 @@ namespace Combot.Modules

public void HandleCommandEvent(CommandMessage command)
{
// Check to make sure the command exists, the nick or channel isn't on a blacklist, and the module is loaded.
if (Loaded
&& Enabled
&& !ChannelBlacklist.Contains(command.Location)
&& !NickBlacklist.Contains(command.Nick.Nickname)
&& Commands.Exists(c => c.Triggers.Contains(command.Command)
&& c.Enabled
&& !c.ChannelBlacklist.Contains(command.Location)
&& !c.NickBlacklist.Contains(command.Nick.Nickname)
)
)
try
{
Bot.Log("Checking Command " + command.Command);
// Figure out access of the nick
Command cmd = Commands.Find(c => c.Triggers.Contains(command.Command));
List<AccessType> nickAccessTypes = new List<AccessType>() { AccessType.User };
foreach (PrivilegeMode privilege in command.Nick.Privileges)
{
nickAccessTypes.Add(Bot.PrivilegeModeMapping[privilege]);
}
if ((Bot.ServerConfig.Owners.Contains(command.Nick.Nickname) && command.Nick.Modes.Exists(mode => mode == UserMode.r || mode == UserMode.o)) || command.Nick.Nickname == Bot.IRC.Nickname)
{
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)))
// Check to make sure the command exists, the nick or channel isn't on a blacklist, and the module is loaded.
if (Loaded
&& Enabled
&& !ChannelBlacklist.Contains(command.Location)
&& !NickBlacklist.Contains(command.Nick.Nickname)
&& Commands.Exists(c => c.Triggers.Contains(command.Command)
&& c.Enabled
&& !c.ChannelBlacklist.Contains(command.Location)
&& !c.NickBlacklist.Contains(command.Nick.Nickname)
)
)
{
// Make sure that the command isn't being spammed
if (Bot.SpamCheck(Bot.IRC.Channels.Find(chan => chan.Name == command.Location), command.Nick, this, cmd))
Bot.Log("Checking Command " + command.Command);
// Figure out access of the nick
Command cmd = Commands.Find(c => c.Triggers.Contains(command.Command));
List<AccessType> nickAccessTypes = new List<AccessType>() { AccessType.User };
foreach (PrivilegeMode privilege in command.Nick.Privileges)
{
ParseCommand(command);
nickAccessTypes.Add(Bot.PrivilegeModeMapping[privilege]);
}
if ((Bot.ServerConfig.Owners.Contains(command.Nick.Nickname) && command.Nick.Modes.Exists(mode => mode == UserMode.r || mode == UserMode.o)) || command.Nick.Nickname == Bot.IRC.Nickname)
{
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)))
{
// Make sure that the command isn't being spammed
if (Bot.SpamCheck(Bot.IRC.Channels.Find(chan => chan.Name == command.Location), command.Nick, this, cmd))
{
ParseCommand(command);
}
}
else
{
string noAccessMessage = string.Format("You do not have access to use \u0002{0}\u000F.", command.Command);
SendResponse(command.MessageType, command.Location, command.Nick.Nickname, noAccessMessage, true);
}
}
else
{
string noAccessMessage = string.Format("You do not have access to use \u0002{0}\u000F.", command.Command);
SendResponse(command.MessageType, command.Location, command.Nick.Nickname, noAccessMessage, true);
}
}
catch (Exception ex)
{
ThrowException(ex, "Unable to handle module command.");
}
}

@@ -112,6 +121,20 @@ namespace Combot.Modules
}
}

protected void ThrowException(Exception ex)
{
ThrowException(ex, string.Format("Module {0} threw exception.", Name));
}

protected void ThrowException(Exception ex, string message)
{
Exception newEx = new Exception(message, ex);
if (ExceptionThrown != null)
{
ExceptionThrown(newEx);
}
}

public void SetDefaults()
{
Name = string.Empty;
@@ -159,25 +182,31 @@ namespace Combot.Modules
public Module CreateInstance(Bot bot)
{
Module newModule = new Module();
if (!Loaded)
try
{
//create the class base on string
//note : include the namespace and class name (namespace=Combot.Modules, class name=<class_name>)
Assembly a = Assembly.LoadFrom(Path.Combine(ConfigPath, string.Format("{0}.dll", Name)));
Type t = a.GetType("Combot.Modules.Plugins." + ClassName);

//check to see if the class is instantiated or not
if (t != null)
if (!Loaded)
{
newModule = (Module)Activator.CreateInstance(t);
newModule.Copy(this);
newModule.Loaded = true;
newModule.ConfigPath = ConfigPath;
newModule.Bot = bot;
newModule.Initialize();
//create the class base on string
//note : include the namespace and class name (namespace=Combot.Modules, class name=<class_name>)
Assembly a = Assembly.LoadFrom(Path.Combine(ConfigPath, string.Format("{0}.dll", Name)));
Type t = a.GetType("Combot.Modules.Plugins." + ClassName);

//check to see if the class is instantiated or not
if (t != null)
{
newModule = (Module)Activator.CreateInstance(t);
newModule.Copy(this);
newModule.Loaded = true;
newModule.ConfigPath = ConfigPath;
newModule.Bot = bot;
newModule.Initialize();
}
}
}

catch (Exception ex)
{
ThrowException(ex, "Unable to create module instance for " + Name + ".");
}
return newModule;
}


+ 21
- 0
Combot/Utility.cs Dosyayı Görüntüle

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

namespace Combot
{
public static class Utility
{
public static string GetAssemblyDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
}

+ 148
- 99
IRCServices/IRC.cs Dosyayı Görüntüle

@@ -22,6 +22,7 @@ namespace Combot.IRCServices
public event Action ConnectEvent;
public event Action DisconnectEvent;
public event Action<TCPError> TCPErrorEvent;
public event Action<Exception> ExceptionThrown;
public string Nickname;
public Dictionary<string, PrivilegeMode> PrivilegeMapping = new Dictionary<string, PrivilegeMode>() { { "+", PrivilegeMode.v }, { "%", PrivilegeMode.h }, { "@", PrivilegeMode.o }, { "&", PrivilegeMode.a }, { "~", PrivilegeMode.q }, { "!", PrivilegeMode.q } };

@@ -76,26 +77,32 @@ namespace Combot.IRCServices
public bool Connect(IPAddress IP, int port)
{
bool result = false;
if (!_TCP.Connected)
try
{
result = _TCP.Connect(IP, port, ReadTimeout, AllowedFailedReads);
if (result)
if (!_TCP.Connected)
{
TCPReader = new Thread(ReadTCPMessages);
TCPReader.IsBackground = true;
TCPReader.Start();
result = _TCP.Connect(IP, port, ReadTimeout, AllowedFailedReads);
if (result)
{
TCPReader = new Thread(ReadTCPMessages);
TCPReader.IsBackground = true;
TCPReader.Start();

KeepAlive = new Thread(() => CheckConnection(IP, port));
KeepAlive.IsBackground = true;
KeepAlive.Start();
KeepAlive = new Thread(() => CheckConnection(IP, port));
KeepAlive.IsBackground = true;
KeepAlive.Start();

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

catch(Exception ex)
{
ThrowException(ex);
}
return result;
}

@@ -105,28 +112,35 @@ namespace Combot.IRCServices
/// <returns></returns>
public void Disconnect()
{
if (_TCP.Connected)
try
{
_TCP.Disconnect();
}
if (_TCP.Connected)
{
_TCP.Disconnect();
}

if (KeepAlive.IsAlive)
{
KeepAlive.Join();
}
if (KeepAlive.IsAlive)
{
KeepAlive.Join();
}

if (TCPReader.IsAlive)
{
TCPReader.Join();
}
if (TCPReader.IsAlive)
{
TCPReader.Join();
}

ChannelRWLock.EnterWriteLock();
Channels = new List<Channel>();
ChannelRWLock.ExitWriteLock();
ChannelRWLock.EnterWriteLock();
Channels = new List<Channel>();
ChannelRWLock.ExitWriteLock();

if (DisconnectEvent != null)
if (DisconnectEvent != null)
{
DisconnectEvent();
}
}
catch (Exception ex)
{
DisconnectEvent();
ThrowException(ex, "Disconnect Exception");
}
}

@@ -137,9 +151,16 @@ namespace Combot.IRCServices
/// <param name="nick">The nick information for the login.</param>
public void Login(string serverName, Nick nick)
{
Nickname = nick.Nickname;
Command.SendNick(nick.Nickname);
Command.SendUser(nick.Username, nick.Host, serverName, nick.Realname);
try
{
Nickname = nick.Nickname;
Command.SendNick(nick.Nickname);
Command.SendUser(nick.Username, nick.Host, serverName, nick.Realname);
}
catch (Exception ex)
{
ThrowException(ex, "Login Exception.");
}
}

/// <summary>
@@ -150,98 +171,112 @@ namespace Combot.IRCServices
/// <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)
try
{
if (mode.Equals('-'))
{
set = false;
}
else if (mode.Equals('+'))
parameterString = null;
string[] modeArgs = parameterString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
char[] modeInfo = modeString.ToCharArray();
bool set = true;
int argIndex = 0;
foreach (char mode in modeInfo)
{
set = true;
}
else
{
ChannelModeInfo newMode = new ChannelModeInfo();
newMode.Set = set;
ChannelMode foundMode;
bool validMode = Enum.TryParse(mode.ToString(), false, out foundMode);
if (validMode)
if (mode.Equals('-'))
{
set = false;
}
else if (mode.Equals('+'))
{
set = true;
}
else
{
newMode.Mode = foundMode;
if (modeArgs.GetUpperBound(0) >= argIndex)
ChannelModeInfo newMode = new ChannelModeInfo();
newMode.Set = set;
ChannelMode foundMode;
bool validMode = Enum.TryParse(mode.ToString(), false, out foundMode);
if (validMode)
{
switch (newMode.Mode)
newMode.Mode = foundMode;
if (modeArgs.GetUpperBound(0) >= argIndex)
{
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;
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);
}
else
{
newMode.Parameter = string.Empty;
}
modeInfos.Add(newMode);
}
}
}
catch (Exception ex)
{
ThrowException(ex, "Unable to parse Channel Mode.");
}
return modeInfos;
}

public List<UserModeInfo> ParseUserModeString(string modeString)
{
List<UserModeInfo> userModes = new List<UserModeInfo>();

bool set = true;
char[] modeArr = modeString.ToCharArray();
for (int i = 1; i <= modeArr.GetUpperBound(0); i++)
try
{
UserModeInfo newMode = new UserModeInfo();
if (modeArr[i].Equals('-'))
{
set = false;
}
else if (modeArr[i].Equals('+'))
{
set = true;
}
else if (modeArr[i].Equals('*'))
{
newMode.Mode = UserMode.o;
newMode.Set = set;
userModes.Add(newMode);
}
else
bool set = true;
char[] modeArr = modeString.ToCharArray();
for (int i = 1; i <= modeArr.GetUpperBound(0); i++)
{
UserMode foundMode;
bool validMode = Enum.TryParse(modeArr[i].ToString(), false, out foundMode);
if (validMode)
UserModeInfo newMode = new UserModeInfo();
if (modeArr[i].Equals('-'))
{
set = false;
}
else if (modeArr[i].Equals('+'))
{
set = true;
}
else if (modeArr[i].Equals('*'))
{
newMode.Mode = foundMode;
newMode.Mode = UserMode.o;
newMode.Set = set;
userModes.Add(newMode);
}
else
{
UserMode foundMode;
bool validMode = Enum.TryParse(modeArr[i].ToString(), false, out foundMode);
if (validMode)
{
newMode.Mode = foundMode;
newMode.Set = set;
userModes.Add(newMode);
}
}
}
}
catch (Exception ex)
{
ThrowException(ex, "Unable to parse User Mode.");
}
return userModes;
}

@@ -662,5 +697,19 @@ namespace Combot.IRCServices
}
ChannelRWLock.ExitWriteLock();
}

private void ThrowException(Exception ex)
{
ThrowException(ex, "Irc Service threw exception.");
}

private void ThrowException(Exception ex, string message)
{
Exception newEx = new Exception(message, ex);
if (ExceptionThrown != null)
{
ExceptionThrown(newEx);
}
}
}
}

+ 18
- 0
Interface/ViewModels/MainViewModel.cs Dosyayı Görüntüle

@@ -155,6 +155,7 @@ namespace Interface.ViewModels
ServerList.Add(Combot.ServerConfig.Name);

Combot.ErrorEvent += e => BotErrorHandler(e, Combot.ServerConfig.Name);
Combot.ExceptionThrown += e => BotExceptionHandler(e, Combot.ServerConfig.Name);

// Incoming Messages
Combot.IRC.Message.ErrorMessageEvent += (sender, e) => ErrorMessageHandler(sender, e, Combot.ServerConfig.Name);
@@ -241,6 +242,12 @@ namespace Interface.ViewModels
AddToBuffer(server, null, string.Format("[{0}] \u0002{1} Error\u0002: {2}", DateTime.Now.ToString("HH:mm:ss"), error.Type, error.Message));
}

private void BotExceptionHandler(Exception ex, string server)
{
string msg = CreateExceptionMessage(ex);
AddToBuffer(server, null, string.Format("[{0}] \u0002Exception Thrown\u0002: {1}", DateTime.Now.ToString("HH:mm:ss"), msg));
}

private void TCPErrorHandler(Combot.IRCServices.TCP.TCPError error, string server)
{
AddToBuffer(server, null, string.Format("[{0}] \u0002TCP Error {1}\u0002: {2}", DateTime.Now.ToString("HH:mm:ss"), error.Code, error.Message));
@@ -539,5 +546,16 @@ namespace Interface.ViewModels
}
}
}

private string CreateExceptionMessage(Exception ex)
{
string message = ex.Message;
if (ex.InnerException != null)
{
message += " Inner Exception: ";
message += CreateExceptionMessage(ex.InnerException);
}
return message;
}
}
}

Loading…
İptal
Kaydet