From 2d94eb37d15032636acb08af43e3c5906bedcc45 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sun, 20 Aug 2017 00:14:46 +0200 Subject: Add Help plugin A new plugin that displays a help listing for every plugin in the bot. Currently the formatting is off in the chat output, but it does work. This introduces two new record fields on `Plugin`: `command` and `description`. The command is the text used to invoke the plugin and the description is a long form explanation of what the plugin does. Needed to update the `Show` for `Plugin` to match these extra fields. Didn't change any of the output for now because I'm not really using the `show` function, so I don't need to see the new fields for now. Also change the `p` argument to an `_` because we're not using it. All existing plugins now have the new fields filled. The Help plugin will go through the list of all plugins and get their help fields for output. In order to be able to use the plugin list in both `Plugin.hs` and in the Help plugin module, I needed to move the list into a new module to avoid a circular dependency. Previously the `Plugin` module defined the list, but we can't import `Plugin` from `Help` because `Plugin` needs to import `Help` in order to build the full list of plugins. The semi-hackish solution I came up with was to create a new module for the plugin list that both these modules can use, but leave out the `Help` plugin from the plugin list there. Then, `Plugin` and `Help` override the list, appending the `Help` plugin to the list. I want the Help plugin to appear last, which is why I'm appending. Wasn't comfortable concatenating the list because of the performance smell, but it's going to be a small enough list anyway that it shouldn't be a problem. One thing I don't really like is the fact that we have to return an `IO a` from `helpAction` even though it doesn't interact with `IO` at all. Not sure if there's a way to use `IO` when we need it and not when we don't. Not a huge deal though. --- src/Lib.hs | 4 ++-- src/Plugin.hs | 10 +++------- src/Plugin/Base.hs | 8 +++++--- src/Plugin/GitHubCommit.hs | 2 ++ src/Plugin/GitRemoteSetOrigin.hs | 2 ++ src/Plugin/Help.hs | 25 +++++++++++++++++++++++++ src/Plugin/PluginList.hs | 15 +++++++++++++++ 7 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 src/Plugin/Help.hs create mode 100644 src/Plugin/PluginList.hs (limited to 'src') diff --git a/src/Lib.hs b/src/Lib.hs index b34100e..f08b289 100644 --- a/src/Lib.hs +++ b/src/Lib.hs @@ -13,5 +13,5 @@ import Plugin someFunc :: IO () someFunc = do - -- connectIRC "irc.freenode.net" 6697 "test-bot-7890asdf" - parseOptions + connectIRC "irc.freenode.net" 6697 "test-bot-7890asdf" + -- parseOptions diff --git a/src/Plugin.hs b/src/Plugin.hs index a9918fc..30b5ab8 100644 --- a/src/Plugin.hs +++ b/src/Plugin.hs @@ -10,8 +10,8 @@ import Text.Regex.TDFA import qualified Message as M import Plugin.Base -import Plugin.GitHubCommit -import Plugin.GitRemoteSetOrigin +import qualified Plugin.PluginList as PL (plugins) +import Plugin.Help -- | Get the first plugin that matches the given message text. matchPlugin :: M.Message -> Maybe Plugin @@ -29,9 +29,5 @@ matchPlugins message plugins = performPlugin :: Plugin -> PluginAction performPlugin p message = perform p $ message --- | The list of plugins to load plugins :: [Plugin] -plugins = - [ gitHubCommit - , gitRemoteSetOrigin - ] +plugins = PL.plugins ++ [help] diff --git a/src/Plugin/Base.hs b/src/Plugin/Base.hs index 62900c7..ac67e71 100644 --- a/src/Plugin/Base.hs +++ b/src/Plugin/Base.hs @@ -13,9 +13,11 @@ import Message type PluginAction = Message -> IO (Either T.Text T.Text) data Plugin = Plugin - { matchRegex :: String - , perform :: PluginAction + { matchRegex :: String + , perform :: PluginAction + , command :: T.Text + , description :: T.Text } instance Show Plugin where - show (Plugin r p) = "matchRegex = " ++ r + show (Plugin r _ _ _) = "matchRegex = " ++ r diff --git a/src/Plugin/GitHubCommit.hs b/src/Plugin/GitHubCommit.hs index 8b43b57..c4dd2ca 100644 --- a/src/Plugin/GitHubCommit.hs +++ b/src/Plugin/GitHubCommit.hs @@ -17,6 +17,8 @@ import Plugin.Base gitHubCommit = Plugin { matchRegex = "^[0-9a-f]{40}$" , perform = gitHubCommitAction + , command = "GIT_SHA" + , description = "Generate a commit URL based on the given SHA." } gitHubCommitAction :: PluginAction diff --git a/src/Plugin/GitRemoteSetOrigin.hs b/src/Plugin/GitRemoteSetOrigin.hs index 208ab9f..5b2aad2 100644 --- a/src/Plugin/GitRemoteSetOrigin.hs +++ b/src/Plugin/GitRemoteSetOrigin.hs @@ -16,6 +16,8 @@ import Plugin.Base gitRemoteSetOrigin = Plugin { matchRegex = "^git remote set origin ([^ ]+)$" , perform = gitRemoteSetOriginAction + , command = "git remote set origin URL" + , description = "Set the git remote URL for this channel." } gitRemoteSetOriginAction :: PluginAction diff --git a/src/Plugin/Help.hs b/src/Plugin/Help.hs new file mode 100644 index 0000000..57e106d --- /dev/null +++ b/src/Plugin/Help.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Plugin.Help + ( help + ) where + +import qualified Data.Text as T + +import qualified Plugin.PluginList as PL (plugins) +import Plugin.Base + +help = Plugin + { matchRegex = "^help$" + , perform = helpAction + , command = "help" + , description = "Show a list of available bot commands." + } + +helpAction :: PluginAction +helpAction _ = do + return $ Right $ T.concat + [command p `T.append` "\t" `T.append` description p | p <- plugins] + +plugins :: [Plugin] +plugins = PL.plugins ++ [help] diff --git a/src/Plugin/PluginList.hs b/src/Plugin/PluginList.hs new file mode 100644 index 0000000..21f3893 --- /dev/null +++ b/src/Plugin/PluginList.hs @@ -0,0 +1,15 @@ +module Plugin.PluginList + ( plugins + ) where + +import Plugin.Base (Plugin) +import Plugin.GitHubCommit +import Plugin.GitRemoteSetOrigin + +-- | The list of plugins to load, minus the Help plugin, which would otherwise +-- cause a circular import. +plugins :: [Plugin] +plugins = + [ gitHubCommit + , gitRemoteSetOrigin + ] -- cgit v1.2.3