From d7b2077ed83649c059bddfdf705d758b728c0499 Mon Sep 17 00:00:00 2001 From: Marc Piulachs Date: Wed, 7 May 2008 14:59:28 +0000 Subject: [PATCH] - code refactoring - made more and more easily extensible: * commands automatically loaded from plugins dlls * declarative and automatic command parameter parsing * common code moved to base classes - other fixes svn path=/trunk/; revision=33344 --- irc/TechBot/Default.build | 14 - .../TechBot.Console/ConsoleTechBotService.cs | 39 + irc/TechBot/TechBot.Console/Main.cs | 98 +-- .../TechBot.Console/TechBot.Console.csproj | 1 + .../Attributes/CommandAttribute.cs | 35 + .../CommandParameterAliasAttribute.cs | 34 + .../Attributes/CommandParameterAttribute.cs | 43 ++ irc/TechBot/TechBot.Library/BugCommand.cs | 50 -- .../Collections/CommandBuilderCollection.cs | 20 + irc/TechBot/TechBot.Library/Command.cs | 53 -- .../{ => Commands}/ApiCommand.cs | 686 +++++++++--------- .../TechBot.Library/Commands/Base/Command.cs | 51 ++ .../Commands/Base/XmlCommand.cs | 3 +- .../Commands/Base/XmlLookupCommand.cs | 18 + .../TechBot.Library/Commands/BugCommand.cs | 41 ++ .../{ => Commands}/ErrorCommand.cs | 391 +++++----- .../TechBot.Library/Commands/HelpCommand.cs | 51 ++ .../{ => Commands}/HresultCommand.cs | 159 ++-- .../{ => Commands}/NtStatusCommand.cs | 157 ++-- .../{ => Commands}/ReactOSBugUrl.cs | 14 +- .../TechBot.Library/Commands/SambaBugUrl.cs | 19 + .../TechBot.Library/Commands/SvnCommand.cs | 20 + .../TechBot.Library/Commands/WineBugUrl.cs | 19 + .../{ => Commands}/WinerrorCommand.cs | 151 ++-- .../{ => Commands}/WmCommand.cs | 196 +++-- .../TechBot.Library/Factory/CommandBuilder.cs | 52 ++ .../TechBot.Library/Factory/CommandFactory.cs | 54 ++ irc/TechBot/TechBot.Library/HelpCommand.cs | 37 - irc/TechBot/TechBot.Library/MessageContext.cs | 34 +- .../TechBot.Library/ParametersParser.cs | 537 ++++++++++++++ irc/TechBot/TechBot.Library/SambaBugUrl.cs | 29 - irc/TechBot/TechBot.Library/ServiceOutput.cs | 9 +- irc/TechBot/TechBot.Library/SvnCommand.cs | 32 - .../TechBot.Library/TechBot.Library.csproj | 34 +- .../{IrcService.cs => TechBotIrcService.cs} | 563 +++++++------- irc/TechBot/TechBot.Library/TechBotService.cs | 86 +-- irc/TechBot/TechBot.Library/WineBugUrl.cs | 29 - irc/TechBot/TechBot.cmbx | 36 - irc/TechBot/TechBot/ProjectInstaller.cs | 28 + irc/TechBot/TechBot/ServiceThread.cs | 10 +- irc/TechBot/TechBot/TechBot.csproj | 4 + irc/TechBot/TechBot/TechBotService.cs | 15 - 42 files changed, 2263 insertions(+), 1689 deletions(-) delete mode 100644 irc/TechBot/Default.build create mode 100644 irc/TechBot/TechBot.Console/ConsoleTechBotService.cs create mode 100644 irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs create mode 100644 irc/TechBot/TechBot.Library/Attributes/CommandParameterAliasAttribute.cs create mode 100644 irc/TechBot/TechBot.Library/Attributes/CommandParameterAttribute.cs delete mode 100644 irc/TechBot/TechBot.Library/BugCommand.cs create mode 100644 irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs delete mode 100644 irc/TechBot/TechBot.Library/Command.cs rename irc/TechBot/TechBot.Library/{ => Commands}/ApiCommand.cs (91%) create mode 100644 irc/TechBot/TechBot.Library/Commands/Base/Command.cs create mode 100644 irc/TechBot/TechBot.Library/Commands/Base/XmlLookupCommand.cs create mode 100644 irc/TechBot/TechBot.Library/Commands/BugCommand.cs rename irc/TechBot/TechBot.Library/{ => Commands}/ErrorCommand.cs (81%) create mode 100644 irc/TechBot/TechBot.Library/Commands/HelpCommand.cs rename irc/TechBot/TechBot.Library/{ => Commands}/HresultCommand.cs (50%) rename irc/TechBot/TechBot.Library/{ => Commands}/NtStatusCommand.cs (51%) rename irc/TechBot/TechBot.Library/{ => Commands}/ReactOSBugUrl.cs (50%) create mode 100644 irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs create mode 100644 irc/TechBot/TechBot.Library/Commands/SvnCommand.cs create mode 100644 irc/TechBot/TechBot.Library/Commands/WineBugUrl.cs rename irc/TechBot/TechBot.Library/{ => Commands}/WinerrorCommand.cs (51%) rename irc/TechBot/TechBot.Library/{ => Commands}/WmCommand.cs (65%) create mode 100644 irc/TechBot/TechBot.Library/Factory/CommandBuilder.cs create mode 100644 irc/TechBot/TechBot.Library/Factory/CommandFactory.cs delete mode 100644 irc/TechBot/TechBot.Library/HelpCommand.cs create mode 100644 irc/TechBot/TechBot.Library/ParametersParser.cs delete mode 100644 irc/TechBot/TechBot.Library/SambaBugUrl.cs delete mode 100644 irc/TechBot/TechBot.Library/SvnCommand.cs rename irc/TechBot/TechBot.Library/{IrcService.cs => TechBotIrcService.cs} (70%) delete mode 100644 irc/TechBot/TechBot.Library/WineBugUrl.cs delete mode 100644 irc/TechBot/TechBot.cmbx create mode 100644 irc/TechBot/TechBot/ProjectInstaller.cs diff --git a/irc/TechBot/Default.build b/irc/TechBot/Default.build deleted file mode 100644 index 62738cfa1f0..00000000000 --- a/irc/TechBot/Default.build +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/irc/TechBot/TechBot.Console/ConsoleTechBotService.cs b/irc/TechBot/TechBot.Console/ConsoleTechBotService.cs new file mode 100644 index 00000000000..14c3c84533d --- /dev/null +++ b/irc/TechBot/TechBot.Console/ConsoleTechBotService.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using TechBot.Library; + +namespace TechBot.Console +{ + public class ConsoleServiceOutput : IServiceOutput + { + public void WriteLine(MessageContext context, + string message) + { + System.Console.WriteLine(message); + } + } + + public class ConsoleTechBotService : TechBotService + { + public ConsoleTechBotService( + string chmPath, + string mainChm) + : base(new ConsoleServiceOutput(), chmPath, mainChm) + { + } + + public override void Run() + { + //Call the base class + base.Run(); + + while (true) + { + string s = System.Console.ReadLine(); + InjectMessage(null, s); + } + } + } +} diff --git a/irc/TechBot/TechBot.Console/Main.cs b/irc/TechBot/TechBot.Console/Main.cs index 6826558a7c6..88760ac51aa 100644 --- a/irc/TechBot/TechBot.Console/Main.cs +++ b/irc/TechBot/TechBot.Console/Main.cs @@ -4,16 +4,6 @@ using TechBot.Library; namespace TechBot.Console { - public class ConsoleServiceOutput : IServiceOutput - { - public void WriteLine(MessageContext context, - string message) - { - System.Console.WriteLine(message); - } - } - - class MainClass { private static void VerifyRequiredOption(string optionName, @@ -208,53 +198,43 @@ namespace TechBot.Console } - private static void RunIrcService() - { - IrcService ircService = new IrcService(IRCServerHostName, - IRCServerHostPort, - IRCChannelNames, - IRCBotName, - IRCBotPassword, - ChmPath, - MainChm, - //NtstatusXml, - //WinerrorXml, - //HresultXml, - //WmXml, - //SvnCommand, - BugUrl, - WineBugUrl, - SambaBugUrl); - ircService.Run(); - } - - public static void Main(string[] args) - { - if (args.Length > 0 && args[0].ToLower().Equals("irc")) - { - RunIrcService(); - return; - } - - System.Console.WriteLine("TechBot running console service..."); - TechBotService service = new TechBotService(new ConsoleServiceOutput(), - ChmPath, - MainChm); - //NtstatusXml, - //WinerrorXml, - //HresultXml, - //WmXml, - //SvnCommand, - //BugUrl, - //WineBugUrl, - //SambaBugUrl); - service.Run(); - while (true) - { - string s = System.Console.ReadLine(); - service.InjectMessage(null, - s); - } - } + //private static void RunIrcService() + //{ + // IrcTechBotService ircService = new IrcTechBotService(IRCServerHostName, + // IRCServerHostPort, + // IRCChannelNames, + // IRCBotName, + // IRCBotPassword, + // ChmPath, + // MainChm); + // ircService.Run(); + //} + + public static void Main(string[] args) + { + TechBotService m_TechBot = null; + + if (args.Length > 0 && args[0].ToLower().Equals("irc")) + { + m_TechBot = new IrcTechBotService(IRCServerHostName, + IRCServerHostPort, + IRCChannelNames, + IRCBotName, + IRCBotPassword, + ChmPath, + MainChm); + } + else + { + System.Console.WriteLine("TechBot running console service..."); + m_TechBot = new ConsoleTechBotService( + ChmPath, + MainChm); + + + } + + m_TechBot.Run(); + } } -} +} \ No newline at end of file diff --git a/irc/TechBot/TechBot.Console/TechBot.Console.csproj b/irc/TechBot/TechBot.Console/TechBot.Console.csproj index 1044ed8c029..4c0b6f77141 100644 --- a/irc/TechBot/TechBot.Console/TechBot.Console.csproj +++ b/irc/TechBot/TechBot.Console/TechBot.Console.csproj @@ -38,6 +38,7 @@ --> + diff --git a/irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs b/irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs new file mode 100644 index 00000000000..a16ee780d7c --- /dev/null +++ b/irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + public class CommandAttribute : Attribute + { + private string m_Name = null; + private string m_Help = "No help for this command is available"; + private string m_Desc = "No description for this command is available"; + + public CommandAttribute(string name) + { + m_Name = name; + } + + public string Name + { + get { return m_Name; } + } + + public string Help + { + get { return m_Help; } + set { m_Help = value; } + } + + public string Description + { + get { return m_Desc; } + set { m_Desc = value; } + } + } +} diff --git a/irc/TechBot/TechBot.Library/Attributes/CommandParameterAliasAttribute.cs b/irc/TechBot/TechBot.Library/Attributes/CommandParameterAliasAttribute.cs new file mode 100644 index 00000000000..05dc10da4bd --- /dev/null +++ b/irc/TechBot/TechBot.Library/Attributes/CommandParameterAliasAttribute.cs @@ -0,0 +1,34 @@ +using System; + +namespace TechBot.Library +{ + /// + /// This class implements an alias attribute to work in conjunction + /// with the CommandLineSwitchAttribute + /// attribute. If the CommandLineSwitchAttribute exists, then this attribute + /// defines an alias for it. + /// + [AttributeUsage( AttributeTargets.Property )] + public class CommandParameterAliasAttribute : Attribute + { + #region Private Variables + protected string m_Alias = ""; + #endregion + + #region Public Properties + public string Alias + { + get { return m_Alias; } + } + + #endregion + + #region Constructors + public CommandParameterAliasAttribute(string alias) + { + m_Alias = alias; + } + #endregion + } + +} diff --git a/irc/TechBot/TechBot.Library/Attributes/CommandParameterAttribute.cs b/irc/TechBot/TechBot.Library/Attributes/CommandParameterAttribute.cs new file mode 100644 index 00000000000..bdb3a8091b0 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Attributes/CommandParameterAttribute.cs @@ -0,0 +1,43 @@ +using System; + +namespace TechBot.Library +{ + /// Implements a basic command-line switch by taking the + /// switching name and the associated description. + /// Only currently is implemented for properties, so all + /// auto-switching variables should have a get/set method supplied. + [AttributeUsage( AttributeTargets.Property )] + public class CommandParameterAttribute : Attribute + { + #region Private Variables + private string m_name = ""; + private string m_description = ""; + private bool m_Required = true; + #endregion + + #region Public Properties + /// Accessor for retrieving the switch-name for an associated + /// property. + public string Name { get { return m_name; } } + + /// Accessor for retrieving the description for a switch of + /// an associated property. + public string Description { get { return m_description; } } + + public bool Required { get { return m_Required; } } + + #endregion + + #region Constructors + + /// + /// Attribute constructor. + /// + public CommandParameterAttribute(string name, string description) + { + m_name = name; + m_description = description; + } + #endregion + } +} diff --git a/irc/TechBot/TechBot.Library/BugCommand.cs b/irc/TechBot/TechBot.Library/BugCommand.cs deleted file mode 100644 index 99427388eb9..00000000000 --- a/irc/TechBot/TechBot.Library/BugCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -namespace TechBot.Library -{ - public abstract class BugCommand : Command//, ICommand - { - public BugCommand(TechBotService techBot) : base (techBot) - { - } - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string bugText = parameters; - if (bugText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide a valid bug number."); - return; - } - - NumberParser np = new NumberParser(); - long bug = np.Parse(bugText); - if (np.Error) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is not a valid bug number.", - bugText)); - return; - } - - /* - string bugUrl = this.RosBugUrl; - - if (context is ChannelMessageContext) - { - ChannelMessageContext channelContext = context as ChannelMessageContext; - if (channelContext.Channel.Name == "winehackers") - bugUrl = this.WineBugUrl; - else if (channelContext.Channel.Name == "samba-technical") - bugUrl = this.SambaBugUrl; - }*/ - - TechBot.ServiceOutput.WriteLine(context, String.Format(BugUrl, bug)); - } - - protected abstract string BugUrl { get; } - } -} diff --git a/irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs b/irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs new file mode 100644 index 00000000000..6cb4a814ca1 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + public class CommandBuilderCollection : List + { + public CommandBuilder Find(string name) + { + foreach (CommandBuilder command in this) + { + if (command.Name == name) + return command; + } + + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/Command.cs b/irc/TechBot/TechBot.Library/Command.cs deleted file mode 100644 index b6ae0a14e58..00000000000 --- a/irc/TechBot/TechBot.Library/Command.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; - -namespace TechBot.Library -{ - /* - public interface ICommand - { - bool CanHandle(string commandName); - void Handle(MessageContext context, - string commandName, - string parameters); - //string Help(); - }*/ - - public abstract class Command - { - protected TechBotService m_TechBotService = null; - - public Command(TechBotService techbot) - { - m_TechBotService = techbot; - } - - public TechBotService TechBot - { - get { return m_TechBotService; } - } - - public abstract string[] AvailableCommands { get; } - - public abstract void Handle(MessageContext context, - string commandName, - string parameters); - - /* - protected bool CanHandle(string commandName, - string[] availableCommands) - { - foreach (string availableCommand in availableCommands) - { - if (String.Compare(availableCommand, commandName, true) == 0) - return true; - } - return false; - } - */ - - public virtual string Help() - { - return "No help is available for this command"; - } - } -} diff --git a/irc/TechBot/TechBot.Library/ApiCommand.cs b/irc/TechBot/TechBot.Library/Commands/ApiCommand.cs similarity index 91% rename from irc/TechBot/TechBot.Library/ApiCommand.cs rename to irc/TechBot/TechBot.Library/Commands/ApiCommand.cs index c0f3cc1d5c7..102ee140d29 100644 --- a/irc/TechBot/TechBot.Library/ApiCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/ApiCommand.cs @@ -1,353 +1,333 @@ -using System; -using System.IO; -using System.Data; -using System.Text.RegularExpressions; -using HtmlHelp; -using HtmlHelp.ChmDecoding; - -namespace TechBot.Library -{ - public class ApiCommand : Command - { - private const bool IsVerbose = false; - - private HtmlHelpSystem chm; - private string chmPath; - private string mainChm; - - public ApiCommand(TechBotService techBot) - : base(techBot) - { - Run(); - } - - private void WriteIfVerbose(MessageContext context, - string message) - { - if (IsVerbose) - TechBot.ServiceOutput.WriteLine(context, - message); - } - - private void Run() - { - string CHMFilename = Path.Combine(chmPath, mainChm); - chm = new HtmlHelpSystem(); - chm.OpenFile(CHMFilename, null); - - Console.WriteLine(String.Format("Loaded main CHM: {0}", - Path.GetFileName(CHMFilename))); - foreach (string filename in Directory.GetFiles(chmPath)) - { - if (!Path.GetExtension(filename).ToLower().Equals(".chm")) - continue; - if (Path.GetFileName(filename).ToLower().Equals(mainChm)) - continue; - - Console.WriteLine(String.Format("Loading CHM: {0}", - Path.GetFileName(filename))); - try - { - chm.MergeFile(filename); - } - catch (Exception ex) - { - Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}", - Path.GetFileName(filename), - ex)); - } - } - Console.WriteLine(String.Format("Loaded {0} CHMs", - chm.FileList.Length)); - } - - public override string[] AvailableCommands - { - get { return new string[] { "api" }; } - } - - /* - public bool CanHandle(string commandName) - { - return CanHandle(commandName, - new string[] { "api" }); - } -*/ - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - if (parameters.Trim().Equals(String.Empty)) - DisplayNoKeyword(context); - else - Search(context, - parameters); - } - - public override string Help() - { - return "!api "; - } - - private bool SearchIndex(MessageContext context, - string keyword) - { - if (chm.HasIndex) - { - IndexItem item = chm.Index.SearchIndex(keyword, - IndexType.KeywordLinks); - if (item != null && item.Topics.Count > 0) - { - WriteIfVerbose(context, - String.Format("Keyword {0} found in index", - item.KeyWord)); - IndexTopic indexTopic = item.Topics[0] as IndexTopic; - return DisplayResult(context, - keyword, - indexTopic); - } - else - { - WriteIfVerbose(context, - String.Format("Keyword {0} not found in index", - keyword)); - return false; - } - } - else - return false; - } - - private void SearchFullText(MessageContext context, - string keyword) - { - string sort = "Rating ASC"; - WriteIfVerbose(context, - String.Format("Searching fulltext database for {0}", - keyword)); - - bool partialMatches = false; - bool titlesOnly = true; - int maxResults = 100; - DataTable results = chm.PerformSearch(keyword, - maxResults, - partialMatches, - titlesOnly); - WriteIfVerbose(context, - String.Format("results.Rows.Count = {0}", - results != null ? - results.Rows.Count.ToString() : "(none)")); - if (results != null && results.Rows.Count > 0) - { - results.DefaultView.Sort = sort; - if (!DisplayResult(context, - keyword, - results)) - { - DisplayNoResult(context, - keyword); - } - } - else - { - DisplayNoResult(context, - keyword); - } - } - - private void Search(MessageContext context, - string keyword) - { - if (!SearchIndex(context, - keyword)) - SearchFullText(context, - keyword); - } - - private bool DisplayResult(MessageContext context, - string keyword, - IndexTopic indexTopic) - { - keyword = keyword.Trim().ToLower(); - string url = indexTopic.URL; - WriteIfVerbose(context, - String.Format("URL from index search {0}", - url)); - string prototype = ExtractPrototype(context, - url); - if (prototype == null || prototype.Trim().Equals(String.Empty)) - return false; - string formattedPrototype = FormatPrototype(prototype); - TechBot.ServiceOutput.WriteLine(context, - formattedPrototype); - return true; - } - - private bool DisplayResult(MessageContext context, - string keyword, - DataTable results) - { - keyword = keyword.Trim().ToLower(); - for (int i = 0; i < results.DefaultView.Count; i++) - { - DataRowView row = results.DefaultView[i]; - string title = row["Title"].ToString(); - WriteIfVerbose(context, - String.Format("Examining {0}", title)); - if (title.Trim().ToLower().Equals(keyword)) - { - string location = row["Location"].ToString(); - string rating = row["Rating"].ToString(); - string url = row["Url"].ToString(); - string prototype = ExtractPrototype(context, - url); - if (prototype == null || prototype.Trim().Equals(String.Empty)) - continue; - string formattedPrototype = FormatPrototype(prototype); - TechBot.ServiceOutput.WriteLine(context, - formattedPrototype); - return true; - } - } - return false; - } - - private void DisplayNoResult(MessageContext context, - string keyword) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about keyword {0}", - keyword)); - } - - private void DisplayNoKeyword(MessageContext context) - { - TechBot.ServiceOutput.WriteLine(context, - "Please give me a keyword."); - } - - private string ReplaceComments(string s) - { - return Regex.Replace(s, "//(.+)\r\n", ""); - } - - private string ReplaceLineEndings(string s) - { - return Regex.Replace(s, "(\r\n)+", " "); - } - - private string ReplaceSpaces(string s) - { - return Regex.Replace(s, @" +", " "); - } - - private string ReplaceSpacesBeforeLeftParenthesis(string s) - { - return Regex.Replace(s, @"\( ", @"("); - } - - private string ReplaceSpacesBeforeRightParenthesis(string s) - { - return Regex.Replace(s, @" \)", @")"); - } - - private string ReplaceSemicolon(string s) - { - return Regex.Replace(s, @";", @""); - } - - private string FormatPrototype(string prototype) - { - string s = ReplaceComments(prototype); - s = ReplaceLineEndings(s); - s = ReplaceSpaces(s); - s = ReplaceSpacesBeforeLeftParenthesis(s); - s = ReplaceSpacesBeforeRightParenthesis(s); - s = ReplaceSemicolon(s); - return s; - } - - private string ExtractPrototype(MessageContext context, - string url) - { - string page = GetPage(context, - url); - Match match = Regex.Match(page, - "
(.+)
", - RegexOptions.Multiline | - RegexOptions.Singleline); - if (match.Groups.Count > 1) - { - string prototype = match.Groups[1].ToString(); - return StripHtml(StripAfterSlashPre(prototype)); - } - - return ""; - } - - private string StripAfterSlashPre(string html) - { - int index = html.IndexOf(""); - if (index != -1) - { - return html.Substring(0, index); - } - else - return html; - } - - private string StripHtml(string html) - { - return Regex.Replace(html, @"<(.|\n)*?>", String.Empty); - } - - private string GetPage(MessageContext context, - string url) - { - string CHMFileName = ""; - string topicName = ""; - string anchor = ""; - CHMStream.CHMStream baseStream; - if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor)) - { - baseStream = chm.BaseStream; - CHMFileName = baseStream.CHMFileName; - topicName = url; - anchor = ""; - } - else - { - baseStream = GetBaseStreamFromCHMFileName(context, - CHMFileName); - } - - if ((topicName == "") || (CHMFileName == "") || (baseStream == null)) - { - return ""; - } - - return baseStream.ExtractTextFile(topicName); - } - - private CHMStream.CHMStream GetBaseStreamFromCHMFileName(MessageContext context, - string CHMFileName) - { - foreach (CHMFile file in chm.FileList) - { - WriteIfVerbose(context, - String.Format("Compare: {0} <> {1}", - file.ChmFilePath, - CHMFileName)); - if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower())) - { - return file.BaseStream; - } - } - WriteIfVerbose(context, - String.Format("Could not find loaded CHM file in list: {0}", - CHMFileName)); - return null; - } - } -} +using System; +using System.IO; +using System.Data; +using System.Text.RegularExpressions; +using HtmlHelp; +using HtmlHelp.ChmDecoding; + +namespace TechBot.Library +{ + [Command("api", Help = "!api ")] + public class ApiCommand : Command + { + private const bool IsVerbose = false; + + private HtmlHelpSystem chm; + private string chmPath; + private string mainChm; + + public ApiCommand() + { + Run(); + } + + private void WriteIfVerbose(MessageContext context, + string message) + { + if (IsVerbose) + TechBot.ServiceOutput.WriteLine(context, + message); + } + + private void Run() + { + string CHMFilename = Path.Combine(chmPath, mainChm); + chm = new HtmlHelpSystem(); + chm.OpenFile(CHMFilename, null); + + Console.WriteLine(String.Format("Loaded main CHM: {0}", + Path.GetFileName(CHMFilename))); + foreach (string filename in Directory.GetFiles(chmPath)) + { + if (!Path.GetExtension(filename).ToLower().Equals(".chm")) + continue; + if (Path.GetFileName(filename).ToLower().Equals(mainChm)) + continue; + + Console.WriteLine(String.Format("Loading CHM: {0}", + Path.GetFileName(filename))); + try + { + chm.MergeFile(filename); + } + catch (Exception ex) + { + Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}", + Path.GetFileName(filename), + ex)); + } + } + Console.WriteLine(String.Format("Loaded {0} CHMs", + chm.FileList.Length)); + } + + public override void Handle(MessageContext context) + { + if (parameters.Trim().Equals(String.Empty)) + DisplayNoKeyword(context); + else + Search(context, + parameters); + } + + private bool SearchIndex(MessageContext context, + string keyword) + { + if (chm.HasIndex) + { + IndexItem item = chm.Index.SearchIndex(keyword, + IndexType.KeywordLinks); + if (item != null && item.Topics.Count > 0) + { + WriteIfVerbose(context, + String.Format("Keyword {0} found in index", + item.KeyWord)); + IndexTopic indexTopic = item.Topics[0] as IndexTopic; + return DisplayResult(context, + keyword, + indexTopic); + } + else + { + WriteIfVerbose(context, + String.Format("Keyword {0} not found in index", + keyword)); + return false; + } + } + else + return false; + } + + private void SearchFullText(MessageContext context, + string keyword) + { + string sort = "Rating ASC"; + WriteIfVerbose(context, + String.Format("Searching fulltext database for {0}", + keyword)); + + bool partialMatches = false; + bool titlesOnly = true; + int maxResults = 100; + DataTable results = chm.PerformSearch(keyword, + maxResults, + partialMatches, + titlesOnly); + WriteIfVerbose(context, + String.Format("results.Rows.Count = {0}", + results != null ? + results.Rows.Count.ToString() : "(none)")); + if (results != null && results.Rows.Count > 0) + { + results.DefaultView.Sort = sort; + if (!DisplayResult(context, + keyword, + results)) + { + DisplayNoResult(context, + keyword); + } + } + else + { + DisplayNoResult(context, + keyword); + } + } + + private void Search(MessageContext context, + string keyword) + { + if (!SearchIndex(context, + keyword)) + SearchFullText(context, + keyword); + } + + private bool DisplayResult(MessageContext context, + string keyword, + IndexTopic indexTopic) + { + keyword = keyword.Trim().ToLower(); + string url = indexTopic.URL; + WriteIfVerbose(context, + String.Format("URL from index search {0}", + url)); + string prototype = ExtractPrototype(context, + url); + if (prototype == null || prototype.Trim().Equals(String.Empty)) + return false; + string formattedPrototype = FormatPrototype(prototype); + TechBot.ServiceOutput.WriteLine(context, + formattedPrototype); + return true; + } + + private bool DisplayResult(MessageContext context, + string keyword, + DataTable results) + { + keyword = keyword.Trim().ToLower(); + for (int i = 0; i < results.DefaultView.Count; i++) + { + DataRowView row = results.DefaultView[i]; + string title = row["Title"].ToString(); + WriteIfVerbose(context, + String.Format("Examining {0}", title)); + if (title.Trim().ToLower().Equals(keyword)) + { + string location = row["Location"].ToString(); + string rating = row["Rating"].ToString(); + string url = row["Url"].ToString(); + string prototype = ExtractPrototype(context, + url); + if (prototype == null || prototype.Trim().Equals(String.Empty)) + continue; + string formattedPrototype = FormatPrototype(prototype); + TechBot.ServiceOutput.WriteLine(context, + formattedPrototype); + return true; + } + } + return false; + } + + private void DisplayNoResult(MessageContext context, + string keyword) + { + TechBot.ServiceOutput.WriteLine(context, + String.Format("I don't know about keyword {0}", + keyword)); + } + + private void DisplayNoKeyword(MessageContext context) + { + TechBot.ServiceOutput.WriteLine(context, + "Please give me a keyword."); + } + + private string ReplaceComments(string s) + { + return Regex.Replace(s, "//(.+)\r\n", ""); + } + + private string ReplaceLineEndings(string s) + { + return Regex.Replace(s, "(\r\n)+", " "); + } + + private string ReplaceSpaces(string s) + { + return Regex.Replace(s, @" +", " "); + } + + private string ReplaceSpacesBeforeLeftParenthesis(string s) + { + return Regex.Replace(s, @"\( ", @"("); + } + + private string ReplaceSpacesBeforeRightParenthesis(string s) + { + return Regex.Replace(s, @" \)", @")"); + } + + private string ReplaceSemicolon(string s) + { + return Regex.Replace(s, @";", @""); + } + + private string FormatPrototype(string prototype) + { + string s = ReplaceComments(prototype); + s = ReplaceLineEndings(s); + s = ReplaceSpaces(s); + s = ReplaceSpacesBeforeLeftParenthesis(s); + s = ReplaceSpacesBeforeRightParenthesis(s); + s = ReplaceSemicolon(s); + return s; + } + + private string ExtractPrototype(MessageContext context, + string url) + { + string page = GetPage(context, + url); + Match match = Regex.Match(page, + "
(.+)
", + RegexOptions.Multiline | + RegexOptions.Singleline); + if (match.Groups.Count > 1) + { + string prototype = match.Groups[1].ToString(); + return StripHtml(StripAfterSlashPre(prototype)); + } + + return ""; + } + + private string StripAfterSlashPre(string html) + { + int index = html.IndexOf(""); + if (index != -1) + { + return html.Substring(0, index); + } + else + return html; + } + + private string StripHtml(string html) + { + return Regex.Replace(html, @"<(.|\n)*?>", String.Empty); + } + + private string GetPage(MessageContext context, + string url) + { + string CHMFileName = ""; + string topicName = ""; + string anchor = ""; + CHMStream.CHMStream baseStream; + if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor)) + { + baseStream = chm.BaseStream; + CHMFileName = baseStream.CHMFileName; + topicName = url; + anchor = ""; + } + else + { + baseStream = GetBaseStreamFromCHMFileName(context, + CHMFileName); + } + + if ((topicName == "") || (CHMFileName == "") || (baseStream == null)) + { + return ""; + } + + return baseStream.ExtractTextFile(topicName); + } + + private CHMStream.CHMStream GetBaseStreamFromCHMFileName(MessageContext context, + string CHMFileName) + { + foreach (CHMFile file in chm.FileList) + { + WriteIfVerbose(context, + String.Format("Compare: {0} <> {1}", + file.ChmFilePath, + CHMFileName)); + if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower())) + { + return file.BaseStream; + } + } + WriteIfVerbose(context, + String.Format("Could not find loaded CHM file in list: {0}", + CHMFileName)); + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/Base/Command.cs b/irc/TechBot/TechBot.Library/Commands/Base/Command.cs new file mode 100644 index 00000000000..839475b73f7 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/Base/Command.cs @@ -0,0 +1,51 @@ +using System; + +namespace TechBot.Library +{ + public abstract class Command + { + protected TechBotService m_TechBotService = null; + protected MessageContext m_Context = null; + + public TechBotService TechBot + { + get { return m_TechBotService; } + set { m_TechBotService = value; } + } + + public MessageContext Context + { + get { return m_Context; } + set { m_Context = value; } + } + + public string Name + { + get + { + CommandAttribute commandAttribute = (CommandAttribute) + Attribute.GetCustomAttribute(GetType(), typeof(CommandAttribute)); + + return commandAttribute.Name; + } + } + + public void ParseParameters(string paramaters) + { + ParametersParser parser = new ParametersParser(paramaters, this); + parser.Parse(); + } + + protected virtual void Say(string message) + { + TechBot.ServiceOutput.WriteLine(Context, message); + } + + protected virtual void Say(string format , params object[] args) + { + TechBot.ServiceOutput.WriteLine(Context, String.Format(format, args)); + } + + public abstract void ExecuteCommand(); + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/Base/XmlCommand.cs b/irc/TechBot/TechBot.Library/Commands/Base/XmlCommand.cs index f5d39df1ab3..673e796eb50 100644 --- a/irc/TechBot/TechBot.Library/Commands/Base/XmlCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/Base/XmlCommand.cs @@ -9,8 +9,7 @@ namespace TechBot.Library { protected XmlDocument m_XmlDocument; - public XmlCommand(TechBotService techBot) - : base(techBot) + public XmlCommand() { m_XmlDocument = new XmlDocument(); m_XmlDocument.Load(XmlFile); diff --git a/irc/TechBot/TechBot.Library/Commands/Base/XmlLookupCommand.cs b/irc/TechBot/TechBot.Library/Commands/Base/XmlLookupCommand.cs new file mode 100644 index 00000000000..547badb5adb --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/Base/XmlLookupCommand.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + public abstract class XmlLookupCommand : XmlCommand + { + private string m_Text = null; + + [CommandParameter("text", "The value to check")] + public string Text + { + get { return m_Text; } + set { m_Text = value; } + } + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/BugCommand.cs b/irc/TechBot/TechBot.Library/Commands/BugCommand.cs new file mode 100644 index 00000000000..1beb5fce661 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/BugCommand.cs @@ -0,0 +1,41 @@ +using System; + +namespace TechBot.Library +{ + public abstract class BugCommand : Command + { + private string m_BugID = null; + + public BugCommand() + { + } + + [CommandParameter("id", "The bug ID")] + public string BugID + { + get { return m_BugID; } + set { m_BugID = value; } + } + + public override void ExecuteCommand() + { + if (BugID == null) + { + Say("Please provide a valid bug number."); + } + else + { + try + { + Say(BugUrl, Int32.Parse(BugID)); + } + catch (Exception) + { + Say("{0} is not a valid bug number.", BugID); + } + } + } + + protected abstract string BugUrl { get; } + } +} diff --git a/irc/TechBot/TechBot.Library/ErrorCommand.cs b/irc/TechBot/TechBot.Library/Commands/ErrorCommand.cs similarity index 81% rename from irc/TechBot/TechBot.Library/ErrorCommand.cs rename to irc/TechBot/TechBot.Library/Commands/ErrorCommand.cs index 3d7a7cf8c1a..5b4551aa9d2 100644 --- a/irc/TechBot/TechBot.Library/ErrorCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/ErrorCommand.cs @@ -1,206 +1,185 @@ -using System; -using System.Xml; -using System.Collections; - -namespace TechBot.Library -{ - public class ErrorCommand : Command - { - private NtStatusCommand ntStatus; - private WinerrorCommand winerror; - private HResultCommand hresult; - - public ErrorCommand(TechBotService techBot) - : base(techBot) - { - this.ntStatus = new NtStatusCommand(techBot); - this.winerror = new WinerrorCommand(techBot); - this.hresult = new HResultCommand(techBot); - } - - /* - public bool CanHandle(string commandName) - { - return CanHandle(commandName, - new string[] { "error" }); - } - */ - - public override string[] AvailableCommands - { - get { return new string[] { "error" }; } - } - - private static int GetSeverity(long error) - { - return (int)((error >> 30) & 0x3); - } - - private static bool IsCustomer(long error) - { - return (error & 0x20000000) != 0; - } - - private static bool IsReserved(long error) - { - return (error & 0x10000000) != 0; - } - - private static int GetFacility(long error) - { - return (int)((error >> 16) & 0xFFF); - } - - private static short GetCode(long error) - { - return (short)((error >> 0) & 0xFFFF); - } - - private static string FormatSeverity(long error) - { - int severity = GetSeverity(error); - switch (severity) - { - case 0: return "SUCCESS"; - case 1: return "INFORMATIONAL"; - case 2: return "WARNING"; - case 3: return "ERROR"; - } - return null; - } - - private static string FormatFacility(long error) - { - int facility = GetFacility(error); - return facility.ToString(); - } - - private static string FormatCode(long error) - { - int code = GetCode(error); - return code.ToString(); - } - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string originalErrorText = parameters.Trim(); - if (originalErrorText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide an Error Code."); - return; - } - - string errorText = originalErrorText; - - retry: - NumberParser np = new NumberParser(); - long error = np.Parse(errorText); - if (np.Error) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is not a valid Error Code.", - originalErrorText)); - return; - } - - ArrayList descriptions = new ArrayList(); - - // Error is out of bounds - if ((ulong)error > uint.MaxValue) - { - // Do nothing - } - // Error is outside of the range [0, 65535]: it cannot be a plain Win32 error code - else if ((ulong)error > ushort.MaxValue) - { - // Customer bit is set: custom error code - if (IsCustomer(error)) - { - string description = String.Format("[custom, severity {0}, facility {1}, code {2}]", - FormatSeverity(error), - FormatFacility(error), - FormatCode(error)); - descriptions.Add(description); - } - // Reserved bit is set: HRESULT_FROM_NT(ntstatus) - else if (IsReserved(error)) - { - int status = (int)(error & 0xCFFFFFFF); - string description = ntStatus.GetNtstatusDescription(status); - - if (description == null) - description = status.ToString("X"); - - description = String.Format("HRESULT_FROM_NT({0})", description); - descriptions.Add(description); - } - // Win32 facility: HRESULT_FROM_WIN32(winerror) - else if (GetFacility(error) == 7) - { - // Must be an error code - if (GetSeverity(error) == 2) - { - short err = GetCode(error); - string description = winerror.GetWinerrorDescription(err); - - if (description == null) - description = err.ToString("D"); - - description = String.Format("HRESULT_FROM_WIN32({0})", description); - descriptions.Add(description); - } - } - } - - string winerrorDescription = winerror.GetWinerrorDescription(error); - string ntstatusDescription = ntStatus.GetNtstatusDescription(error); - string hresultDescription = hresult.GetHresultDescription(error); - - if (winerrorDescription != null) - descriptions.Add(winerrorDescription); - if (ntstatusDescription != null) - descriptions.Add(ntstatusDescription); - if (hresultDescription != null) - descriptions.Add(hresultDescription); - - if (descriptions.Count == 0) - { - // Last chance heuristics: attempt to parse a 8-digit decimal as hexadecimal - if (errorText.Length == 8) - { - errorText = "0x" + errorText; - goto retry; - } - - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about Error Code {0}.", - originalErrorText)); - } - else if (descriptions.Count == 1) - { - string description = (string)descriptions[0]; - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is {1}.", - originalErrorText, - description)); - } - else - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} could be:", - originalErrorText)); - - foreach(string description in descriptions) - TechBot.ServiceOutput.WriteLine(context, String.Format("\t{0}", description)); - } - } - - public override string Help() - { - return "!error "; - } - } -} +using System; +using System.Xml; +using System.Collections; + +namespace TechBot.Library +{ + [Command("error", Help = "!error ")] + public class ErrorCommand : Command + { + private NtStatusCommand ntStatus; + private WinErrorCommand winerror; + private HResultCommand hresult; + + public ErrorCommand() + { + this.ntStatus = new NtStatusCommand(); + this.winerror = new WinErrorCommand(); + this.hresult = new HResultCommand(); + } + + private static int GetSeverity(long error) + { + return (int)((error >> 30) & 0x3); + } + + private static bool IsCustomer(long error) + { + return (error & 0x20000000) != 0; + } + + private static bool IsReserved(long error) + { + return (error & 0x10000000) != 0; + } + + private static int GetFacility(long error) + { + return (int)((error >> 16) & 0xFFF); + } + + private static short GetCode(long error) + { + return (short)((error >> 0) & 0xFFFF); + } + + private static string FormatSeverity(long error) + { + int severity = GetSeverity(error); + switch (severity) + { + case 0: return "SUCCESS"; + case 1: return "INFORMATIONAL"; + case 2: return "WARNING"; + case 3: return "ERROR"; + } + return null; + } + + private static string FormatFacility(long error) + { + int facility = GetFacility(error); + return facility.ToString(); + } + + private static string FormatCode(long error) + { + int code = GetCode(error); + return code.ToString(); + } + + public override void Handle(MessageContext context) + { + if (Text.Equals(String.Empty)) + { + TechBot.ServiceOutput.WriteLine(context, + "Please provide an Error Code."); + return; + } + + string errorText = originalErrorText; + + retry: + NumberParser np = new NumberParser(); + long error = np.Parse(errorText); + if (np.Error) + { + TechBot.ServiceOutput.WriteLine(context, + String.Format("{0} is not a valid Error Code.", + originalErrorText)); + return; + } + + ArrayList descriptions = new ArrayList(); + + // Error is out of bounds + if ((ulong)error > uint.MaxValue) + { + // Do nothing + } + // Error is outside of the range [0, 65535]: it cannot be a plain Win32 error code + else if ((ulong)error > ushort.MaxValue) + { + // Customer bit is set: custom error code + if (IsCustomer(error)) + { + string description = String.Format("[custom, severity {0}, facility {1}, code {2}]", + FormatSeverity(error), + FormatFacility(error), + FormatCode(error)); + descriptions.Add(description); + } + // Reserved bit is set: HRESULT_FROM_NT(ntstatus) + else if (IsReserved(error)) + { + int status = (int)(error & 0xCFFFFFFF); + string description = ntStatus.GetNtstatusDescription(status); + + if (description == null) + description = status.ToString("X"); + + description = String.Format("HRESULT_FROM_NT({0})", description); + descriptions.Add(description); + } + // Win32 facility: HRESULT_FROM_WIN32(winerror) + else if (GetFacility(error) == 7) + { + // Must be an error code + if (GetSeverity(error) == 2) + { + short err = GetCode(error); + string description = winerror.GetWinerrorDescription(err); + + if (description == null) + description = err.ToString("D"); + + description = String.Format("HRESULT_FROM_WIN32({0})", description); + descriptions.Add(description); + } + } + } + + string winerrorDescription = winerror.GetWinerrorDescription(error); + string ntstatusDescription = ntStatus.GetNtstatusDescription(error); + string hresultDescription = hresult.GetHresultDescription(error); + + if (winerrorDescription != null) + descriptions.Add(winerrorDescription); + if (ntstatusDescription != null) + descriptions.Add(ntstatusDescription); + if (hresultDescription != null) + descriptions.Add(hresultDescription); + + if (descriptions.Count == 0) + { + // Last chance heuristics: attempt to parse a 8-digit decimal as hexadecimal + if (errorText.Length == 8) + { + errorText = "0x" + errorText; + goto retry; + } + + TechBot.ServiceOutput.WriteLine(context, + String.Format("I don't know about Error Code {0}.", + originalErrorText)); + } + else if (descriptions.Count == 1) + { + string description = (string)descriptions[0]; + TechBot.ServiceOutput.WriteLine(context, + String.Format("{0} is {1}.", + originalErrorText, + description)); + } + else + { + TechBot.ServiceOutput.WriteLine(context, + String.Format("{0} could be:", + originalErrorText)); + + foreach(string description in descriptions) + TechBot.ServiceOutput.WriteLine(context, String.Format("\t{0}", description)); + } + } + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/HelpCommand.cs b/irc/TechBot/TechBot.Library/Commands/HelpCommand.cs new file mode 100644 index 00000000000..f47ae18e912 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/HelpCommand.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; + +namespace TechBot.Library +{ + [Command("help", Help = "!help")] + public class HelpCommand : Command + { + private string m_CommandName = null; + + public HelpCommand() + { + } + + [CommandParameter("Name", "The command name to show help")] + public string CommandName + { + get { return m_CommandName; } + set { m_CommandName = value; } + } + + public override void ExecuteCommand() + { + if (CommandName == null) + { + Say("I support the following commands:"); + + foreach (CommandBuilder command in TechBot.Commands) + { + Say("!{0} - {1}", + command.Name, + command.Description); + } + } + else + { + CommandBuilder cmdBuilder = TechBot.Commands.Find(CommandName); + + if (cmdBuilder == null) + { + Say("Command '{0}' is not recognized. Type '!help' to show all available commands", CommandName); + } + else + { + Say("Command '{0}' help:", CommandName); + Say(""); + } + } + } + } +} diff --git a/irc/TechBot/TechBot.Library/HresultCommand.cs b/irc/TechBot/TechBot.Library/Commands/HresultCommand.cs similarity index 50% rename from irc/TechBot/TechBot.Library/HresultCommand.cs rename to irc/TechBot/TechBot.Library/Commands/HresultCommand.cs index c8842cdbb98..cb631cd6c3b 100644 --- a/irc/TechBot/TechBot.Library/HresultCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/HresultCommand.cs @@ -1,90 +1,69 @@ -using System; -using System.Xml; - -namespace TechBot.Library -{ - public class HResultCommand : XmlCommand - { - public HResultCommand(TechBotService techBot) - : base(techBot) - { - } - - public override string XmlFile - { - get { return Settings.Default.HResultXml; } - } - - public override string[] AvailableCommands - { - get { return new string[] { "hresult" }; } - } - - /* - public bool CanHandle(string commandName) - { - return CanHandle(commandName, - new string[] { "hresult" }); - } - */ - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string hresultText = parameters; - if (hresultText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide a valid HRESULT value."); - return; - } - - NumberParser np = new NumberParser(); - long hresult = np.Parse(hresultText); - if (np.Error) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is not a valid HRESULT value.", - hresultText)); - return; - } - - string description = GetHresultDescription(hresult); - if (description != null) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is {1}.", - hresultText, - description)); - } - else - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about HRESULT {0}.", - hresultText)); - } - } - - public override string Help() - { - return "!hresult "; - } - - public string GetHresultDescription(long hresult) - { - XmlElement root = base.m_XmlDocument.DocumentElement; - XmlNode node = root.SelectSingleNode(String.Format("Hresult[@value='{0}']", - hresult.ToString("X8"))); - if (node != null) - { - XmlAttribute text = node.Attributes["text"]; - if (text == null) - throw new Exception("Node has no text attribute."); - return text.Value; - } - else - return null; - } - } -} +using System; +using System.Xml; + +namespace TechBot.Library +{ + [Command("hresult", Help = "!hresult ")] + public class HResultCommand : XmlLookupCommand + { + public HResultCommand() + { + } + + public override string XmlFile + { + get { return Settings.Default.HResultXml; } + } + + public override void ExecuteCommand() + { + if (Text.Equals(String.Empty)) + { + TechBot.ServiceOutput.WriteLine(Context, + "Please provide a valid HRESULT value."); + return; + } + + NumberParser np = new NumberParser(); + long hresult = np.Parse(Text); + if (np.Error) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is not a valid HRESULT value.", + Text)); + return; + } + + string description = GetHresultDescription(hresult); + if (description != null) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is {1}.", + Text, + description)); + } + else + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("I don't know about HRESULT {0}.", + Text)); + } + } + + public string GetHresultDescription(long hresult) + { + XmlElement root = base.m_XmlDocument.DocumentElement; + XmlNode node = root.SelectSingleNode(String.Format("Hresult[@value='{0}']", + hresult.ToString("X8"))); + if (node != null) + { + XmlAttribute text = node.Attributes["text"]; + if (text == null) + throw new Exception("Node has no text attribute."); + return text.Value; + } + else + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/NtStatusCommand.cs b/irc/TechBot/TechBot.Library/Commands/NtStatusCommand.cs similarity index 51% rename from irc/TechBot/TechBot.Library/NtStatusCommand.cs rename to irc/TechBot/TechBot.Library/Commands/NtStatusCommand.cs index 4833c57487d..c38751bf35b 100644 --- a/irc/TechBot/TechBot.Library/NtStatusCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/NtStatusCommand.cs @@ -1,88 +1,69 @@ -using System; -using System.Xml; - -namespace TechBot.Library -{ - public class NtStatusCommand : XmlCommand - { - public NtStatusCommand(TechBotService techBot) - : base(techBot) - { - } - - public override string XmlFile - { - get { return Settings.Default.NtStatusXml; } - } - - public override string[] AvailableCommands - { - get { return new string[] { "ntstatus" }; } - } -/* - public bool CanHandle(string commandName) - { - return CanHandle(commandName, - new string[] { "ntstatus" }); - } -*/ - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string ntstatusText = parameters; - if (ntstatusText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide a valid NTSTATUS value."); - return; - } - - NumberParser np = new NumberParser(); - long ntstatus = np.Parse(ntstatusText); - if (np.Error) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is not a valid NTSTATUS value.", - ntstatusText)); - return; - } - - string description = GetNtstatusDescription(ntstatus); - if (description != null) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is {1}.", - ntstatusText, - description)); - } - else - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about NTSTATUS {0}.", - ntstatusText)); - } - } - - public override string Help() - { - return "!ntstatus "; - } - - public string GetNtstatusDescription(long ntstatus) - { - XmlElement root = base.m_XmlDocument.DocumentElement; - XmlNode node = root.SelectSingleNode(String.Format("Ntstatus[@value='{0}']", - ntstatus.ToString("X8"))); - if (node != null) - { - XmlAttribute text = node.Attributes["text"]; - if (text == null) - throw new Exception("Node has no text attribute."); - return text.Value; - } - else - return null; - } - } -} +using System; +using System.Xml; + +namespace TechBot.Library +{ + [Command("ntstatus", Help = "!ntstatus ")] + public class NtStatusCommand : XmlLookupCommand + { + public NtStatusCommand() + { + } + + public override string XmlFile + { + get { return Settings.Default.NtStatusXml; } + } + + public override void ExecuteCommand() + { + if (Text.Equals(String.Empty)) + { + TechBot.ServiceOutput.WriteLine(Context, + "Please provide a valid NTSTATUS value."); + return; + } + + NumberParser np = new NumberParser(); + long ntstatus = np.Parse(Text); + if (np.Error) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is not a valid NTSTATUS value.", + Text)); + return; + } + + string description = GetNtstatusDescription(ntstatus); + if (description != null) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is {1}.", + Text, + description)); + } + else + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("I don't know about NTSTATUS {0}.", + Text)); + } + } + + public string GetNtstatusDescription(long ntstatus) + { + XmlElement root = base.m_XmlDocument.DocumentElement; + XmlNode node = root.SelectSingleNode(String.Format("Ntstatus[@value='{0}']", + ntstatus.ToString("X8"))); + if (node != null) + { + XmlAttribute text = node.Attributes["text"]; + if (text == null) + throw new Exception("Node has no text attribute."); + return text.Value; + } + else + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/ReactOSBugUrl.cs b/irc/TechBot/TechBot.Library/Commands/ReactOSBugUrl.cs similarity index 50% rename from irc/TechBot/TechBot.Library/ReactOSBugUrl.cs rename to irc/TechBot/TechBot.Library/Commands/ReactOSBugUrl.cs index daa0e5c5c73..0fd194d8443 100644 --- a/irc/TechBot/TechBot.Library/ReactOSBugUrl.cs +++ b/irc/TechBot/TechBot.Library/Commands/ReactOSBugUrl.cs @@ -4,26 +4,16 @@ using System.Text; namespace TechBot.Library { + [Command("rosbug", Help = "!rosbug ")] class ReactOSBugUrl : BugCommand { - public ReactOSBugUrl(TechBotService techBot) - : base(techBot) + public ReactOSBugUrl() { } - public override string[] AvailableCommands - { - get { return new string[] { "rosbug" }; } - } - protected override string BugUrl { get { return "http://www.reactos.org/bugzilla/show_bug.cgi?id={0}"; } } - - public override string Help() - { - return "!rosbug "; - } } } diff --git a/irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs b/irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs new file mode 100644 index 00000000000..74b51827d5a --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + [Command("sambabug", Help = "!sambabug ")] + class SambaBugUrl : BugCommand + { + public SambaBugUrl() + { + } + + protected override string BugUrl + { + get { return "https://bugzilla.samba.org/show_bug.cgi?id={0}"; } + } + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/SvnCommand.cs b/irc/TechBot/TechBot.Library/Commands/SvnCommand.cs new file mode 100644 index 00000000000..5146c281b5f --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/SvnCommand.cs @@ -0,0 +1,20 @@ +using System; + +namespace TechBot.Library +{ + [Command("svn", Help = "!svn")] + public class SvnCommand : Command + { + private string m_SvnRoot; + + public SvnCommand() + { + m_SvnRoot = Settings.Default.SVNRoot; + } + + public override void ExecuteCommand() + { + Say("svn co {0}", m_SvnRoot); + } + } +} diff --git a/irc/TechBot/TechBot.Library/Commands/WineBugUrl.cs b/irc/TechBot/TechBot.Library/Commands/WineBugUrl.cs new file mode 100644 index 00000000000..ace4f003e0a --- /dev/null +++ b/irc/TechBot/TechBot.Library/Commands/WineBugUrl.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + [Command("winebug", Help = "!winebug ")] + class WineBugUrl : BugCommand + { + public WineBugUrl() + { + } + + protected override string BugUrl + { + get { return "http://bugs.winehq.org/show_bug.cgi?id={0}"; } + } + } +} diff --git a/irc/TechBot/TechBot.Library/WinerrorCommand.cs b/irc/TechBot/TechBot.Library/Commands/WinerrorCommand.cs similarity index 51% rename from irc/TechBot/TechBot.Library/WinerrorCommand.cs rename to irc/TechBot/TechBot.Library/Commands/WinerrorCommand.cs index 512157575a8..66e439a68e0 100644 --- a/irc/TechBot/TechBot.Library/WinerrorCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/WinerrorCommand.cs @@ -1,82 +1,69 @@ -using System; -using System.Xml; - -namespace TechBot.Library -{ - public class WinerrorCommand : XmlCommand - { - public WinerrorCommand(TechBotService techBot) - : base(techBot) - { - } - - public override string XmlFile - { - get { return Settings.Default.WinErrorXml; } - } - - public override string[] AvailableCommands - { - get { return new string[] { "winerror" }; } - } - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string winerrorText = parameters; - if (winerrorText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide a valid System Error Code value."); - return; - } - - NumberParser np = new NumberParser(); - long winerror = np.Parse(winerrorText); - if (np.Error) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is not a valid System Error Code value.", - winerrorText)); - return; - } - - string description = GetWinerrorDescription(winerror); - if (description != null) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is {1}.", - winerrorText, - description)); - } - else - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about System Error Code {0}.", - winerrorText)); - } - } - - public override string Help() - { - return "!winerror "; - } - - public string GetWinerrorDescription(long winerror) - { - XmlElement root = base.m_XmlDocument.DocumentElement; - XmlNode node = root.SelectSingleNode(String.Format("Winerror[@value='{0}']", - winerror)); - if (node != null) - { - XmlAttribute text = node.Attributes["text"]; - if (text == null) - throw new Exception("Node has no text attribute."); - return text.Value; - } - else - return null; - } - } -} +using System; +using System.Xml; + +namespace TechBot.Library +{ + [Command("winerror", Help = "!winerror ")] + public class WinErrorCommand : XmlLookupCommand + { + public WinErrorCommand() + { + } + + public override string XmlFile + { + get { return Settings.Default.WinErrorXml; } + } + + public override void ExecuteCommand() + { + if (Text.Equals(String.Empty)) + { + TechBot.ServiceOutput.WriteLine(Context, + "Please provide a valid System Error Code value."); + return; + } + + NumberParser np = new NumberParser(); + long winerror = np.Parse(Text); + if (np.Error) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is not a valid System Error Code value.", + Text)); + return; + } + + string description = GetWinerrorDescription(winerror); + if (description != null) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is {1}.", + Text, + description)); + } + else + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("I don't know about System Error Code {0}.", + Text)); + } + } + + public string GetWinerrorDescription(long winerror) + { + XmlElement root = base.m_XmlDocument.DocumentElement; + XmlNode node = root.SelectSingleNode(String.Format("Winerror[@value='{0}']", + Text)); + if (node != null) + { + XmlAttribute text = node.Attributes["text"]; + if (text == null) + throw new Exception("Node has no text attribute."); + return text.Value; + } + else + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/WmCommand.cs b/irc/TechBot/TechBot.Library/Commands/WmCommand.cs similarity index 65% rename from irc/TechBot/TechBot.Library/WmCommand.cs rename to irc/TechBot/TechBot.Library/Commands/WmCommand.cs index 31d670109ae..4ffd4939e85 100644 --- a/irc/TechBot/TechBot.Library/WmCommand.cs +++ b/irc/TechBot/TechBot.Library/Commands/WmCommand.cs @@ -1,100 +1,96 @@ -using System; -using System.Xml; - -namespace TechBot.Library -{ - public class WMCommand : XmlCommand - { - public WMCommand(TechBotService techBot) - : base(techBot) - { - } - - public override string XmlFile - { - get { return Settings.Default.WMXml; } - } - - public override string[] AvailableCommands - { - get { return new string[] { "wm" }; } - } - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - string wmText = parameters; - if (wmText.Equals(String.Empty)) - { - TechBot.ServiceOutput.WriteLine(context, - "Please provide a valid window message value or name."); - return; - } - - NumberParser np = new NumberParser(); - long wm = np.Parse(wmText); - string output; - if (np.Error) - { - // Assume "!wm " form. - output = GetWmNumber(wmText); - } - else - { - output = GetWmDescription(wm); - } - - if (output != null) - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("{0} is {1}.", - wmText, - output)); - } - else - { - TechBot.ServiceOutput.WriteLine(context, - String.Format("I don't know about window message {0}.", - wmText)); - } - } - - public override string Help() - { - return "!wm or !wm "; - } - - private string GetWmDescription(long wm) - { - XmlElement root = base.m_XmlDocument.DocumentElement; - XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@value='{0}']", - wm)); - if (node != null) - { - XmlAttribute text = node.Attributes["text"]; - if (text == null) - throw new Exception("Node has no text attribute."); - return text.Value; - } - else - return null; - } - - private string GetWmNumber(string wmName) - { - XmlElement root = base.m_XmlDocument.DocumentElement; - XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@text='{0}']", - wmName)); - if (node != null) - { - XmlAttribute value = node.Attributes["value"]; - if (value == null) - throw new Exception("Node has no value attribute."); - return value.Value; - } - else - return null; - } - } -} +using System; +using System.Xml; + +namespace TechBot.Library +{ + [Command("wm" , Help = "!wm or !wm ")] + public class WMCommand : XmlCommand + { + private string m_WMText = null; + + public WMCommand() + { + } + + public override string XmlFile + { + get { return Settings.Default.WMXml; } + } + + [CommandParameter("wm", "The windows message to check")] + public string WMText + { + get { return m_WMText; } + set { m_WMText = value; } + } + + public override void ExecuteCommand() + { + if (WMText.Equals(String.Empty)) + { + TechBot.ServiceOutput.WriteLine(Context, + "Please provide a valid window message value or name."); + return; + } + + NumberParser np = new NumberParser(); + long wm = np.Parse(WMText); + string output; + if (np.Error) + { + // Assume "!wm " form. + output = GetWmNumber(WMText); + } + else + { + output = GetWmDescription(wm); + } + + if (output != null) + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("{0} is {1}.", + WMText, + output)); + } + else + { + TechBot.ServiceOutput.WriteLine(Context, + String.Format("I don't know about window message {0}.", + WMText)); + } + } + + private string GetWmDescription(long wm) + { + XmlElement root = base.m_XmlDocument.DocumentElement; + XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@value='{0}']", + wm)); + if (node != null) + { + XmlAttribute text = node.Attributes["text"]; + if (text == null) + throw new Exception("Node has no text attribute."); + return text.Value; + } + else + return null; + } + + private string GetWmNumber(string wmName) + { + XmlElement root = base.m_XmlDocument.DocumentElement; + XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@text='{0}']", + wmName)); + if (node != null) + { + XmlAttribute value = node.Attributes["value"]; + if (value == null) + throw new Exception("Node has no value attribute."); + return value.Value; + } + else + return null; + } + } +} diff --git a/irc/TechBot/TechBot.Library/Factory/CommandBuilder.cs b/irc/TechBot/TechBot.Library/Factory/CommandBuilder.cs new file mode 100644 index 00000000000..6a459670a4c --- /dev/null +++ b/irc/TechBot/TechBot.Library/Factory/CommandBuilder.cs @@ -0,0 +1,52 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Text; + +namespace TechBot.Library +{ + public class CommandBuilder + { + private Type m_CommandType; + private string m_CommandName; + private string m_CommandHelp; + private string m_CommandDesc; + + public CommandBuilder(Type commandType) + { + m_CommandType = commandType; + + CommandAttribute commandAttribute = (CommandAttribute) + Attribute.GetCustomAttribute(commandType, typeof(CommandAttribute)); + + m_CommandName = commandAttribute.Name; + m_CommandHelp = commandAttribute.Help; + m_CommandDesc = commandAttribute.Description; + } + + public string Name + { + get { return m_CommandName; } + } + + public string Help + { + get { return m_CommandHelp; } + } + + public string Description + { + get { return m_CommandDesc; } + } + + public Type Type + { + get { return m_CommandType; } + } + + public Command CreateCommand() + { + return (Command)Type.Assembly.CreateInstance(Type.FullName, true); + } + } +} diff --git a/irc/TechBot/TechBot.Library/Factory/CommandFactory.cs b/irc/TechBot/TechBot.Library/Factory/CommandFactory.cs new file mode 100644 index 00000000000..34e3471f6f8 --- /dev/null +++ b/irc/TechBot/TechBot.Library/Factory/CommandFactory.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; + +namespace TechBot.Library +{ + public class CommandFactory + { + private static CommandBuilderCollection m_Commands = new CommandBuilderCollection(); + + private CommandFactory() + { + } + + public static void LoadPlugins() + { + //get the file names of the dll files in the current directory. + FileInfo objExeInfo = new FileInfo(@"C:\Ros\current\irc\TechBot\TechBot.Console\bin\Debug\"); + + foreach (FileInfo objInfo in objExeInfo.Directory.GetFiles("*.dll")) + { + LoadPluginsFromDLLFile(objInfo.FullName); + } + } + + private static void LoadPluginsFromDLLFile(string sFile) + { + Assembly assPlugin = Assembly.LoadFile(sFile); + + if (assPlugin != null) + { + foreach (Type pluginType in assPlugin.GetTypes()) + { + if (pluginType.IsSubclassOf(typeof(Command))) + { + if (pluginType.IsAbstract == false) + { + //Add it to the list. + Commands.Add(new CommandBuilder(pluginType)); + } + } + } + } + } + + public static CommandBuilderCollection Commands + { + get { return m_Commands; } + } + } +} diff --git a/irc/TechBot/TechBot.Library/HelpCommand.cs b/irc/TechBot/TechBot.Library/HelpCommand.cs deleted file mode 100644 index b007a7e5cc3..00000000000 --- a/irc/TechBot/TechBot.Library/HelpCommand.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections; - -namespace TechBot.Library -{ - public class HelpCommand : Command - { - public HelpCommand(TechBotService techBot) - : base(techBot) - { - } - - public override string[] AvailableCommands - { - get { return new string[] { "help" }; } - } - - public override void Handle( - MessageContext context, - string commandName, - string parameters) - { - TechBot.ServiceOutput.WriteLine(context, "I support the following commands:"); - - foreach (Command command in TechBot.Commands) - { - TechBot.ServiceOutput.WriteLine(context, - command.Help()); - } - } - - public override string Help() - { - return "!help"; - } - } -} diff --git a/irc/TechBot/TechBot.Library/MessageContext.cs b/irc/TechBot/TechBot.Library/MessageContext.cs index 8d1728a25c1..1d9677c8462 100644 --- a/irc/TechBot/TechBot.Library/MessageContext.cs +++ b/irc/TechBot/TechBot.Library/MessageContext.cs @@ -7,43 +7,33 @@ namespace TechBot.Library { } - - public class ChannelMessageContext : MessageContext { - private IrcChannel channel; + private IrcChannel m_IrcChannel; - public IrcChannel Channel - { - get - { - return channel; - } - } + public IrcChannel Channel + { + get { return m_IrcChannel; } + } public ChannelMessageContext(IrcChannel channel) { - this.channel = channel; + m_IrcChannel = channel; } } - - public class UserMessageContext : MessageContext { - private IrcUser user; + private IrcUser m_IrcUser; - public IrcUser User - { - get - { - return user; - } - } + public IrcUser User + { + get { return m_IrcUser; } + } public UserMessageContext(IrcUser user) { - this.user = user; + m_IrcUser = user; } } } diff --git a/irc/TechBot/TechBot.Library/ParametersParser.cs b/irc/TechBot/TechBot.Library/ParametersParser.cs new file mode 100644 index 00000000000..4ce16853518 --- /dev/null +++ b/irc/TechBot/TechBot.Library/ParametersParser.cs @@ -0,0 +1,537 @@ +using System; +using System.Text.RegularExpressions; + +//Code taken from : http://www.codeproject.com/KB/recipes/commandlineparser.aspx + +namespace TechBot.Library +{ + /// Implementation of a command-line parsing class. Is capable of + /// having switches registered with it directly or can examine a registered + /// class for any properties with the appropriate attributes appended to + /// them. + public class ParametersParser + { + /// A simple internal class for passing back to the caller + /// some information about the switch. The internals/implementation + /// of this class has privillaged access to the contents of the + /// SwitchRecord class. + public class SwitchInfo + { + #region Private Variables + private object m_Switch = null; + #endregion + + #region Public Properties + public string Name { get { return (m_Switch as SwitchRecord).Name; } } + public string Description { get { return (m_Switch as SwitchRecord).Description; } } + public string[] Aliases { get { return (m_Switch as SwitchRecord).Aliases; } } + public System.Type Type { get { return (m_Switch as SwitchRecord).Type; } } + public object Value { get { return (m_Switch as SwitchRecord).Value; } } + public object InternalValue { get { return (m_Switch as SwitchRecord).InternalValue; } } + public bool IsEnum { get { return (m_Switch as SwitchRecord).Type.IsEnum; } } + public string[] Enumerations { get { return (m_Switch as SwitchRecord).Enumerations; } } + #endregion + + /// + /// Constructor for the SwitchInfo class. Note, in order to hide to the outside world + /// information not necessary to know, the constructor takes a System.Object (aka + /// object) as it's registering type. If the type isn't of the correct type, an exception + /// is thrown. + /// + /// The SwitchRecord for which this class store information. + /// Thrown if the rec parameter is not of + /// the type SwitchRecord. + public SwitchInfo( object rec ) + { + if ( rec is SwitchRecord ) + m_Switch = rec; + else + throw new ArgumentException(); + } + } + + /// + /// The SwitchRecord is stored within the parser's collection of registered + /// switches. This class is private to the outside world. + /// + private class SwitchRecord + { + #region Private Variables + private string m_name = ""; + private string m_description = ""; + private object m_value = null; + private System.Type m_switchType = typeof(bool); + private System.Collections.ArrayList m_Aliases = null; + private string m_Pattern = ""; + + // The following advanced functions allow for callbacks to be + // made to manipulate the associated data type. + private System.Reflection.MethodInfo m_SetMethod = null; + private System.Reflection.MethodInfo m_GetMethod = null; + private object m_PropertyOwner = null; + #endregion + + #region Private Utility Functions + private void Initialize( string name, string description ) + { + m_name = name; + m_description = description; + + BuildPattern(); + } + + private void BuildPattern() + { + string matchString = Name; + + if ( Aliases != null && Aliases.Length > 0 ) + foreach( string s in Aliases ) + matchString += "|" + s; + + string strPatternStart = @"(\s|^)(?(-{1,2}|/)("; + string strPatternEnd; // To be defined below. + + // The common suffix ensures that the switches are followed by + // a white-space OR the end of the string. This will stop + // switches such as /help matching /helpme + // + string strCommonSuffix = @"(?=(\s|$))"; + + if ( Type == typeof(bool) ) + strPatternEnd = @")(?(\+|-){0,1}))"; + else if ( Type == typeof(string) ) + strPatternEnd = @")(?::|\s+))((?:"")(?.+)(?:"")|(?\S+))"; + else if ( Type == typeof(int) ) + strPatternEnd = @")(?::|\s+))((?(-|\+)[0-9]+)|(?[0-9]+))"; + else if ( Type.IsEnum ) + { + string[] enumNames = Enumerations; + string e_str = enumNames[0]; + for ( int e=1; e" + e_str + @")"; + } + else + throw new System.ArgumentException(); + + // Set the internal regular expression pattern. + m_Pattern = strPatternStart + matchString + strPatternEnd + strCommonSuffix; + } + #endregion + + #region Public Properties + public object Value + { + get + { + if ( ReadValue != null ) + return ReadValue; + else + return m_value; + } + } + + public object InternalValue + { + get { return m_value; } + } + + public string Name + { + get { return m_name; } + set { m_name = value; } + } + + public string Description + { + get { return m_description; } + set { m_description = value; } + } + + public System.Type Type + { + get { return m_switchType; } + } + + public string[] Aliases + { + get { return (m_Aliases != null) ? (string[])m_Aliases.ToArray(typeof(string)): null; } + } + + public string Pattern + { + get { return m_Pattern; } + } + + public System.Reflection.MethodInfo SetMethod + { + set { m_SetMethod = value; } + } + + public System.Reflection.MethodInfo GetMethod + { + set { m_GetMethod = value; } + } + + public object PropertyOwner + { + set { m_PropertyOwner = value; } + } + + public object ReadValue + { + get + { + object o = null; + if ( m_PropertyOwner != null && m_GetMethod != null ) + o = m_GetMethod.Invoke( m_PropertyOwner, null ); + return o; + } + } + + public string[] Enumerations + { + get + { + if ( m_switchType.IsEnum ) + return System.Enum.GetNames( m_switchType ); + else + return null; + } + } + #endregion + + #region Constructors + public SwitchRecord( string name, string description ) + { + Initialize( name, description ); + } + + public SwitchRecord( string name, string description, System.Type type ) + { + if ( type == typeof(bool) || + type == typeof(string) || + type == typeof(int) || + type.IsEnum ) + { + m_switchType = type; + Initialize( name, description ); + } + else + throw new ArgumentException("Currently only Ints, Bool and Strings are supported"); + } + #endregion + + #region Public Methods + public void AddAlias( string alias ) + { + if ( m_Aliases == null ) + m_Aliases = new System.Collections.ArrayList(); + m_Aliases.Add( alias ); + + BuildPattern(); + } + + public void Notify( object value ) + { + if ( m_PropertyOwner != null && m_SetMethod != null ) + { + object[] parameters = new object[1]; + parameters[0] = value; + m_SetMethod.Invoke( m_PropertyOwner, parameters ); + } + m_value = value; + } + + #endregion + } + + + #region Private Variables + private string m_commandLine = ""; + private string m_workingString = ""; + private string m_applicationName = ""; + private string[] m_splitParameters = null; + private System.Collections.ArrayList m_switches = null; + #endregion + + #region Private Utility Functions + private void ExtractApplicationName() + { + Regex r = new Regex(@"^(?("".+""|(\S)+))(?.+)", + RegexOptions.ExplicitCapture); + Match m = r.Match(m_commandLine); + if ( m != null && m.Groups["commandLine"] != null ) + { + m_applicationName = m.Groups["commandLine"].Value; + m_workingString = m.Groups["remainder"].Value; + } + } + + private void SplitParameters() + { + // Populate the split parameters array with the remaining parameters. + // Note that if quotes are used, the quotes are removed. + // e.g. one two three "four five six" + // 0 - one + // 1 - two + // 2 - three + // 3 - four five six + // (e.g. 3 is not in quotes). + Regex r = new Regex(@"((\s*(""(?.+?)""|(?\S+))))", + RegexOptions.ExplicitCapture); + MatchCollection m = r.Matches( m_workingString ); + + if ( m != null ) + { + m_splitParameters = new string[ m.Count ]; + for ( int i=0; i < m.Count; i++ ) + m_splitParameters[i] = m[i].Groups["param"].Value; + } + } + + private void HandleSwitches() + { + if ( m_switches != null ) + { + foreach ( SwitchRecord s in m_switches ) + { + Regex r = new Regex( s.Pattern, + RegexOptions.ExplicitCapture + | RegexOptions.IgnoreCase ); + MatchCollection m = r.Matches( m_workingString ); + if ( m != null ) + { + for ( int i=0; i < m.Count; i++ ) + { + string value = null; + if ( m[i].Groups != null && m[i].Groups["value"] != null ) + value = m[i].Groups["value"].Value; + + if ( s.Type == typeof(bool)) + { + bool state = true; + // The value string may indicate what value we want. + if ( m[i].Groups != null && m[i].Groups["value"] != null ) + { + switch ( value ) + { + case "+": state = true; + break; + case "-": state = false; + break; + case "": if ( s.ReadValue != null ) + state = !(bool)s.ReadValue; + break; + default: break; + } + } + s.Notify( state ); + break; + } + else if ( s.Type == typeof(string) ) + s.Notify( value ); + else if ( s.Type == typeof(int) ) + s.Notify( int.Parse( value ) ); + else if ( s.Type.IsEnum ) + s.Notify( System.Enum.Parse(s.Type,value,true) ); + } + } + + m_workingString = r.Replace(m_workingString, " "); + } + } + } + + #endregion + + #region Public Properties + public string ApplicationName + { + get { return m_applicationName; } + } + + public string[] Parameters + { + get { return m_splitParameters; } + } + + public SwitchInfo[] Switches + { + get + { + if ( m_switches == null ) + return null; + else + { + SwitchInfo[] si = new SwitchInfo[ m_switches.Count ]; + for ( int i=0; iThis function returns a list of the unhandled switches + /// that the parser has seen, but not processed. + /// The unhandled switches are not removed from the remainder + /// of the command-line. + public string[] UnhandledSwitches + { + get + { + string switchPattern = @"(\s|^)(?(-{1,2}|/)(.+?))(?=(\s|$))"; + Regex r = new Regex( switchPattern, + RegexOptions.ExplicitCapture + | RegexOptions.IgnoreCase ); + MatchCollection m = r.Matches( m_workingString ); + + if ( m != null ) + { + string[] unhandled = new string[ m.Count ]; + for ( int i=0; i < m.Count; i++ ) + unhandled[i] = m[i].Groups["match"].Value; + return unhandled; + } + else + return null; + } + } + #endregion + + #region Public Methods + public void AddSwitch( string name, string description ) + { + if ( m_switches == null ) + m_switches = new System.Collections.ArrayList(); + + SwitchRecord rec = new SwitchRecord( name, description ); + m_switches.Add( rec ); + } + + public void AddSwitch( string[] names, string description ) + { + if ( m_switches == null ) + m_switches = new System.Collections.ArrayList(); + SwitchRecord rec = new SwitchRecord( names[0], description ); + for ( int s=1; s 0) + { + SwitchRecord rec = null; + + foreach ( Attribute attribute in attributes ) + { + if ( attribute is CommandParameterAttribute ) + { + CommandParameterAttribute switchAttrib = + (CommandParameterAttribute) attribute; + + // Get the property information. We're only handling + // properties at the moment! + if ( members[i] is System.Reflection.PropertyInfo ) + { + System.Reflection.PropertyInfo pi = (System.Reflection.PropertyInfo) members[i]; + + rec = new SwitchRecord( switchAttrib.Name, + switchAttrib.Description, + pi.PropertyType ); + + // Map in the Get/Set methods. + rec.SetMethod = pi.GetSetMethod(); + rec.GetMethod = pi.GetGetMethod(); + rec.PropertyOwner = classForAutoAttributes; + + // Can only handle a single switch for each property + // (otherwise the parsing of aliases gets silly...) + break; + } + } + } + + // See if any aliases are required. We can only do this after + // a switch has been registered and the framework doesn't make + // any guarantees about the order of attributes, so we have to + // walk the collection a second time. + if ( rec != null ) + { + foreach ( Attribute attribute in attributes ) + { + if (attribute is CommandParameterAliasAttribute) + { + CommandParameterAliasAttribute aliasAttrib = + (CommandParameterAliasAttribute)attribute; + rec.AddAlias( aliasAttrib.Alias ); + } + } + } + + // Assuming we have a switch record (that may or may not have + // aliases), add it to the collection of switches. + if ( rec != null ) + { + if ( m_switches == null ) + m_switches = new System.Collections.ArrayList(); + m_switches.Add( rec ); + } + } + } + } + #endregion + } +} diff --git a/irc/TechBot/TechBot.Library/SambaBugUrl.cs b/irc/TechBot/TechBot.Library/SambaBugUrl.cs deleted file mode 100644 index 6b70fac0aeb..00000000000 --- a/irc/TechBot/TechBot.Library/SambaBugUrl.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TechBot.Library -{ - class SambaBugUrl : BugCommand - { - public SambaBugUrl(TechBotService techBot) - : base(techBot) - { - } - - public override string[] AvailableCommands - { - get { return new string[] { "sambabug" }; } - } - - protected override string BugUrl - { - get { return "https://bugzilla.samba.org/show_bug.cgi?id={0}"; } - } - - public override string Help() - { - return "!sambabug "; - } - } -} diff --git a/irc/TechBot/TechBot.Library/ServiceOutput.cs b/irc/TechBot/TechBot.Library/ServiceOutput.cs index 809cc930819..d73a93e62ea 100644 --- a/irc/TechBot/TechBot.Library/ServiceOutput.cs +++ b/irc/TechBot/TechBot.Library/ServiceOutput.cs @@ -2,9 +2,8 @@ using System; namespace TechBot.Library { - public interface IServiceOutput - { - void WriteLine(MessageContext context, - string message); - } + public interface IServiceOutput + { + void WriteLine(MessageContext context, string message); + } } diff --git a/irc/TechBot/TechBot.Library/SvnCommand.cs b/irc/TechBot/TechBot.Library/SvnCommand.cs deleted file mode 100644 index aef633b2b7b..00000000000 --- a/irc/TechBot/TechBot.Library/SvnCommand.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace TechBot.Library -{ - public class SvnCommand : Command - { - private string m_SvnRoot; - - public SvnCommand(TechBotService techBot) - : base(techBot) - { - m_SvnRoot = Settings.Default.SVNRoot; - } - - public override string[] AvailableCommands - { - get { return new string[] { "svn" }; } - } - - public override void Handle(MessageContext context, - string commandName, - string parameters) - { - TechBot.ServiceOutput.WriteLine(context, string.Format("svn co {0}" , m_SvnRoot)); - } - - public override string Help() - { - return "!svn"; - } - } -} diff --git a/irc/TechBot/TechBot.Library/TechBot.Library.csproj b/irc/TechBot/TechBot.Library/TechBot.Library.csproj index 3db836921f6..9cd39a4a7ff 100644 --- a/irc/TechBot/TechBot.Library/TechBot.Library.csproj +++ b/irc/TechBot/TechBot.Library/TechBot.Library.csproj @@ -39,21 +39,29 @@ --> - - + + + + + + + + - - - - - - - + + + + + + + + + + - + - @@ -61,10 +69,8 @@ True Settings.settings - + - - diff --git a/irc/TechBot/TechBot.Library/IrcService.cs b/irc/TechBot/TechBot.Library/TechBotIrcService.cs similarity index 70% rename from irc/TechBot/TechBot.Library/IrcService.cs rename to irc/TechBot/TechBot.Library/TechBotIrcService.cs index bfd2867df36..ec5f70af967 100644 --- a/irc/TechBot/TechBot.Library/IrcService.cs +++ b/irc/TechBot/TechBot.Library/TechBotIrcService.cs @@ -1,296 +1,267 @@ -using System; -using System.Text; -using System.Collections; -using System.Threading; - -using TechBot.IRCLibrary; - -namespace TechBot.Library -{ - public class IrcService : IServiceOutput - { - private string hostname; - private int port; - private string channelnames; - private string botname; - private string password; - private string chmPath; - private string mainChm; - private string ntstatusXml; - private string winerrorXml; - private string hresultXml; - private string wmXml; - private string svnCommand; - private string bugUrl, WineBugUrl, SambaBugUrl; - private IrcClient m_IrcClient; - private ArrayList channels = new ArrayList(); /* IrcChannel */ - private TechBotService service; - private bool isStopped = false; - - public IrcService(string hostname, - int port, - string channelnames, - string botname, - string password, - string chmPath, - string mainChm, - //string ntstatusXml, - //string winerrorXml, - //string hresultXml, - //string wmXml, - //string svnCommand, - string BugUrl, - string WineBugUrl, - string SambaBugUrl) - { - this.hostname = hostname; - this.port = port; - this.channelnames = channelnames; - this.botname = botname; - if (password == null || password.Trim() == "") - this.password = null; - else - this.password = password; - this.chmPath = chmPath; - this.mainChm = mainChm; - this.ntstatusXml = ntstatusXml; - this.winerrorXml = winerrorXml; - this.hresultXml = hresultXml; - this.wmXml = wmXml; - this.svnCommand = svnCommand; - this.bugUrl = BugUrl; - this.WineBugUrl = WineBugUrl; - this.SambaBugUrl = SambaBugUrl; - } - - public void Run() - { - service = new TechBotService(this, - chmPath, - mainChm); - //ntstatusXml, - //winerrorXml, - //hresultXml, - //wmXml, - //svnCommand, - //bugUrl, - //WineBugUrl, - //SambaBugUrl); - service.Run(); - - m_IrcClient = new IrcClient(); - m_IrcClient.Encoding = Encoding.GetEncoding("iso-8859-1"); - m_IrcClient.MessageReceived += new MessageReceivedHandler(client_MessageReceived); - m_IrcClient.ChannelUserDatabaseChanged += new ChannelUserDatabaseChangedHandler(client_ChannelUserDatabaseChanged); - Console.WriteLine("Connecting to {0} port {1}", - hostname, - port); - m_IrcClient.Connect(hostname, port); - Console.WriteLine("Connected..."); - m_IrcClient.Register(botname, password, null); - Console.WriteLine("Registered as {0}...", botname); - JoinChannels(); - - while (!isStopped) - { - Thread.Sleep(1000); - } - - PartChannels(); - m_IrcClient.Diconnect(); - Console.WriteLine("Disconnected..."); - } - - public void Stop() - { - isStopped = true; - } - - private void JoinChannels() - { - foreach (string channelname in channelnames.Split(new char[] { ';' })) - { - IrcChannel channel = m_IrcClient.JoinChannel(channelname); - channels.Add(channel); - System.Console.WriteLine(String.Format("Joined channel #{0}...", - channel.Name)); - } - } - - private void PartChannels() - { - foreach (IrcChannel channel in channels) - { - m_IrcClient.PartChannel(channel, "Caught in the bitstream..."); - System.Console.WriteLine(String.Format("Parted channel #{0}...", - channel.Name)); - } - } - - private string GetMessageSource(MessageContext context) - { - if (context is ChannelMessageContext) - { - ChannelMessageContext channelContext = context as ChannelMessageContext; - return String.Format("#{0}", - channelContext.Channel.Name); - } - else if (context is UserMessageContext) - { - UserMessageContext userContext = context as UserMessageContext; - return userContext.User.Nickname; - } - else - { - throw new InvalidOperationException(String.Format("Unhandled message context '{0}'", - context.GetType())); - } - } - - public void WriteLine(MessageContext context, - string message) - { - if (context is ChannelMessageContext) - { - ChannelMessageContext channelContext = context as ChannelMessageContext; - channelContext.Channel.Talk(message); - } - else if (context is UserMessageContext) - { - UserMessageContext userContext = context as UserMessageContext; - userContext.User.Talk(message); - } - else - { - throw new InvalidOperationException(String.Format("Unhandled message context '{0}'", - context.GetType())); - } - } - - private void ExtractMessage(string parameters, - out string message) - { - int startIndex = parameters.IndexOf(':'); - if (startIndex != -1) - { - message = parameters.Substring(startIndex + 1); - } - else - { - message = parameters; - } - } - - private bool GetChannelName(IrcMessage message, - out string channelName) - { - if (message.Parameters == null || !message.Parameters.StartsWith("#")) - { - channelName = null; - return false; - } - - int index = message.Parameters.IndexOf(' '); - if (index == -1) - index = message.Parameters.Length; - else - index = index - 1; - channelName = message.Parameters.Substring(1, index); - return true; - } - - private bool GetTargetNickname(IrcMessage message, - out string nickname) - { - if (message.Parameters == null) - { - nickname = null; - return false; - } - - int index = message.Parameters.IndexOf(' '); - if (index == -1) - index = message.Parameters.Length; - nickname = message.Parameters.Substring(0, index); - Console.WriteLine("nickname: " + nickname); - return true; - } - - private bool ShouldAcceptMessage(IrcMessage message, - out MessageContext context) - { - if (message.Command.ToUpper().Equals("PRIVMSG")) - { - string channelName; - string nickname; - if (GetChannelName(message, - out channelName)) - { - foreach (IrcChannel channel in channels) - { - if (String.Compare(channel.Name, channelName, true) == 0) - { - context = new ChannelMessageContext(channel); - return true; - } - } - } - else if (GetTargetNickname(message, - out nickname)) - { - IrcUser targetUser = new IrcUser(m_IrcClient, - nickname); - if (String.Compare(targetUser.Nickname, botname, true) == 0) - { - IrcUser sourceUser = new IrcUser(m_IrcClient, - message.PrefixNickname); - context = new UserMessageContext(sourceUser); - return true; - } - } - } - context = null; - return false; - } - - private void client_MessageReceived(IrcMessage message) - { - try - { - if (message.Command != null && - message.Parameters != null) - { - string injectMessage; - ExtractMessage(message.Parameters, - out injectMessage); - MessageContext context; - if (ShouldAcceptMessage(message, - out context)) - { - Console.WriteLine(String.Format("Injecting: {0} from {1}", - injectMessage, - GetMessageSource(context))); - service.InjectMessage(context, - injectMessage); - } - else - { - Console.WriteLine("Received: " + message.Line); - } - } - else - { - Console.WriteLine("Received: " + message.Line); - } - } - catch (Exception ex) - { - Console.WriteLine(String.Format("Exception: {0}", ex)); - } - } - - private void client_ChannelUserDatabaseChanged(IrcChannel channel) - { - } - } -} +using System; +using System.Text; +using System.Collections; +using System.Threading; + +using TechBot.IRCLibrary; + +namespace TechBot.Library +{ + public class IrcServiceOutput : IServiceOutput + { + public void WriteLine(MessageContext context, + string message) + { + if (context is ChannelMessageContext) + { + ChannelMessageContext channelContext = context as ChannelMessageContext; + channelContext.Channel.Talk(message); + } + else if (context is UserMessageContext) + { + UserMessageContext userContext = context as UserMessageContext; + userContext.User.Talk(message); + } + else + { + throw new InvalidOperationException(String.Format("Unhandled message context '{0}'", + context.GetType())); + } + } + } + + public class IrcTechBotService : TechBotService + { + private int port; + private string hostname; + private string channelnames; + private string botname; + private string password; + private string chmPath; + private string mainChm; + private IrcClient m_IrcClient; + private ArrayList channels = new ArrayList(); + private bool isStopped = false; + + public IrcTechBotService(string hostname, + int port, + string channelnames, + string botname, + string password, + string chmPath, + string mainChm) + : base (new IrcServiceOutput() , chmPath , mainChm) + { + this.hostname = hostname; + this.port = port; + this.channelnames = channelnames; + this.botname = botname; + if (password == null || password.Trim() == "") + this.password = null; + else + this.password = password; + this.chmPath = chmPath; + this.mainChm = mainChm; + } + + public override void Run() + { + //Call the base class + base.Run(); + + m_IrcClient = new IrcClient(); + m_IrcClient.Encoding = Encoding.GetEncoding("iso-8859-1"); + m_IrcClient.MessageReceived += new MessageReceivedHandler(client_MessageReceived); + m_IrcClient.ChannelUserDatabaseChanged += new ChannelUserDatabaseChangedHandler(client_ChannelUserDatabaseChanged); + Console.WriteLine("Connecting to {0} port {1}", + hostname, + port); + m_IrcClient.Connect(hostname, port); + Console.WriteLine("Connected..."); + m_IrcClient.Register(botname, password, null); + Console.WriteLine("Registered as {0}...", botname); + JoinChannels(); + + while (!isStopped) + { + Thread.Sleep(1000); + } + + PartChannels(); + m_IrcClient.Diconnect(); + Console.WriteLine("Disconnected..."); + } + + public void Stop() + { + isStopped = true; + } + + private void JoinChannels() + { + foreach (string channelname in channelnames.Split(new char[] { ';' })) + { + IrcChannel channel = m_IrcClient.JoinChannel(channelname); + channels.Add(channel); + System.Console.WriteLine(String.Format("Joined channel #{0}...", + channel.Name)); + } + } + + private void PartChannels() + { + foreach (IrcChannel channel in channels) + { + m_IrcClient.PartChannel(channel, "Caught in the bitstream..."); + System.Console.WriteLine(String.Format("Parted channel #{0}...", + channel.Name)); + } + } + + private string GetMessageSource(MessageContext context) + { + if (context is ChannelMessageContext) + { + ChannelMessageContext channelContext = context as ChannelMessageContext; + return String.Format("#{0}", + channelContext.Channel.Name); + } + else if (context is UserMessageContext) + { + UserMessageContext userContext = context as UserMessageContext; + return userContext.User.Nickname; + } + else + { + throw new InvalidOperationException(String.Format("Unhandled message context '{0}'", + context.GetType())); + } + } + + private void ExtractMessage(string parameters, + out string message) + { + int startIndex = parameters.IndexOf(':'); + if (startIndex != -1) + { + message = parameters.Substring(startIndex + 1); + } + else + { + message = parameters; + } + } + + private bool GetChannelName(IrcMessage message, + out string channelName) + { + if (message.Parameters == null || !message.Parameters.StartsWith("#")) + { + channelName = null; + return false; + } + + int index = message.Parameters.IndexOf(' '); + if (index == -1) + index = message.Parameters.Length; + else + index = index - 1; + channelName = message.Parameters.Substring(1, index); + return true; + } + + private bool GetTargetNickname(IrcMessage message, + out string nickname) + { + if (message.Parameters == null) + { + nickname = null; + return false; + } + + int index = message.Parameters.IndexOf(' '); + if (index == -1) + index = message.Parameters.Length; + nickname = message.Parameters.Substring(0, index); + Console.WriteLine("nickname: " + nickname); + return true; + } + + private bool ShouldAcceptMessage(IrcMessage message, + out MessageContext context) + { + if (message.Command.ToUpper().Equals("PRIVMSG")) + { + string channelName; + string nickname; + if (GetChannelName(message, + out channelName)) + { + foreach (IrcChannel channel in channels) + { + if (String.Compare(channel.Name, channelName, true) == 0) + { + context = new ChannelMessageContext(channel); + return true; + } + } + } + else if (GetTargetNickname(message, + out nickname)) + { + IrcUser targetUser = new IrcUser(m_IrcClient, + nickname); + if (String.Compare(targetUser.Nickname, botname, true) == 0) + { + IrcUser sourceUser = new IrcUser(m_IrcClient, + message.PrefixNickname); + context = new UserMessageContext(sourceUser); + return true; + } + } + } + context = null; + return false; + } + + private void client_MessageReceived(IrcMessage message) + { + try + { + if (message.Command != null && + message.Parameters != null) + { + string injectMessage; + ExtractMessage(message.Parameters, + out injectMessage); + MessageContext context; + if (ShouldAcceptMessage(message, + out context)) + { + Console.WriteLine(String.Format("Injecting: {0} from {1}", + injectMessage, + GetMessageSource(context))); + InjectMessage(context, + injectMessage); + } + else + { + Console.WriteLine("Received: " + message.Line); + } + } + else + { + Console.WriteLine("Received: " + message.Line); + } + } + catch (Exception ex) + { + Console.WriteLine(String.Format("Exception: {0}", ex)); + } + } + + private void client_ChannelUserDatabaseChanged(IrcChannel channel) + { + } + } +} diff --git a/irc/TechBot/TechBot.Library/TechBotService.cs b/irc/TechBot/TechBot.Library/TechBotService.cs index d40ab91e2c7..bb0bb5563d8 100644 --- a/irc/TechBot/TechBot.Library/TechBotService.cs +++ b/irc/TechBot/TechBot.Library/TechBotService.cs @@ -4,63 +4,29 @@ using System.Collections.Generic; using System.IO; using System.Data; using System.Threading; + using TechBot.IRCLibrary; namespace TechBot.Library { - public class TechBotService + public abstract class TechBotService { - private IServiceOutput serviceOutput; + protected IServiceOutput serviceOutput; private string chmPath; private string mainChm; - private string ntstatusXml; - private string winerrorXml; - private string hresultXml; - private string wmXml; - private string svnCommand; - private string bugUrl, WineBugUrl, SambaBugUrl; - private List commands = new List(); public TechBotService(IServiceOutput serviceOutput, string chmPath, string mainChm) - //string ntstatusXml, - //string winerrorXml, - //string hresultXml, - //string wmXml, - //string svnCommand, - //string bugUrl, - //string WineBugUrl, - //string SambaBugUrl) { this.serviceOutput = serviceOutput; this.chmPath = chmPath; this.mainChm = mainChm; - this.ntstatusXml = ntstatusXml; - this.winerrorXml = winerrorXml; - this.hresultXml = hresultXml; - this.wmXml = wmXml; - this.svnCommand = svnCommand; - this.bugUrl = bugUrl; - this.WineBugUrl = WineBugUrl; - this.SambaBugUrl = SambaBugUrl; } - public void Run() + public virtual void Run() { - commands.Add(new HelpCommand(this)); - /*commands.Add(new ApiCommand(serviceOutput, - chmPath, - mainChm));*/ - commands.Add(new NtStatusCommand(this)); - commands.Add(new WinerrorCommand(this)); - commands.Add(new HResultCommand(this)); - commands.Add(new ErrorCommand(this)); - commands.Add(new WMCommand(this)); - commands.Add(new SvnCommand(this)); - commands.Add(new ReactOSBugUrl(this)); - commands.Add(new SambaBugUrl(this)); - commands.Add(new WineBugUrl(this)); + CommandFactory.LoadPlugins(); } public IServiceOutput ServiceOutput @@ -68,18 +34,16 @@ namespace TechBot.Library get { return serviceOutput; } } - public IList Commands + public CommandBuilderCollection Commands { - get { return commands; } + get { return CommandFactory.Commands; } + } + + public void InjectMessage(MessageContext context, string message) + { + ParseCommandMessage(context, + message); } - - public void InjectMessage(MessageContext context, - string message) - { - if (message.StartsWith("!")) - ParseCommandMessage(context, - message); - } private bool IsCommandMessage(string message) { @@ -104,19 +68,21 @@ namespace TechBot.Library else commandName = message.Trim(); - foreach (Command command in commands) - { - foreach (string cmd in command.AvailableCommands) + foreach (CommandBuilder command in Commands) + { + if (command.Name == commandName) { - if (cmd == commandName) - { - command.Handle(context, - commandName, - commandParams); - return; - } + //Create a new instance of the required command type + Command cmd = command.CreateCommand(); + + cmd.TechBot = this; + cmd.Context = context; + cmd.ParseParameters(message); + cmd.ExecuteCommand(); + + return; } - } + } } } } diff --git a/irc/TechBot/TechBot.Library/WineBugUrl.cs b/irc/TechBot/TechBot.Library/WineBugUrl.cs deleted file mode 100644 index 94464a1fdc8..00000000000 --- a/irc/TechBot/TechBot.Library/WineBugUrl.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TechBot.Library -{ - class WineBugUrl : BugCommand - { - public WineBugUrl(TechBotService techBot) - : base(techBot) - { - } - - public override string[] AvailableCommands - { - get { return new string[] { "winebug" }; } - } - - protected override string BugUrl - { - get { return "http://bugs.winehq.org/show_bug.cgi?id={0}"; } - } - - public override string Help() - { - return "!winebug "; - } - } -} diff --git a/irc/TechBot/TechBot.cmbx b/irc/TechBot/TechBot.cmbx deleted file mode 100644 index 3e8915e180a..00000000000 --- a/irc/TechBot/TechBot.cmbx +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/irc/TechBot/TechBot/ProjectInstaller.cs b/irc/TechBot/TechBot/ProjectInstaller.cs new file mode 100644 index 00000000000..0dc2b152a6e --- /dev/null +++ b/irc/TechBot/TechBot/ProjectInstaller.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel; +using System.ServiceProcess; +using System.Configuration.Install; +using System.Collections.Generic; +using System.Text; + +namespace TechBot +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public ProjectInstaller() + { + ServiceProcessInstaller spi = null; + ServiceInstaller si = null; + + spi = new ServiceProcessInstaller(); + spi.Account = ServiceAccount.LocalSystem; + + si = new ServiceInstaller(); + si.ServiceName = "TechBot"; + si.StartType = ServiceStartMode.Automatic; + + Installers.AddRange(new Installer[] { spi, si }); + } + } +} diff --git a/irc/TechBot/TechBot/ServiceThread.cs b/irc/TechBot/TechBot/ServiceThread.cs index 9e22beb25fb..0675bca22b6 100644 --- a/irc/TechBot/TechBot/ServiceThread.cs +++ b/irc/TechBot/TechBot/ServiceThread.cs @@ -51,21 +51,21 @@ namespace TechBot SetupConfiguration(); System.Console.WriteLine("TechBot irc service..."); - IrcService ircService = new IrcService(IRCServerHostName, + IrcTechBotService ircService = new IrcTechBotService(IRCServerHostName, IRCServerHostPort, IRCChannelNames, IRCBotName, IRCBotPassword, ChmPath, - MainChm, + MainChm); //NtstatusXml, //WinerrorXml, //HresultXml, //WmXml, //SvnCommand, - BugUrl, - WineBugUrl, - SambaBugUrl); + //BugUrl, + //WineBugUrl, + //SambaBugUrl); ircService.Run(); } diff --git a/irc/TechBot/TechBot/TechBot.csproj b/irc/TechBot/TechBot/TechBot.csproj index 692e4739541..85fc4791e03 100644 --- a/irc/TechBot/TechBot/TechBot.csproj +++ b/irc/TechBot/TechBot/TechBot.csproj @@ -42,6 +42,9 @@ + + Component + Component @@ -52,6 +55,7 @@ + diff --git a/irc/TechBot/TechBot/TechBotService.cs b/irc/TechBot/TechBot/TechBotService.cs index 79fca2d38d3..362ce849d0f 100644 --- a/irc/TechBot/TechBot/TechBotService.cs +++ b/irc/TechBot/TechBot/TechBotService.cs @@ -80,18 +80,3 @@ namespace TechBot } } } - -[RunInstaller(true)] -public class ProjectInstaller : Installer -{ - public ProjectInstaller() - { - ServiceProcessInstaller spi = new ServiceProcessInstaller(); - spi.Account = ServiceAccount.LocalSystem; - - ServiceInstaller si = new ServiceInstaller(); - si.ServiceName = "TechBot"; - si.StartType = ServiceStartMode.Automatic; - Installers.AddRange(new Installer[] {spi, si}); - } -}