- code refactoring
authorMarc Piulachs <marc.piulachs@live.com>
Wed, 7 May 2008 14:59:28 +0000 (14:59 +0000)
committerMarc Piulachs <marc.piulachs@live.com>
Wed, 7 May 2008 14:59:28 +0000 (14:59 +0000)
- 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

42 files changed:
irc/TechBot/Default.build [deleted file]
irc/TechBot/TechBot.Console/ConsoleTechBotService.cs [new file with mode: 0644]
irc/TechBot/TechBot.Console/Main.cs
irc/TechBot/TechBot.Console/TechBot.Console.csproj
irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Attributes/CommandParameterAliasAttribute.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Attributes/CommandParameterAttribute.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/BugCommand.cs [deleted file]
irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Command.cs [deleted file]
irc/TechBot/TechBot.Library/Commands/ApiCommand.cs [moved from irc/TechBot/TechBot.Library/ApiCommand.cs with 91% similarity]
irc/TechBot/TechBot.Library/Commands/Base/Command.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/Base/XmlCommand.cs
irc/TechBot/TechBot.Library/Commands/Base/XmlLookupCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/BugCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/ErrorCommand.cs [moved from irc/TechBot/TechBot.Library/ErrorCommand.cs with 81% similarity]
irc/TechBot/TechBot.Library/Commands/HelpCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/HresultCommand.cs [moved from irc/TechBot/TechBot.Library/HresultCommand.cs with 50% similarity]
irc/TechBot/TechBot.Library/Commands/NtStatusCommand.cs [moved from irc/TechBot/TechBot.Library/NtStatusCommand.cs with 51% similarity]
irc/TechBot/TechBot.Library/Commands/ReactOSBugUrl.cs [moved from irc/TechBot/TechBot.Library/ReactOSBugUrl.cs with 50% similarity]
irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/SvnCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/WineBugUrl.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Commands/WinerrorCommand.cs [moved from irc/TechBot/TechBot.Library/WinerrorCommand.cs with 51% similarity]
irc/TechBot/TechBot.Library/Commands/WmCommand.cs [moved from irc/TechBot/TechBot.Library/WmCommand.cs with 65% similarity]
irc/TechBot/TechBot.Library/Factory/CommandBuilder.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Factory/CommandFactory.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/HelpCommand.cs [deleted file]
irc/TechBot/TechBot.Library/MessageContext.cs
irc/TechBot/TechBot.Library/ParametersParser.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/SambaBugUrl.cs [deleted file]
irc/TechBot/TechBot.Library/ServiceOutput.cs
irc/TechBot/TechBot.Library/SvnCommand.cs [deleted file]
irc/TechBot/TechBot.Library/TechBot.Library.csproj
irc/TechBot/TechBot.Library/TechBotIrcService.cs [moved from irc/TechBot/TechBot.Library/IrcService.cs with 70% similarity]
irc/TechBot/TechBot.Library/TechBotService.cs
irc/TechBot/TechBot.Library/WineBugUrl.cs [deleted file]
irc/TechBot/TechBot.cmbx [deleted file]
irc/TechBot/TechBot/ProjectInstaller.cs [new file with mode: 0644]
irc/TechBot/TechBot/ServiceThread.cs
irc/TechBot/TechBot/TechBot.csproj
irc/TechBot/TechBot/TechBotService.cs

diff --git a/irc/TechBot/Default.build b/irc/TechBot/Default.build
deleted file mode 100644 (file)
index 62738cf..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0"?>\r
-<project name="TechBot" default="build">\r
-\r
-       <target name="build" description="Build components">\r
-               <delete dir="bin" failonerror="false" />\r
-               <nant buildfile="Compression/Default.build" />\r
-               <nant buildfile="CHMLibrary/Default.build" />\r
-               <nant buildfile="TechBot.IRCLibrary/Default.build" />\r
-               <nant buildfile="TechBot.Library/Default.build" />\r
-               <nant buildfile="TechBot.Console/Default.build" />\r
-               <nant buildfile="TechBot/Default.build" />\r
-       </target>\r
-\r
-</project>\r
diff --git a/irc/TechBot/TechBot.Console/ConsoleTechBotService.cs b/irc/TechBot/TechBot.Console/ConsoleTechBotService.cs
new file mode 100644 (file)
index 0000000..14c3c84
--- /dev/null
@@ -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);
+            }
+        }
+    }
+}
index 6826558..88760ac 100644 (file)
@@ -4,16 +4,6 @@ using TechBot.Library;
 \r
 namespace TechBot.Console\r
 {\r
-       public class ConsoleServiceOutput : IServiceOutput\r
-       {\r
-               public void WriteLine(MessageContext context,\r
-                                     string message)\r
-               {\r
-                       System.Console.WriteLine(message);\r
-               }\r
-       }\r
-\r
-       \r
        class MainClass\r
        {\r
                private static void VerifyRequiredOption(string optionName,\r
@@ -208,53 +198,43 @@ namespace TechBot.Console
                }\r
 \r
 \r
-               private static void RunIrcService()\r
-               {\r
-                       IrcService ircService = new IrcService(IRCServerHostName,\r
-                                                              IRCServerHostPort,\r
-                                                              IRCChannelNames,\r
-                                                              IRCBotName,\r
-                                                              IRCBotPassword,\r
-                                                              ChmPath,\r
-                                                              MainChm,\r
-                                                   //NtstatusXml,\r
-                                                   //WinerrorXml,\r
-                                                   //HresultXml,\r
-                                                   //WmXml,\r
-                                                              //SvnCommand,\r
-                                                              BugUrl,\r
-                                                              WineBugUrl,\r
-                                                              SambaBugUrl);\r
-                       ircService.Run();\r
-               }\r
-               \r
-               public static void Main(string[] args)\r
-               {\r
-                       if (args.Length > 0 && args[0].ToLower().Equals("irc"))\r
-                       {\r
-                               RunIrcService();\r
-                               return;\r
-                       }\r
-                       \r
-                       System.Console.WriteLine("TechBot running console service...");\r
-                       TechBotService service = new TechBotService(new ConsoleServiceOutput(),\r
-                                                                   ChmPath,\r
-                                                                   MainChm);\r
-                                                        //NtstatusXml,\r
-                                                        //WinerrorXml,\r
-                                                        //HresultXml,\r
-                                                        //WmXml,\r
-                                                                   //SvnCommand,\r
-                                                        //BugUrl,\r
-                                                        //WineBugUrl,\r
-                                                        //SambaBugUrl);\r
-                       service.Run();\r
-                       while (true)\r
-                       {\r
-                               string s = System.Console.ReadLine();\r
-                               service.InjectMessage(null,\r
-                                                     s);\r
-                       }\r
-               }\r
+        //private static void RunIrcService()\r
+        //{\r
+        //    IrcTechBotService ircService = new IrcTechBotService(IRCServerHostName,\r
+        //                                           IRCServerHostPort,\r
+        //                                           IRCChannelNames,\r
+        //                                           IRCBotName,\r
+        //                                           IRCBotPassword,\r
+        //                                           ChmPath,\r
+        //                                           MainChm);\r
+        //    ircService.Run();\r
+        //}\r
+\r
+        public static void Main(string[] args)\r
+        {\r
+            TechBotService m_TechBot = null;\r
+\r
+            if (args.Length > 0 && args[0].ToLower().Equals("irc"))\r
+            {\r
+                m_TechBot = new IrcTechBotService(IRCServerHostName,\r
+                                                                   IRCServerHostPort,\r
+                                                                   IRCChannelNames,\r
+                                                                   IRCBotName,\r
+                                                                   IRCBotPassword,\r
+                                                                   ChmPath,\r
+                                                                   MainChm);\r
+            }\r
+            else\r
+            {\r
+                System.Console.WriteLine("TechBot running console service...");\r
+                m_TechBot = new ConsoleTechBotService(\r
+                                                            ChmPath,\r
+                                                            MainChm);\r
+\r
+\r
+            }\r
+\r
+            m_TechBot.Run();\r
+        }\r
        }\r
-}\r
+}
\ No newline at end of file
index 1044ed8..4c0b6f7 100644 (file)
@@ -38,6 +38,7 @@
   -->
   <ItemGroup>
     <Compile Include="AssemblyInfo.cs" />
+    <Compile Include="ConsoleTechBotService.cs" />
     <Compile Include="Main.cs" />
   </ItemGroup>
   <ItemGroup>
diff --git a/irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs b/irc/TechBot/TechBot.Library/Attributes/CommandAttribute.cs
new file mode 100644 (file)
index 0000000..a16ee78
--- /dev/null
@@ -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 (file)
index 0000000..05dc10d
--- /dev/null
@@ -0,0 +1,34 @@
+using System;
+
+namespace TechBot.Library
+{
+       /// <summary>
+       /// This class implements an alias attribute to work in conjunction
+       /// with the <see cref="CommandLineSwitchAttribute">CommandLineSwitchAttribute</see>
+       /// attribute.  If the CommandLineSwitchAttribute exists, then this attribute
+       /// defines an alias for it.
+       /// </summary>
+       [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 (file)
index 0000000..bdb3a80
--- /dev/null
@@ -0,0 +1,43 @@
+using System;
+
+namespace TechBot.Library
+{
+       /// <summary>Implements a basic command-line switch by taking the
+       /// switching name and the associated description.</summary>
+       /// <remark>Only currently is implemented for properties, so all
+       /// auto-switching variables should have a get/set method supplied.</remark>
+       [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
+               /// <summary>Accessor for retrieving the switch-name for an associated
+               /// property.</summary>
+               public string Name                      { get { return m_name; } }
+
+               /// <summary>Accessor for retrieving the description for a switch of
+               /// an associated property.</summary>
+               public string Description       { get { return m_description; } }
+
+        public bool Required { get { return m_Required; } }
+
+               #endregion
+
+               #region Constructors
+               
+        /// <summary>
+        /// Attribute constructor.
+        /// </summary>
+        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 (file)
index 9942738..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public abstract class BugCommand : Command//, ICommand\r
-       {\r
-               public BugCommand(TechBotService techBot) : base (techBot)\r
-               {\r
-               }\r
-\r
-        public override void Handle(MessageContext context,\r
-                           string commandName,\r
-                           string parameters)\r
-        {\r
-            string bugText = parameters;\r
-            if (bugText.Equals(String.Empty))\r
-            {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                        "Please provide a valid bug number.");\r
-                return;\r
-            }\r
-\r
-            NumberParser np = new NumberParser();\r
-            long bug = np.Parse(bugText);\r
-            if (np.Error)\r
-            {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                        String.Format("{0} is not a valid bug number.",\r
-                                                      bugText));\r
-                return;\r
-            }\r
-\r
-            /*\r
-            string bugUrl = this.RosBugUrl;\r
-\r
-            if (context is ChannelMessageContext)\r
-            {\r
-                ChannelMessageContext channelContext = context as ChannelMessageContext;\r
-                if (channelContext.Channel.Name == "winehackers")\r
-                    bugUrl = this.WineBugUrl;\r
-                else if (channelContext.Channel.Name == "samba-technical")\r
-                    bugUrl = this.SambaBugUrl;\r
-            }*/\r
-\r
-            TechBot.ServiceOutput.WriteLine(context, String.Format(BugUrl, bug));\r
-        }\r
-\r
-        protected abstract string BugUrl { get; }\r
-       }\r
-}\r
diff --git a/irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs b/irc/TechBot/TechBot.Library/Collections/CommandBuilderCollection.cs
new file mode 100644 (file)
index 0000000..6cb4a81
--- /dev/null
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TechBot.Library
+{
+    public class CommandBuilderCollection : List<CommandBuilder>
+    {
+        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 (file)
index b6ae0a1..0000000
+++ /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";
-        }
-    }
-}
-using System;\r
-using System.IO;\r
-using System.Data;\r
-using System.Text.RegularExpressions;\r
-using HtmlHelp;\r
-using HtmlHelp.ChmDecoding;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class ApiCommand : Command\r
-       {\r
-               private const bool IsVerbose = false;\r
-\r
-               private HtmlHelpSystem chm;\r
-               private string chmPath;\r
-               private string mainChm;\r
-\r
-        public ApiCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-                       Run();\r
-               }\r
-               \r
-               private void WriteIfVerbose(MessageContext context,\r
-                                           string message)\r
-               {\r
-                       if (IsVerbose)\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       message);\r
-               }\r
-\r
-               private void Run()\r
-               {\r
-                       string CHMFilename = Path.Combine(chmPath, mainChm);\r
-                       chm = new HtmlHelpSystem();\r
-                       chm.OpenFile(CHMFilename, null);\r
-                       \r
-                       Console.WriteLine(String.Format("Loaded main CHM: {0}",\r
-                                                       Path.GetFileName(CHMFilename)));\r
-                       foreach (string filename in Directory.GetFiles(chmPath))\r
-                       {\r
-                               if (!Path.GetExtension(filename).ToLower().Equals(".chm"))\r
-                                       continue;\r
-                               if (Path.GetFileName(filename).ToLower().Equals(mainChm))\r
-                                       continue;\r
-\r
-                               Console.WriteLine(String.Format("Loading CHM: {0}",\r
-                                                               Path.GetFileName(filename)));\r
-                               try\r
-                               {\r
-                                       chm.MergeFile(filename);\r
-                               }\r
-                               catch (Exception ex)\r
-                               {\r
-                                       Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",\r
-                                                                       Path.GetFileName(filename),\r
-                                                                       ex));\r
-                               }\r
-                       }\r
-                       Console.WriteLine(String.Format("Loaded {0} CHMs",\r
-                                                       chm.FileList.Length));\r
-               }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "api" }; }\r
-        }\r
-\r
-        /*\r
-               public bool CanHandle(string commandName)\r
-               {\r
-                       return CanHandle(commandName,\r
-                                        new string[] { "api" });\r
-               }\r
-*/\r
-               \r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       if (parameters.Trim().Equals(String.Empty))\r
-                               DisplayNoKeyword(context);\r
-                       else\r
-                               Search(context,\r
-                                      parameters);\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!api <apiname>";\r
-               }\r
-               \r
-               private bool SearchIndex(MessageContext context,\r
-                                        string keyword)\r
-               {\r
-                       if (chm.HasIndex)\r
-                       {\r
-                               IndexItem item = chm.Index.SearchIndex(keyword,\r
-                                                                      IndexType.KeywordLinks);\r
-                               if (item != null && item.Topics.Count > 0)\r
-                               {\r
-                                       WriteIfVerbose(context,\r
-                                                      String.Format("Keyword {0} found in index",\r
-                                                                    item.KeyWord));\r
-                                       IndexTopic indexTopic = item.Topics[0] as IndexTopic;\r
-                                       return DisplayResult(context,\r
-                                                            keyword,\r
-                                                            indexTopic);\r
-                               }\r
-                               else\r
-                               {\r
-                                       WriteIfVerbose(context,\r
-                                                      String.Format("Keyword {0} not found in index",\r
-                                                                    keyword));\r
-                                       return false;\r
-                               }\r
-                       }\r
-                       else\r
-                               return false;\r
-               }\r
-\r
-               private void SearchFullText(MessageContext context,\r
-                                           string keyword)\r
-               {\r
-                       string sort = "Rating ASC";\r
-                       WriteIfVerbose(context,\r
-                                      String.Format("Searching fulltext database for {0}",\r
-                                         keyword));\r
-\r
-                       bool partialMatches = false;\r
-                       bool titlesOnly = true;\r
-                       int maxResults = 100;\r
-                       DataTable results = chm.PerformSearch(keyword,\r
-                                                             maxResults,\r
-                                                             partialMatches,\r
-                                                             titlesOnly);\r
-                       WriteIfVerbose(context,\r
-                                      String.Format("results.Rows.Count = {0}",\r
-                                                    results != null ?\r
-                                                    results.Rows.Count.ToString() : "(none)"));\r
-                       if (results != null && results.Rows.Count > 0)\r
-                       {\r
-                               results.DefaultView.Sort = sort;\r
-                               if (!DisplayResult(context,\r
-                                                  keyword,\r
-                                                  results))\r
-                               {\r
-                                       DisplayNoResult(context,\r
-                                                       keyword);\r
-                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               DisplayNoResult(context,\r
-                                               keyword);\r
-                       }\r
-               }\r
-\r
-               private void Search(MessageContext context,\r
-                                   string keyword)\r
-               {\r
-                       if (!SearchIndex(context,\r
-                                        keyword))\r
-                               SearchFullText(context,\r
-                                              keyword);\r
-               }\r
-               \r
-               private bool DisplayResult(MessageContext context,\r
-                                          string keyword,\r
-                                          IndexTopic indexTopic)\r
-               {\r
-                       keyword = keyword.Trim().ToLower();\r
-                       string url = indexTopic.URL;\r
-                       WriteIfVerbose(context,\r
-                                      String.Format("URL from index search {0}",\r
-                                         url));\r
-                       string prototype = ExtractPrototype(context,\r
-                                                           url);\r
-                       if (prototype == null || prototype.Trim().Equals(String.Empty))\r
-                               return false;\r
-                       string formattedPrototype = FormatPrototype(prototype);\r
-            TechBot.ServiceOutput.WriteLine(context,\r
-                                               formattedPrototype);\r
-                       return true;\r
-               }\r
-               \r
-               private bool DisplayResult(MessageContext context,\r
-                                          string keyword,\r
-                                          DataTable results)\r
-               {\r
-                       keyword = keyword.Trim().ToLower();\r
-                       for (int i = 0; i < results.DefaultView.Count; i++)\r
-                       {\r
-                               DataRowView row = results.DefaultView[i];\r
-                               string title = row["Title"].ToString();\r
-                               WriteIfVerbose(context,\r
-                                              String.Format("Examining {0}", title));\r
-                               if (title.Trim().ToLower().Equals(keyword))\r
-                               {\r
-                                       string location = row["Location"].ToString();\r
-                                       string rating = row["Rating"].ToString();\r
-                                       string url = row["Url"].ToString();\r
-                                       string prototype = ExtractPrototype(context,\r
-                                                                           url);\r
-                                       if (prototype == null || prototype.Trim().Equals(String.Empty))\r
-                                               continue;\r
-                                       string formattedPrototype = FormatPrototype(prototype);\r
-                    TechBot.ServiceOutput.WriteLine(context,\r
-                                                               formattedPrototype);\r
-                                       return true;\r
-                               }\r
-                       }\r
-                       return false;\r
-               }\r
-\r
-               private void DisplayNoResult(MessageContext context,\r
-                                            string keyword)\r
-               {\r
-            TechBot.ServiceOutput.WriteLine(context,\r
-                                               String.Format("I don't know about keyword {0}",\r
-                                                             keyword));\r
-               }\r
-\r
-               private void DisplayNoKeyword(MessageContext context)\r
-               {\r
-            TechBot.ServiceOutput.WriteLine(context,\r
-                                               "Please give me a keyword.");\r
-               }\r
-\r
-               private string ReplaceComments(string s)\r
-               {\r
-                       return Regex.Replace(s, "//(.+)\r\n", "");\r
-               }\r
-\r
-               private string ReplaceLineEndings(string s)\r
-               {\r
-                       return Regex.Replace(s, "(\r\n)+", " ");\r
-               }\r
-\r
-               private string ReplaceSpaces(string s)\r
-               {\r
-                       return Regex.Replace(s, @" +", " ");\r
-               }\r
-               \r
-               private string ReplaceSpacesBeforeLeftParenthesis(string s)\r
-               {\r
-                       return Regex.Replace(s, @"\( ", @"(");\r
-               }\r
-\r
-               private string ReplaceSpacesBeforeRightParenthesis(string s)\r
-               {\r
-                       return Regex.Replace(s, @" \)", @")");\r
-               }\r
-\r
-               private string ReplaceSemicolon(string s)\r
-               {\r
-                       return Regex.Replace(s, @";", @"");\r
-               }\r
-\r
-               private string FormatPrototype(string prototype)\r
-               {\r
-                       string s = ReplaceComments(prototype);\r
-                       s = ReplaceLineEndings(s);\r
-                       s = ReplaceSpaces(s);\r
-                       s = ReplaceSpacesBeforeLeftParenthesis(s);\r
-                       s = ReplaceSpacesBeforeRightParenthesis(s);\r
-                       s = ReplaceSemicolon(s);\r
-                       return s;\r
-               }\r
-               \r
-               private string ExtractPrototype(MessageContext context,\r
-                                               string url)\r
-               {\r
-                       string page = GetPage(context,\r
-                                             url);\r
-                       Match match = Regex.Match(page,\r
-                                                 "<PRE class=\"?syntax\"?>(.+)</PRE>",\r
-                                                 RegexOptions.Multiline |\r
-                                                 RegexOptions.Singleline);\r
-                       if (match.Groups.Count > 1)\r
-                       {\r
-                               string prototype = match.Groups[1].ToString();\r
-                               return StripHtml(StripAfterSlashPre(prototype));\r
-                       }\r
-                       \r
-                       return "";\r
-               }\r
-               \r
-               private string StripAfterSlashPre(string html)\r
-               {\r
-                       int index = html.IndexOf("</PRE>");\r
-                       if (index != -1)\r
-                       {\r
-                               return html.Substring(0, index);\r
-                       }\r
-                       else\r
-                               return html;\r
-               }\r
-               \r
-               private string StripHtml(string html)\r
-               {\r
-                       return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);\r
-               }\r
-\r
-               private string GetPage(MessageContext context,\r
-                                      string url)\r
-               {\r
-                       string CHMFileName = "";\r
-                       string topicName = "";\r
-                       string anchor = "";\r
-                       CHMStream.CHMStream baseStream;\r
-                       if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))\r
-                       {\r
-                               baseStream = chm.BaseStream;\r
-                               CHMFileName = baseStream.CHMFileName;\r
-                               topicName = url;\r
-                               anchor = "";\r
-                       }\r
-                       else\r
-                       {\r
-                               baseStream = GetBaseStreamFromCHMFileName(context,\r
-                                                                         CHMFileName);\r
-                       }\r
-\r
-                       if ((topicName == "") || (CHMFileName == "") || (baseStream == null))\r
-                       {\r
-                               return "";\r
-                       }\r
-\r
-                       return baseStream.ExtractTextFile(topicName);\r
-               }\r
-\r
-               private CHMStream.CHMStream GetBaseStreamFromCHMFileName(MessageContext context,\r
-                                                                        string CHMFileName)\r
-               {\r
-                       foreach (CHMFile file in chm.FileList)\r
-                       {\r
-                               WriteIfVerbose(context,\r
-                                              String.Format("Compare: {0} <> {1}",\r
-                                                            file.ChmFilePath,\r
-                                                            CHMFileName));\r
-                               if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))\r
-                               {\r
-                                       return file.BaseStream;\r
-                               }\r
-                       }\r
-                       WriteIfVerbose(context,\r
-                                      String.Format("Could not find loaded CHM file in list: {0}",\r
-                                                    CHMFileName));\r
-                       return null;\r
-               }\r
-       }\r
-}\r
+using System;
+using System.IO;
+using System.Data;
+using System.Text.RegularExpressions;
+using HtmlHelp;
+using HtmlHelp.ChmDecoding;
+
+namespace TechBot.Library
+{
+    [Command("api", Help = "!api <apiname>")]
+       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,
+                                                 "<PRE class=\"?syntax\"?>(.+)</PRE>",
+                                                 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("</PRE>");
+                       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 (file)
index 0000000..839475b
--- /dev/null
@@ -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();
+    }
+}
index f5d39df..673e796 100644 (file)
@@ -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 (file)
index 0000000..547badb
--- /dev/null
@@ -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 (file)
index 0000000..1beb5fc
--- /dev/null
@@ -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; }
+       }
+}
-using System;\r
-using System.Xml;\r
-using System.Collections;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class ErrorCommand : Command\r
-       {\r
-               private NtStatusCommand ntStatus;\r
-               private WinerrorCommand winerror;\r
-               private HResultCommand hresult;\r
-\r
-        public ErrorCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-                       this.ntStatus = new NtStatusCommand(techBot);\r
-                       this.winerror = new WinerrorCommand(techBot);\r
-                       this.hresult = new HResultCommand(techBot);\r
-               }\r
-               \r
-        /*\r
-               public bool CanHandle(string commandName)\r
-               {\r
-                       return CanHandle(commandName,\r
-                                        new string[] { "error" });\r
-               }\r
-        */\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "error" }; }\r
-        }\r
-\r
-               private static int GetSeverity(long error)\r
-               {\r
-                       return (int)((error >> 30) & 0x3);\r
-               }\r
-\r
-               private static bool IsCustomer(long error)\r
-               {\r
-                       return (error & 0x20000000) != 0;\r
-               }\r
-               \r
-               private static bool IsReserved(long error)\r
-               {\r
-                       return (error & 0x10000000) != 0;\r
-               }\r
-\r
-               private static int GetFacility(long error)\r
-               {\r
-                       return (int)((error >> 16) & 0xFFF);\r
-               }\r
-               \r
-               private static short GetCode(long error)\r
-               {\r
-                       return (short)((error >> 0) & 0xFFFF);\r
-               }\r
-\r
-               private static string FormatSeverity(long error)\r
-               {\r
-                       int severity = GetSeverity(error);\r
-                       switch (severity)\r
-                       {\r
-                       case 0: return "SUCCESS";\r
-                       case 1: return "INFORMATIONAL";\r
-                       case 2: return "WARNING";\r
-                       case 3: return "ERROR";\r
-                       }\r
-                       return null;\r
-               }\r
-\r
-               private static string FormatFacility(long error)\r
-               {\r
-                       int facility = GetFacility(error);\r
-                       return facility.ToString();\r
-               }\r
-\r
-               private static string FormatCode(long error)\r
-               {\r
-                       int code = GetCode(error);\r
-                       return code.ToString();\r
-               }\r
-\r
-               public override  void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       string originalErrorText = parameters.Trim();\r
-                       if (originalErrorText.Equals(String.Empty))\r
-                       {\r
-                               TechBot.ServiceOutput.WriteLine(context,\r
-                                                       "Please provide an Error Code.");\r
-                               return;\r
-                       }\r
-                       \r
-                       string errorText = originalErrorText;\r
-\r
-               retry:\r
-                       NumberParser np = new NumberParser();\r
-                       long error = np.Parse(errorText);\r
-                       if (np.Error)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is not a valid Error Code.",\r
-                                                                                                         originalErrorText));\r
-                               return;\r
-                       }\r
-                       \r
-                       ArrayList descriptions = new ArrayList();\r
-\r
-                       // Error is out of bounds\r
-                       if ((ulong)error > uint.MaxValue)\r
-                       {\r
-                               // Do nothing\r
-                       }\r
-                       // Error is outside of the range [0, 65535]: it cannot be a plain Win32 error code\r
-                       else if ((ulong)error > ushort.MaxValue)\r
-                       {\r
-                               // Customer bit is set: custom error code\r
-                               if (IsCustomer(error))\r
-                               {\r
-                                       string description = String.Format("[custom, severity {0}, facility {1}, code {2}]",\r
-                                                                          FormatSeverity(error),\r
-                                                                          FormatFacility(error),\r
-                                                                          FormatCode(error));\r
-                                       descriptions.Add(description);\r
-                               }\r
-                               // Reserved bit is set: HRESULT_FROM_NT(ntstatus)\r
-                               else if (IsReserved(error))\r
-                               {\r
-                                       int status = (int)(error & 0xCFFFFFFF);\r
-                                       string description = ntStatus.GetNtstatusDescription(status);\r
-                                       \r
-                                       if (description == null)\r
-                                               description = status.ToString("X");\r
-                                       \r
-                                       description = String.Format("HRESULT_FROM_NT({0})", description);\r
-                                       descriptions.Add(description);\r
-                               }\r
-                               // Win32 facility: HRESULT_FROM_WIN32(winerror)\r
-                               else if (GetFacility(error) == 7)\r
-                               {\r
-                                       // Must be an error code\r
-                                       if (GetSeverity(error) == 2)\r
-                                       {\r
-                                               short err = GetCode(error);\r
-                                               string description = winerror.GetWinerrorDescription(err);\r
-                                               \r
-                                               if (description == null)\r
-                                                       description = err.ToString("D");\r
-                                               \r
-                                               description = String.Format("HRESULT_FROM_WIN32({0})", description);\r
-                                               descriptions.Add(description);\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       string winerrorDescription = winerror.GetWinerrorDescription(error);\r
-                       string ntstatusDescription = ntStatus.GetNtstatusDescription(error);\r
-                       string hresultDescription = hresult.GetHresultDescription(error);\r
-                       \r
-                       if (winerrorDescription != null)\r
-                               descriptions.Add(winerrorDescription);\r
-                       if (ntstatusDescription != null)\r
-                               descriptions.Add(ntstatusDescription);\r
-                       if (hresultDescription != null)\r
-                               descriptions.Add(hresultDescription);\r
-\r
-                       if (descriptions.Count == 0)\r
-                       {\r
-                               // Last chance heuristics: attempt to parse a 8-digit decimal as hexadecimal\r
-                               if (errorText.Length == 8)\r
-                               {\r
-                                       errorText = "0x" + errorText;\r
-                                       goto retry;\r
-                               }\r
-\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                                               String.Format("I don't know about Error Code {0}.",\r
-                                                                                                         originalErrorText));\r
-                       }\r
-                       else if (descriptions.Count == 1)\r
-                       {\r
-                               string description = (string)descriptions[0];\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                                               String.Format("{0} is {1}.",\r
-                                                                                                         originalErrorText,\r
-                                                                                                         description));\r
-                       }\r
-                       else\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} could be:",\r
-                                                                     originalErrorText));\r
-                               \r
-                               foreach(string description in descriptions)\r
-                    TechBot.ServiceOutput.WriteLine(context, String.Format("\t{0}", description));\r
-                       }\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!error <value>";\r
-               }\r
-       }\r
-}\r
+using System;
+using System.Xml;
+using System.Collections;
+
+namespace TechBot.Library
+{
+    [Command("error", Help = "!error <value>")]
+       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 (file)
index 0000000..f47ae18
--- /dev/null
@@ -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("");
+                }
+            }
+        }
+       }
+}
@@ -1,90 +1,69 @@
-using System;\r
-using System.Xml;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class HResultCommand : XmlCommand\r
-       {\r
-        public HResultCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-               }\r
-\r
-        public override string XmlFile\r
-        {\r
-            get { return Settings.Default.HResultXml; }\r
-        }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "hresult" }; }\r
-        }\r
-\r
-        /*\r
-               public bool CanHandle(string commandName)\r
-               {\r
-                       return CanHandle(commandName,\r
-                                        new string[] { "hresult" });\r
-               }\r
-        */\r
-\r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       string hresultText = parameters;\r
-                       if (hresultText.Equals(String.Empty))\r
-                       {\r
-                               TechBot.ServiceOutput.WriteLine(context,\r
-                                                       "Please provide a valid HRESULT value.");\r
-                               return;\r
-                       }\r
-\r
-                       NumberParser np = new NumberParser();\r
-                       long hresult = np.Parse(hresultText);\r
-                       if (np.Error)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is not a valid HRESULT value.",\r
-                                                                     hresultText));\r
-                               return;\r
-                       }\r
-                       \r
-                       string description = GetHresultDescription(hresult);\r
-                       if (description != null)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is {1}.",\r
-                                                                     hresultText,\r
-                                                                     description));\r
-                       }\r
-                       else\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("I don't know about HRESULT {0}.",\r
-                                                                     hresultText));\r
-                       }\r
-               }\r
-               \r
-               public override string Help()\r
-               {\r
-                       return "!hresult <value>";\r
-               }\r
-               \r
-               public string GetHresultDescription(long hresult)\r
-               {\r
-                       XmlElement root = base.m_XmlDocument.DocumentElement;\r
-                       XmlNode node = root.SelectSingleNode(String.Format("Hresult[@value='{0}']",\r
-                                                                          hresult.ToString("X8")));\r
-                       if (node != null)\r
-                       {\r
-                               XmlAttribute text = node.Attributes["text"];\r
-                               if (text == null)\r
-                                       throw new Exception("Node has no text attribute.");\r
-                               return text.Value;\r
-                       }\r
-                       else\r
-                               return null;\r
-               }\r
-       }\r
-}\r
+using System;
+using System.Xml;
+
+namespace TechBot.Library
+{
+    [Command("hresult", Help = "!hresult <value>")]
+    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;
+               }
+       }
+}
@@ -1,88 +1,69 @@
-using System;\r
-using System.Xml;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class NtStatusCommand : XmlCommand\r
-       {\r
-        public NtStatusCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-               }\r
-\r
-        public override string XmlFile\r
-        {\r
-            get { return Settings.Default.NtStatusXml; }\r
-        }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "ntstatus" }; }\r
-        }\r
-/*             \r
-               public bool CanHandle(string commandName)\r
-               {\r
-                       return CanHandle(commandName,\r
-                                        new string[] { "ntstatus" });\r
-               }\r
-*/\r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       string ntstatusText = parameters;\r
-                       if (ntstatusText.Equals(String.Empty))\r
-                       {\r
-                               TechBot.ServiceOutput.WriteLine(context,\r
-                                                       "Please provide a valid NTSTATUS value.");\r
-                               return;\r
-                       }\r
-\r
-                       NumberParser np = new NumberParser();\r
-                       long ntstatus = np.Parse(ntstatusText);\r
-                       if (np.Error)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is not a valid NTSTATUS value.",\r
-                                                                     ntstatusText));\r
-                               return;\r
-                       }\r
-                       \r
-                       string description = GetNtstatusDescription(ntstatus);\r
-                       if (description != null)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is {1}.",\r
-                                                                     ntstatusText,\r
-                                                                     description));\r
-                       }\r
-                       else\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("I don't know about NTSTATUS {0}.",\r
-                                                                     ntstatusText));\r
-                       }\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!ntstatus <value>";\r
-               }\r
-               \r
-               public string GetNtstatusDescription(long ntstatus)\r
-               {\r
-                       XmlElement root = base.m_XmlDocument.DocumentElement;\r
-                       XmlNode node = root.SelectSingleNode(String.Format("Ntstatus[@value='{0}']",\r
-                                                                          ntstatus.ToString("X8")));\r
-                       if (node != null)\r
-                       {\r
-                               XmlAttribute text = node.Attributes["text"];\r
-                               if (text == null)\r
-                                       throw new Exception("Node has no text attribute.");\r
-                               return text.Value;\r
-                       }\r
-                       else\r
-                               return null;\r
-               }\r
-       }\r
-}\r
+using System;
+using System.Xml;
+
+namespace TechBot.Library
+{
+    [Command("ntstatus", Help = "!ntstatus <value>")]
+       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;
+               }
+       }
+}
@@ -4,26 +4,16 @@ using System.Text;
 
 namespace TechBot.Library
 {
+    [Command("rosbug", Help = "!rosbug <number>")]
     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 <number>";
-        }
     }
 }
diff --git a/irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs b/irc/TechBot/TechBot.Library/Commands/SambaBugUrl.cs
new file mode 100644 (file)
index 0000000..74b5182
--- /dev/null
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TechBot.Library
+{
+    [Command("sambabug", Help = "!sambabug <number>")]
+    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 (file)
index 0000000..5146c28
--- /dev/null
@@ -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 (file)
index 0000000..ace4f00
--- /dev/null
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TechBot.Library
+{
+    [Command("winebug", Help = "!winebug <number>")]
+    class WineBugUrl : BugCommand
+    {
+        public WineBugUrl()
+        {
+        }
+
+        protected override string BugUrl
+        {
+            get { return "http://bugs.winehq.org/show_bug.cgi?id={0}"; }
+        }
+    }
+}
@@ -1,82 +1,69 @@
-using System;\r
-using System.Xml;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class WinerrorCommand : XmlCommand\r
-       {\r
-        public WinerrorCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-               }\r
-\r
-        public override string XmlFile\r
-        {\r
-            get { return Settings.Default.WinErrorXml; }\r
-        }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "winerror" }; }\r
-        }\r
-\r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       string winerrorText = parameters;\r
-                       if (winerrorText.Equals(String.Empty))\r
-                       {\r
-                               TechBot.ServiceOutput.WriteLine(context,\r
-                                                       "Please provide a valid System Error Code value.");\r
-                               return;\r
-                       }\r
-\r
-                       NumberParser np = new NumberParser();\r
-                       long winerror = np.Parse(winerrorText);\r
-                       if (np.Error)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is not a valid System Error Code value.",\r
-                                                                     winerrorText));\r
-                               return;\r
-                       }\r
-                       \r
-                       string description = GetWinerrorDescription(winerror);\r
-                       if (description != null)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is {1}.",\r
-                                                                     winerrorText,\r
-                                                                     description));\r
-                       }\r
-                       else\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("I don't know about System Error Code {0}.",\r
-                                                                     winerrorText));\r
-                       }\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!winerror <value>";\r
-               }\r
-               \r
-               public string GetWinerrorDescription(long winerror)\r
-               {\r
-                       XmlElement root = base.m_XmlDocument.DocumentElement;\r
-                       XmlNode node = root.SelectSingleNode(String.Format("Winerror[@value='{0}']",\r
-                                                                          winerror));\r
-                       if (node != null)\r
-                       {\r
-                               XmlAttribute text = node.Attributes["text"];\r
-                               if (text == null)\r
-                                       throw new Exception("Node has no text attribute.");\r
-                               return text.Value;\r
-                       }\r
-                       else\r
-                               return null;\r
-               }\r
-       }\r
-}\r
+using System;
+using System.Xml;
+
+namespace TechBot.Library
+{
+    [Command("winerror", Help = "!winerror <value>")]
+    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;
+               }
+       }
+}
similarity index 65%
rename from irc/TechBot/TechBot.Library/WmCommand.cs
rename to irc/TechBot/TechBot.Library/Commands/WmCommand.cs
index 31d6701..4ffd493 100644 (file)
-using System;\r
-using System.Xml;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class WMCommand : XmlCommand\r
-       {\r
-        public WMCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-               }\r
-\r
-        public override string XmlFile\r
-        {\r
-            get { return Settings.Default.WMXml; }\r
-        }\r
-               \r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "wm" }; }\r
-        }\r
-\r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       string wmText = parameters;\r
-                       if (wmText.Equals(String.Empty))\r
-                       {\r
-                               TechBot.ServiceOutput.WriteLine(context,\r
-                                                       "Please provide a valid window message value or name.");\r
-                               return;\r
-                       }\r
-\r
-                       NumberParser np = new NumberParser();\r
-                       long wm = np.Parse(wmText);\r
-                       string output;\r
-                       if (np.Error)\r
-                       {\r
-                               // Assume "!wm <name>" form.\r
-                               output = GetWmNumber(wmText);\r
-                       }\r
-                       else\r
-                       {\r
-                               output = GetWmDescription(wm);\r
-                       }\r
-\r
-                       if (output != null)\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("{0} is {1}.",\r
-                                                                     wmText,\r
-                                                                     output));\r
-                       }\r
-                       else\r
-                       {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                                       String.Format("I don't know about window message {0}.",\r
-                                                                     wmText));\r
-                       }\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!wm <value> or !wm <name>";\r
-               }\r
-               \r
-               private string GetWmDescription(long wm)\r
-               {\r
-                       XmlElement root = base.m_XmlDocument.DocumentElement;\r
-                       XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@value='{0}']",\r
-                                                                          wm));\r
-                       if (node != null)\r
-                       {\r
-                               XmlAttribute text = node.Attributes["text"];\r
-                               if (text == null)\r
-                                       throw new Exception("Node has no text attribute.");\r
-                               return text.Value;\r
-                       }\r
-                       else\r
-                               return null;\r
-               }\r
-               \r
-               private string GetWmNumber(string wmName)\r
-               {\r
-                       XmlElement root = base.m_XmlDocument.DocumentElement;\r
-                       XmlNode node = root.SelectSingleNode(String.Format("WindowMessage[@text='{0}']",\r
-                                                                          wmName));\r
-                       if (node != null)\r
-                       {\r
-                               XmlAttribute value = node.Attributes["value"];\r
-                               if (value == null)\r
-                                       throw new Exception("Node has no value attribute.");\r
-                               return value.Value;\r
-                       }\r
-                       else\r
-                               return null;\r
-               }\r
-       }\r
-}\r
+using System;
+using System.Xml;
+
+namespace TechBot.Library
+{
+    [Command("wm" , Help = "!wm <value> or !wm <name>")]
+       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 <name>" 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 (file)
index 0000000..6a45967
--- /dev/null
@@ -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 (file)
index 0000000..34e3471
--- /dev/null
@@ -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 (file)
index b007a7e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;\r
-using System.Collections;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class HelpCommand : Command\r
-       {\r
-        public HelpCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-               }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "help" }; }\r
-        }\r
-\r
-        public override void Handle(\r
-            MessageContext context,\r
-                           string commandName,\r
-                           string parameters)\r
-        {\r
-            TechBot.ServiceOutput.WriteLine(context, "I support the following commands:");\r
-\r
-            foreach (Command command in TechBot.Commands)\r
-            {\r
-                TechBot.ServiceOutput.WriteLine(context,\r
-                                        command.Help());\r
-            }\r
-        }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!help";\r
-               }\r
-       }\r
-}\r
index 8d1728a..1d9677c 100644 (file)
@@ -7,43 +7,33 @@ namespace TechBot.Library
        {\r
        }\r
 \r
-\r
-\r
        public class ChannelMessageContext : MessageContext\r
        {\r
-               private IrcChannel channel;\r
+               private IrcChannel m_IrcChannel;\r
 \r
-               public IrcChannel Channel\r
-               {\r
-                       get\r
-                       {\r
-                               return channel;\r
-                       }\r
-               }\r
+        public IrcChannel Channel\r
+        {\r
+            get { return m_IrcChannel; }\r
+        }\r
                \r
                public ChannelMessageContext(IrcChannel channel)\r
                {\r
-                       this.channel = channel;\r
+                       m_IrcChannel = channel;\r
                }\r
        }\r
-\r
-       \r
        \r
        public class UserMessageContext : MessageContext\r
        {\r
-               private IrcUser user;\r
+               private IrcUser m_IrcUser;\r
 \r
-               public IrcUser User\r
-               {\r
-                       get\r
-                       {\r
-                               return user;\r
-                       }\r
-               }\r
+        public IrcUser User\r
+        {\r
+            get { return m_IrcUser; }\r
+        }\r
                \r
                public UserMessageContext(IrcUser user)\r
                {\r
-                       this.user = user;\r
+                       m_IrcUser = user;\r
                }\r
        }\r
 }\r
diff --git a/irc/TechBot/TechBot.Library/ParametersParser.cs b/irc/TechBot/TechBot.Library/ParametersParser.cs
new file mode 100644 (file)
index 0000000..4ce1685
--- /dev/null
@@ -0,0 +1,537 @@
+using System;
+using System.Text.RegularExpressions;
+
+//Code taken from : http://www.codeproject.com/KB/recipes/commandlineparser.aspx
+
+namespace TechBot.Library
+{
+       /// <summary>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.</summary>
+       public class ParametersParser
+       {
+               /// <summary>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.</summary>
+               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
+
+                       /// <summary>
+                       /// 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.
+                       /// </summary>
+                       /// <param name="rec">The SwitchRecord for which this class store information.</param>
+                       /// <exception cref="ArgumentException">Thrown if the rec parameter is not of
+                       /// the type SwitchRecord.</exception>
+                       public SwitchInfo( object rec )
+                       {
+                               if ( rec is SwitchRecord )
+                                       m_Switch = rec;
+                               else
+                                       throw new ArgumentException();
+                       }
+               }
+
+               /// <summary>
+               /// The SwitchRecord is stored within the parser's collection of registered
+               /// switches.  This class is private to the outside world.
+               /// </summary>
+               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|^)(?<match>(-{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 = @")(?<value>(\+|-){0,1}))";
+                               else if ( Type == typeof(string) )
+                                       strPatternEnd = @")(?::|\s+))((?:"")(?<value>.+)(?:"")|(?<value>\S+))";
+                               else if ( Type == typeof(int) )
+                                       strPatternEnd = @")(?::|\s+))((?<value>(-|\+)[0-9]+)|(?<value>[0-9]+))";
+                               else if ( Type.IsEnum )
+                               {
+                                       string[] enumNames = Enumerations;
+                                       string e_str = enumNames[0];
+                                       for ( int e=1; e<enumNames.Length; e++ )
+                                               e_str += "|" + enumNames[e];
+                                       strPatternEnd = @")(?::|\s+))(?<value>" + 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(@"^(?<commandLine>("".+""|(\S)+))(?<remainder>.+)",
+                               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*(""(?<param>.+?)""|(?<param>\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; i<m_switches.Count; i++ )
+                                               si[i] = new SwitchInfo( m_switches[i] );
+                                       return si;
+                               }
+                       }
+               }
+
+               public object this[string name] 
+               {
+                       get
+                       {
+                               if ( m_switches != null )
+                                       for ( int i=0; i<m_switches.Count; i++ )
+                                               if ( string.Compare( (m_switches[i] as SwitchRecord).Name, name, true )==0 )
+                                                       return (m_switches[i] as SwitchRecord).Value;
+                               return null;
+                       }
+               }
+
+               /// <summary>This function returns a list of the unhandled switches
+               /// that the parser has seen, but not processed.</summary>
+               /// <remark>The unhandled switches are not removed from the remainder
+               /// of the command-line.</remark>
+               public string[] UnhandledSwitches 
+               {
+                       get
+                       {
+                               string switchPattern = @"(\s|^)(?<match>(-{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<names.Length; s++ )
+                               rec.AddAlias( names[s] );
+                       m_switches.Add( rec );
+               }
+
+               public bool Parse()
+               {
+                       ExtractApplicationName();
+
+                       // Remove switches and associated info.
+                       HandleSwitches();
+
+                       // Split parameters.
+                       SplitParameters();
+
+                       return true;
+               }
+
+               public object InternalValue(string name)
+               {
+                       if ( m_switches != null )
+                               for ( int i=0; i<m_switches.Count; i++ )
+                                       if ( string.Compare( (m_switches[i] as SwitchRecord).Name, name, true )==0 )
+                                               return (m_switches[i] as SwitchRecord).InternalValue;
+                       return null;
+               }
+               #endregion
+
+               #region Constructors
+               public ParametersParser( string commandLine )
+               {
+                       m_commandLine = commandLine;
+               }
+
+               public ParametersParser( string commandLine,
+                                                       object classForAutoAttributes )
+               {
+                       m_commandLine = commandLine;
+
+                       Type type = classForAutoAttributes.GetType();
+                       System.Reflection.MemberInfo[] members = type.GetMembers();
+
+                       for(int i=0; i<members.Length; i++)
+                       {
+                               object[] attributes = members[i].GetCustomAttributes(false);
+                               if(attributes.Length > 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 (file)
index 6b70fac..0000000
+++ /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 <number>";
-        }
-    }
-}
index 809cc93..d73a93e 100644 (file)
@@ -2,9 +2,8 @@ using System;
 \r
 namespace TechBot.Library\r
 {\r
-       public interface IServiceOutput\r
-       {\r
-               void WriteLine(MessageContext context,\r
-                              string message);\r
-       }\r
+    public interface IServiceOutput\r
+    {\r
+        void WriteLine(MessageContext context, string message);\r
+    }\r
 }\r
diff --git a/irc/TechBot/TechBot.Library/SvnCommand.cs b/irc/TechBot/TechBot.Library/SvnCommand.cs
deleted file mode 100644 (file)
index aef633b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class SvnCommand : Command\r
-       {\r
-               private string m_SvnRoot;\r
-\r
-        public SvnCommand(TechBotService techBot)\r
-            : base(techBot)\r
-               {\r
-                       m_SvnRoot = Settings.Default.SVNRoot;\r
-               }\r
-\r
-        public override string[] AvailableCommands\r
-        {\r
-            get { return new string[] { "svn" }; }\r
-        }\r
-               \r
-               public override void Handle(MessageContext context,\r
-                                  string commandName,\r
-                                  string parameters)\r
-               {\r
-                       TechBot.ServiceOutput.WriteLine(context, string.Format("svn co {0}" , m_SvnRoot));\r
-               }\r
-\r
-        public override string Help()\r
-               {\r
-                       return "!svn";\r
-               }\r
-       }\r
-}\r
index 3db8369..9cd39a4 100644 (file)
   </Target>
   -->
   <ItemGroup>
-    <Compile Include="ApiCommand.cs" />
-    <Compile Include="BugCommand.cs" />
+    <Compile Include="Attributes\CommandAttribute.cs" />
+    <Compile Include="Attributes\CommandParameterAliasAttribute.cs" />
+    <Compile Include="Attributes\CommandParameterAttribute.cs" />
+    <Compile Include="Collections\CommandBuilderCollection.cs" />
+    <Compile Include="Commands\Base\XmlLookupCommand.cs" />
+    <Compile Include="Factory\CommandBuilder.cs" />
+    <Compile Include="Factory\CommandFactory.cs" />
+    <Compile Include="Commands\Base\Command.cs" />
     <Compile Include="Commands\Base\XmlCommand.cs" />
-    <Compile Include="SambaBugUrl.cs" />
-    <Compile Include="WineBugUrl.cs" />
-    <Compile Include="ErrorCommand.cs" />
-    <Compile Include="HelpCommand.cs" />
-    <Compile Include="HresultCommand.cs" />
-    <Compile Include="Command.cs" />
-    <Compile Include="IrcService.cs" />
+    <Compile Include="Commands\BugCommand.cs" />
+    <Compile Include="Commands\HelpCommand.cs" />
+    <Compile Include="Commands\HResultCommand.cs" />
+    <Compile Include="Commands\NtStatusCommand.cs" />
+    <Compile Include="Commands\ReactOSBugUrl.cs" />
+    <Compile Include="Commands\SambaBugUrl.cs" />
+    <Compile Include="Commands\SvnCommand.cs" />
+    <Compile Include="Commands\WineBugUrl.cs" />
+    <Compile Include="Commands\WinerrorCommand.cs" />
+    <Compile Include="Commands\WMCommand.cs" />
     <Compile Include="MessageContext.cs" />
-    <Compile Include="NtStatusCommand.cs" />
     <Compile Include="NumberParser.cs" />
+    <Compile Include="ParametersParser.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="ReactOSBugUrl.cs" />
     <Compile Include="ServiceOutput.cs" />
     <Compile Include="Settings.cs" />
     <Compile Include="Settings.Designer.cs">
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
       <DependentUpon>Settings.settings</DependentUpon>
     </Compile>
-    <Compile Include="SvnCommand.cs" />
+    <Compile Include="TechBotIrcService.cs" />
     <Compile Include="TechBotService.cs" />
-    <Compile Include="WinerrorCommand.cs" />
-    <Compile Include="WmCommand.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
similarity index 70%
rename from irc/TechBot/TechBot.Library/IrcService.cs
rename to irc/TechBot/TechBot.Library/TechBotIrcService.cs
index bfd2867..ec5f70a 100644 (file)
-using System;\r
-using System.Text;\r
-using System.Collections;\r
-using System.Threading;\r
-\r
-using TechBot.IRCLibrary;\r
-\r
-namespace TechBot.Library\r
-{\r
-       public class IrcService : IServiceOutput\r
-       {\r
-               private string hostname;\r
-               private int port;\r
-               private string channelnames;\r
-               private string botname;\r
-               private string password;\r
-               private string chmPath;\r
-               private string mainChm;\r
-               private string ntstatusXml;\r
-               private string winerrorXml;\r
-               private string hresultXml;\r
-               private string wmXml;\r
-               private string svnCommand;\r
-               private string bugUrl, WineBugUrl, SambaBugUrl;\r
-               private IrcClient m_IrcClient;\r
-               private ArrayList channels = new ArrayList(); /* IrcChannel */\r
-               private TechBotService service;\r
-               private bool isStopped = false;\r
-\r
-               public IrcService(string hostname,\r
-                                 int port,\r
-                                 string channelnames,\r
-                                 string botname,\r
-                                 string password,\r
-                                 string chmPath,\r
-                                 string mainChm,\r
-                          //string ntstatusXml,\r
-                          //string winerrorXml,\r
-                          //string hresultXml,\r
-                          //string wmXml,\r
-                                 //string svnCommand,\r
-                                 string BugUrl,\r
-                                 string WineBugUrl,\r
-                                 string SambaBugUrl)\r
-               {\r
-                       this.hostname = hostname;\r
-                       this.port = port;\r
-                       this.channelnames = channelnames;\r
-                       this.botname = botname;\r
-                       if (password == null || password.Trim() == "")\r
-                               this.password = null;\r
-                       else\r
-                               this.password = password;\r
-                       this.chmPath = chmPath;\r
-                       this.mainChm = mainChm;\r
-                       this.ntstatusXml = ntstatusXml;\r
-                       this.winerrorXml = winerrorXml;\r
-                       this.hresultXml = hresultXml;\r
-                       this.wmXml = wmXml;\r
-                       this.svnCommand = svnCommand;\r
-                       this.bugUrl = BugUrl;\r
-                       this.WineBugUrl = WineBugUrl;\r
-                       this.SambaBugUrl = SambaBugUrl;\r
-               }\r
-\r
-        public void Run()\r
-        {\r
-            service = new TechBotService(this,\r
-                                         chmPath,\r
-                                         mainChm);\r
-                                         //ntstatusXml,\r
-                                         //winerrorXml,\r
-                                         //hresultXml,\r
-                                         //wmXml,\r
-                                         //svnCommand,\r
-                                         //bugUrl,\r
-                                         //WineBugUrl,\r
-                                         //SambaBugUrl);\r
-            service.Run();\r
-\r
-            m_IrcClient = new IrcClient();\r
-            m_IrcClient.Encoding = Encoding.GetEncoding("iso-8859-1");\r
-            m_IrcClient.MessageReceived += new MessageReceivedHandler(client_MessageReceived);\r
-            m_IrcClient.ChannelUserDatabaseChanged += new ChannelUserDatabaseChangedHandler(client_ChannelUserDatabaseChanged);\r
-            Console.WriteLine("Connecting to {0} port {1}",\r
-                                                   hostname,\r
-                                                   port);\r
-            m_IrcClient.Connect(hostname, port);\r
-            Console.WriteLine("Connected...");\r
-            m_IrcClient.Register(botname, password, null);\r
-            Console.WriteLine("Registered as {0}...", botname);\r
-            JoinChannels();\r
-\r
-            while (!isStopped)\r
-            {\r
-                Thread.Sleep(1000);\r
-            }\r
-\r
-            PartChannels();\r
-            m_IrcClient.Diconnect();\r
-            Console.WriteLine("Disconnected...");\r
-        }\r
-\r
-               public void Stop()\r
-               {\r
-                       isStopped = true;\r
-               }\r
-\r
-               private void JoinChannels()\r
-               {\r
-                       foreach (string channelname in channelnames.Split(new char[] { ';' }))\r
-                       {\r
-                               IrcChannel channel = m_IrcClient.JoinChannel(channelname);\r
-                               channels.Add(channel);\r
-                               System.Console.WriteLine(String.Format("Joined channel #{0}...",\r
-                                                                      channel.Name));\r
-                       }\r
-               }\r
-\r
-               private void PartChannels()\r
-               {\r
-                       foreach (IrcChannel channel in channels)\r
-                       {\r
-                               m_IrcClient.PartChannel(channel, "Caught in the bitstream...");\r
-                               System.Console.WriteLine(String.Format("Parted channel #{0}...",\r
-                                                                      channel.Name));\r
-                       }\r
-               }\r
-\r
-               private string GetMessageSource(MessageContext context)\r
-               {\r
-                       if (context is ChannelMessageContext)\r
-                       {\r
-                               ChannelMessageContext channelContext = context as ChannelMessageContext;\r
-                               return String.Format("#{0}",\r
-                                                    channelContext.Channel.Name);\r
-                       }\r
-                       else if (context is UserMessageContext)\r
-                       {\r
-                               UserMessageContext userContext = context as UserMessageContext;\r
-                               return userContext.User.Nickname;\r
-                       }\r
-                       else\r
-                       {\r
-                               throw new InvalidOperationException(String.Format("Unhandled message context '{0}'",\r
-                                                                                 context.GetType()));\r
-                       }\r
-               }\r
-\r
-               public void WriteLine(MessageContext context,\r
-                                     string message)\r
-               {\r
-                       if (context is ChannelMessageContext)\r
-                       {\r
-                               ChannelMessageContext channelContext = context as ChannelMessageContext;\r
-                               channelContext.Channel.Talk(message);\r
-                       }\r
-                       else if (context is UserMessageContext)\r
-                       {\r
-                               UserMessageContext userContext = context as UserMessageContext;\r
-                               userContext.User.Talk(message);\r
-                       }\r
-                       else\r
-                       {\r
-                               throw new InvalidOperationException(String.Format("Unhandled message context '{0}'",\r
-                                                                                 context.GetType()));\r
-                       }\r
-               }\r
-\r
-               private void ExtractMessage(string parameters,\r
-                                           out string message)\r
-               {\r
-                       int startIndex = parameters.IndexOf(':');\r
-                       if (startIndex != -1)\r
-                       {\r
-                               message = parameters.Substring(startIndex + 1);\r
-                       }\r
-                       else\r
-                       {\r
-                               message = parameters;\r
-                       }\r
-               }\r
-\r
-               private bool GetChannelName(IrcMessage message,\r
-                                          out string channelName)\r
-               {\r
-                       if (message.Parameters == null || !message.Parameters.StartsWith("#"))\r
-                       {\r
-                               channelName = null;\r
-                               return false;\r
-                       }\r
-\r
-                       int index = message.Parameters.IndexOf(' ');\r
-                       if (index == -1)\r
-                               index = message.Parameters.Length;\r
-                       else\r
-                               index = index - 1;\r
-                       channelName = message.Parameters.Substring(1, index);\r
-                       return true;\r
-               }\r
-\r
-               private bool GetTargetNickname(IrcMessage message,\r
-                                              out string nickname)\r
-               {\r
-                       if (message.Parameters == null)\r
-                       {\r
-                               nickname = null;\r
-                               return false;\r
-                       }\r
-\r
-                       int index = message.Parameters.IndexOf(' ');\r
-                       if (index == -1)\r
-                               index = message.Parameters.Length;\r
-                       nickname = message.Parameters.Substring(0, index);\r
-                       Console.WriteLine("nickname: " + nickname);\r
-                       return true;\r
-               }\r
-\r
-               private bool ShouldAcceptMessage(IrcMessage message,\r
-                                                out MessageContext context)\r
-               {\r
-                       if (message.Command.ToUpper().Equals("PRIVMSG"))\r
-                       {\r
-                               string channelName;\r
-                               string nickname;\r
-                               if (GetChannelName(message,\r
-                                                  out channelName))\r
-                               {\r
-                                       foreach (IrcChannel channel in channels)\r
-                                       {\r
-                                               if (String.Compare(channel.Name, channelName, true) == 0)\r
-                                               {\r
-                                                       context = new ChannelMessageContext(channel);\r
-                                                       return true;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               else if (GetTargetNickname(message,\r
-                                                          out nickname))\r
-                               {\r
-                                       IrcUser targetUser = new IrcUser(m_IrcClient,\r
-                                                                        nickname);\r
-                                       if (String.Compare(targetUser.Nickname, botname, true) == 0)\r
-                                       {\r
-                                               IrcUser sourceUser = new IrcUser(m_IrcClient,\r
-                                                                                message.PrefixNickname);\r
-                                               context = new UserMessageContext(sourceUser);\r
-                                               return true;\r
-                                       }\r
-                               }\r
-                       }\r
-                       context = null;\r
-                       return false;\r
-               }\r
-                               \r
-               private void client_MessageReceived(IrcMessage message)\r
-               {\r
-                       try\r
-                       {\r
-                               if (message.Command != null &&\r
-                                   message.Parameters != null)\r
-                               {\r
-                                       string injectMessage;\r
-                                       ExtractMessage(message.Parameters,\r
-                                                      out injectMessage);\r
-                                       MessageContext context;\r
-                                       if (ShouldAcceptMessage(message,\r
-                                                               out context))\r
-                                       {\r
-                                               Console.WriteLine(String.Format("Injecting: {0} from {1}",\r
-                                                                               injectMessage,\r
-                                                                               GetMessageSource(context)));\r
-                                               service.InjectMessage(context,\r
-                                                                     injectMessage);\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               Console.WriteLine("Received: " + message.Line);\r
-                                       }\r
-                               }\r
-                               else\r
-                               {\r
-                                       Console.WriteLine("Received: " + message.Line);\r
-                               }\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               Console.WriteLine(String.Format("Exception: {0}", ex));\r
-                       }\r
-               }\r
-               \r
-               private void client_ChannelUserDatabaseChanged(IrcChannel channel)\r
-               {\r
-               }\r
-       }\r
-}\r
+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)
+               {
+               }
+       }
+}
index d40ab91..bb0bb55 100644 (file)
@@ -4,63 +4,29 @@ using System.Collections.Generic;
 using System.IO;\r
 using System.Data;\r
 using System.Threading;\r
+\r
 using TechBot.IRCLibrary;\r
 \r
 namespace TechBot.Library\r
 {\r
-       public class TechBotService\r
+       public abstract class TechBotService\r
        {\r
-               private IServiceOutput serviceOutput;\r
+               protected IServiceOutput serviceOutput;\r
                private string chmPath;\r
                private string mainChm;\r
-               private string ntstatusXml;\r
-               private string winerrorXml;\r
-               private string hresultXml;\r
-               private string wmXml;\r
-               private string svnCommand;\r
-               private string bugUrl, WineBugUrl, SambaBugUrl;\r
-        private List<Command> commands = new List<Command>();\r
                \r
                public TechBotService(IServiceOutput serviceOutput,\r
                                      string chmPath,\r
                                      string mainChm)\r
-                              //string ntstatusXml,\r
-                              //string winerrorXml,\r
-                              //string hresultXml,\r
-                              //string wmXml,\r
-                                     //string svnCommand,\r
-                              //string bugUrl,\r
-                              //string WineBugUrl,\r
-                              //string SambaBugUrl)\r
                {\r
                        this.serviceOutput = serviceOutput;\r
                        this.chmPath = chmPath;\r
                        this.mainChm = mainChm;\r
-                       this.ntstatusXml = ntstatusXml;\r
-                       this.winerrorXml = winerrorXml;\r
-                       this.hresultXml = hresultXml;\r
-                       this.wmXml = wmXml;\r
-                       this.svnCommand = svnCommand;\r
-                       this.bugUrl = bugUrl;\r
-                       this.WineBugUrl = WineBugUrl;\r
-                       this.SambaBugUrl = SambaBugUrl;\r
                }\r
 \r
-        public void Run()\r
+        public virtual void Run()\r
         {\r
-            commands.Add(new HelpCommand(this));\r
-            /*commands.Add(new ApiCommand(serviceOutput,\r
-                                        chmPath,\r
-                                        mainChm));*/\r
-            commands.Add(new NtStatusCommand(this));\r
-            commands.Add(new WinerrorCommand(this));\r
-            commands.Add(new HResultCommand(this));\r
-            commands.Add(new ErrorCommand(this));\r
-            commands.Add(new WMCommand(this));\r
-            commands.Add(new SvnCommand(this));\r
-            commands.Add(new ReactOSBugUrl(this));\r
-            commands.Add(new SambaBugUrl(this));\r
-            commands.Add(new WineBugUrl(this));\r
+            CommandFactory.LoadPlugins();\r
         }\r
 \r
         public IServiceOutput ServiceOutput\r
@@ -68,18 +34,16 @@ namespace TechBot.Library
             get { return serviceOutput; }\r
         }\r
 \r
-        public IList<Command> Commands\r
+        public CommandBuilderCollection Commands\r
         {\r
-            get { return commands; }\r
+            get { return CommandFactory.Commands; }\r
+        }\r
+\r
+        public void InjectMessage(MessageContext context, string message)\r
+        {\r
+            ParseCommandMessage(context,\r
+                                message);\r
         }\r
-               \r
-               public void InjectMessage(MessageContext context,\r
-                                         string message)\r
-               {\r
-                       if (message.StartsWith("!"))\r
-                               ParseCommandMessage(context,\r
-                                                   message);\r
-               }\r
                \r
                private bool IsCommandMessage(string message)\r
                {\r
@@ -104,19 +68,21 @@ namespace TechBot.Library
                        else\r
                                commandName = message.Trim();\r
 \r
-                       foreach (Command command in commands)\r
-                       {\r
-                foreach (string cmd in command.AvailableCommands)\r
+            foreach (CommandBuilder command in Commands)\r
+            {\r
+                if (command.Name == commandName)\r
                 {\r
-                    if (cmd == commandName)\r
-                    {\r
-                        command.Handle(context,\r
-                                       commandName, \r
-                                       commandParams);\r
-                        return;\r
-                    }\r
+                    //Create a new instance of the required command type\r
+                    Command cmd = command.CreateCommand();\r
+\r
+                    cmd.TechBot = this;\r
+                    cmd.Context = context;\r
+                    cmd.ParseParameters(message);\r
+                    cmd.ExecuteCommand();\r
+\r
+                    return;\r
                 }\r
-                       }\r
+            }\r
                }\r
        }\r
 }\r
diff --git a/irc/TechBot/TechBot.Library/WineBugUrl.cs b/irc/TechBot/TechBot.Library/WineBugUrl.cs
deleted file mode 100644 (file)
index 94464a1..0000000
+++ /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 <number>";
-        }
-    }
-}
diff --git a/irc/TechBot/TechBot.cmbx b/irc/TechBot/TechBot.cmbx
deleted file mode 100644 (file)
index 3e8915e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<Combine fileversion="1.0" name="TechBot" description="">\r
-  <StartMode startupentry="TechBot" single="True">\r
-    <Execute entry="TechBot" type="None" />\r
-    <Execute entry="TechBot.Library" type="None" />\r
-    <Execute entry="CHMLibrary" type="None" />\r
-    <Execute entry="Compression" type="None" />\r
-    <Execute entry="TechBot.Console" type="None" />\r
-    <Execute entry="TechBot.IRCLibrary" type="None" />\r
-  </StartMode>\r
-  <Entries>\r
-    <Entry filename=".\TechBot\TechBot.prjx" />\r
-    <Entry filename=".\TechBot.Library\TechBot.Library.prjx" />\r
-    <Entry filename=".\CHMLibrary\CHMLibrary.prjx" />\r
-    <Entry filename=".\Compression\Compression.prjx" />\r
-    <Entry filename=".\TechBot.Console\TechBot.Console.prjx" />\r
-    <Entry filename=".\TechBot.IRCLibrary\TechBot.IRCLibrary.prjx" />\r
-  </Entries>\r
-  <Configurations active="Debug">\r
-    <Configuration name="Release">\r
-      <Entry name="TechBot" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
-      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
-      <Entry name="Compression" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
-    </Configuration>\r
-    <Configuration name="Debug">\r
-      <Entry name="TechBot" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
-      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
-      <Entry name="Compression" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
-      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
-    </Configuration>\r
-  </Configurations>\r
-</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot/ProjectInstaller.cs b/irc/TechBot/TechBot/ProjectInstaller.cs
new file mode 100644 (file)
index 0000000..0dc2b15
--- /dev/null
@@ -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 });
+        }
+    }
+}
index 9e22beb..0675bca 100644 (file)
@@ -51,21 +51,21 @@ namespace TechBot
                        SetupConfiguration();\r
                        System.Console.WriteLine("TechBot irc service...");\r
                        \r
-                       IrcService ircService = new IrcService(IRCServerHostName,\r
+                       IrcTechBotService ircService = new IrcTechBotService(IRCServerHostName,\r
                                                               IRCServerHostPort,\r
                                                               IRCChannelNames,\r
                                                               IRCBotName,\r
                                                               IRCBotPassword,\r
                                                               ChmPath,\r
-                                                              MainChm,\r
+                                                              MainChm);\r
                                                    //NtstatusXml,\r
                                                    //WinerrorXml,\r
                                                    //HresultXml,\r
                                                    //WmXml,\r
                                                               //SvnCommand,\r
-                                                              BugUrl,\r
-                                                              WineBugUrl,\r
-                                                              SambaBugUrl);\r
+                                                   //BugUrl,\r
+                                                   //WineBugUrl,\r
+                                                   //SambaBugUrl);\r
                        ircService.Run();\r
                }\r
                \r
index 692e473..85fc479 100644 (file)
@@ -42,6 +42,9 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AssemblyInfo.cs" />
+    <Compile Include="ProjectInstaller.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="ServiceThread.cs" />
     <Compile Include="TechBotService.cs">
       <SubType>Component</SubType>
@@ -52,6 +55,7 @@
     <Reference Include="System.Configuration.Install" />
     <Reference Include="System.Data" />
     <Reference Include="System.ServiceProcess" />
+    <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\TechBot.IRCLibrary\TechBot.IRCLibrary.csproj">
index 79fca2d..362ce84 100644 (file)
@@ -80,18 +80,3 @@ namespace TechBot
                }\r
        }\r
 }\r
-\r
-[RunInstaller(true)]\r
-public class ProjectInstaller : Installer\r
-{\r
-       public ProjectInstaller()\r
-       {\r
-               ServiceProcessInstaller spi = new ServiceProcessInstaller();\r
-               spi.Account = ServiceAccount.LocalSystem;\r
-               \r
-               ServiceInstaller si = new ServiceInstaller();\r
-               si.ServiceName = "TechBot";\r
-               si.StartType = ServiceStartMode.Automatic;\r
-               Installers.AddRange(new Installer[] {spi, si});\r
-       }\r
-}\r