From 8c59b45dc7c88cea63a4cf47566a3d5124a8a0b0 Mon Sep 17 00:00:00 2001
From: Julien Dessaux
Date: Sun, 1 Aug 2010 23:29:48 +0200
Subject: Got a working hsbot-irc back online!

---
 HsbotIrcBot/Hsbot/Irc/Command.hs |  11 ----
 HsbotIrcBot/Hsbot/Irc/Core.hs    | 121 ++++++++++++---------------------------
 HsbotIrcBot/Hsbot/Irc/Message.hs |   2 +-
 HsbotIrcBot/Hsbot/Irc/Plugin.hs  |  13 +----
 HsbotIrcBot/Hsbot/Irc/Types.hs   |  10 ++--
 HsbotIrcBot/Main.hs              |   5 ++
 HsbotIrcBot/hsbot-irc.cabal      |   8 +++
 7 files changed, 59 insertions(+), 111 deletions(-)

(limited to 'HsbotIrcBot')

diff --git a/HsbotIrcBot/Hsbot/Irc/Command.hs b/HsbotIrcBot/Hsbot/Irc/Command.hs
index 51c2187..1b913e2 100644
--- a/HsbotIrcBot/Hsbot/Irc/Command.hs
+++ b/HsbotIrcBot/Hsbot/Irc/Command.hs
@@ -12,7 +12,6 @@ import Data.Maybe
 import Hsbot.Irc.Message
 import Hsbot.Irc.Plugin
 import Hsbot.Irc.Types
-import Hsbot.Types
 
 -- | Registers a plugin's command
 registerCommand :: String -> String -> IrcBot ()
@@ -57,18 +56,8 @@ processCoreCommand ircCmd = do
         "REGISTER"   -> registerCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd)
         "UNLOAD"     -> unloadIrcPlugin $ ircCmdMsg ircCmd
         "UNREGISTER" -> unregisterCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd)
-        "UPDATE"     -> processUpdateCommand ircCmd
         _            -> return ()
     if command' == "REBOOT"
       then return BotReboot
       else return BotContinue
 
--- | Process an update command
-processUpdateCommand :: IrcCmd -> IrcBot ()
-processUpdateCommand ircCmd = do
-    ircbot <- get
-    let oldData = ircBotResumeData ircbot
-        from    = ircCmdFrom ircCmd
-        stuff   = ircCmdMsg ircCmd
-    put $ ircbot { ircBotResumeData = M.insert from stuff oldData }
-
diff --git a/HsbotIrcBot/Hsbot/Irc/Core.hs b/HsbotIrcBot/Hsbot/Irc/Core.hs
index d65e975..525c3d6 100644
--- a/HsbotIrcBot/Hsbot/Irc/Core.hs
+++ b/HsbotIrcBot/Hsbot/Irc/Core.hs
@@ -10,72 +10,56 @@ import Data.Maybe (fromMaybe)
 import Network
 import Prelude hiding (catch)
 import System.IO
-import System.Posix.IO (fdToHandle, handleToFd)
-import System.Posix.Types (Fd)
 
+import Hsbot.Irc.CLI
 import Hsbot.Irc.Command
 import Hsbot.Irc.Config
 import Hsbot.Irc.Message
 import Hsbot.Irc.Plugin
 import Hsbot.Irc.Server
 import Hsbot.Irc.Types
-import Hsbot.Types
 
 -- | IrcBot's main entry point
-startIrcbot :: IrcConfig -> Chan BotMsg -> Chan BotMsg -> Maybe String -> IO ()
-startIrcbot config masterChan myChan txtResumeData = do
-    let resumeData = case txtResumeData of
-            Just txtData -> read txtData :: ResumeData  -- TODO : catch exception
-            Nothing -> M.empty :: ResumeData
-    print resumeData
-    putStrLn "[IrcBot] Opening communication channel... "
+startIrcbot :: Options -> IrcConfig -> IO ()
+startIrcbot opts ircConfig = do
+    when (optDebug opts) $ putStrLn "[IrcBot] Opening communication channel... "
     chan <- newChan :: IO (Chan IrcBotMsg)
-    handle <- case M.lookup "HANDLE" resumeData of
-        Just txtFd -> do
-            let fd = read txtFd :: Fd
-            fdToHandle fd
-        Nothing -> do
-            putStrLn $ concat ["[IrcBot] Connecting to ", ircConfigAddress config, "... "]
-            handle <- connectTo (ircConfigAddress config) (ircConfigPort config)
-            hSetBuffering handle NoBuffering
-            hSetEncoding handle utf8
-            return handle
-    fd <- handleToFd handle
-    putStrLn "[IrcBot] Spawning reader threads..."
+    when (optDebug opts) . putStrLn $ concat ["[IrcBot] Connecting to ", ircConfigAddress ircConfig, "... "]
+    handle <- connectTo (ircConfigAddress ircConfig) (ircConfigPort ircConfig)
+    hSetBuffering handle NoBuffering
+    hSetEncoding handle utf8
+    when (optDebug opts) $ putStrLn "[IrcBot] Spawning reader thread..."
     myOwnThreadId  <- myThreadId
     readerThreadId <- forkIO $ ircBotReader handle chan myOwnThreadId
-    masterReaderThreadId <- forkIO $ ircBotMasterReader myChan chan
-    putStrLn "[IrcBot] Initializing server connection..."
-    let ircServerState = IrcServerState { ircServerId            = ircConfigAddress config
+    when (optDebug opts) $ putStrLn "[IrcBot] Initializing server connection..."
+    let ircServerState = IrcServerState { ircServerId            = ircConfigAddress ircConfig
                                         , ircServerChannels      = []
-                                        , ircServerNickname      = ircConfigNickname config
-                                        , ircServerCommandPrefix = ircConfigCommandPrefix config
+                                        , ircServerNickname      = ircConfigNickname ircConfig
+                                        , ircServerCommandPrefix = ircConfigCommandPrefix ircConfig
                                         , ircServerChan          = chan }
         ircBotState = IrcBotState { ircBotPlugins              = M.empty
                                   , ircBotCommands             = M.empty
                                   , ircBotChan                 = chan
-                                  , ircBotMasterChan           = masterChan
                                   , ircBotServerState          = ircServerState
                                   , ircBotHandle               = handle
-                                  , ircBotConfig               = config
-                                  , ircBotResumeData           = M.singleton "HANDLE" (show fd) }
-    ircBotState' <- execStateT (initBotServerConnection config) ircBotState
-    putStrLn "[IrcBot] Spawning plugins..."
+                                  , ircBotConfig               = ircConfig }
+    ircBotState' <- execStateT (initBotServerConnection ircConfig) ircBotState
+    when (optDebug opts) $ putStrLn "[IrcBot] Spawning plugins..."
     ircBotState'' <- execStateT spawnIrcPlugins ircBotState'
-    putStrLn "[IrcBot] Entering Core loop... "
-    ircBotState''' <- (execStateT ircBotLoop ircBotState'') `catches` [ Handler (\ (_ :: IOException) -> return (ircBotState''))
-                                                                      , Handler (\ (_ :: AsyncException) -> return (ircBotState'')) ]
-    putStrLn "[IrcBot] Killing reader threads..."
+    when (optDebug opts) $ putStrLn "[IrcBot] Entering Core loop... "
+    (_, ircBotState''') <- runLoop ircBotState''
+    when (optDebug opts) $ putStrLn "[IrcBot] Killing reader thread..."
     killThread readerThreadId
-    killThread masterReaderThreadId
-    putStrLn "[IrcBot] Killing active plugins... "
-    let resumeData' = ircBotResumeData ircBotState'''
-        ircPlugins = read (fromMaybe [] (M.lookup "PLUGINS" resumeData')) :: [String]
-    evalStateT (mapM_ killIrcPlugin ircPlugins) ircBotState'''
-    return ()
-
---resumeIrcBot
---resumeIrcBot
+    when (optDebug opts) $ putStrLn "[IrcBot] Killing active plugins... "
+    evalStateT (mapM_ killIrcPlugin . M.keys $ ircBotPlugins ircBotState''') ircBotState'''
+  where
+    runLoop :: IrcBotState -> IO (BotStatus, IrcBotState)
+    runLoop botState = do
+        (status, botState') <- (runStateT ircBotCore botState) `catches` [ Handler (\ (_ :: IOException) -> return (BotExit, botState))
+                                                                         , Handler (\ (_ :: AsyncException) -> return (BotExit, botState)) ]
+        case status of
+            BotContinue -> runLoop botState'
+            _           -> return (status, botState')
 
 -- | Runs the IrcBot's reader loop
 ircBotReader :: Handle -> Chan IrcBotMsg -> ThreadId -> IO ()
@@ -93,13 +77,6 @@ ircBotReader handle chan fatherThreadId = forever $ do
         killThread myId
         return ""
 
--- | Reads the Master's chan
-ircBotMasterReader :: Chan BotMsg -> Chan IrcBotMsg -> IO ()
-ircBotMasterReader masterChan _ = forever $ do
-    _ <- readChan masterChan
-    return ()
-    -- TODO : handle botMsg
-
 -- | Initialize the bot's server connection
 initBotServerConnection :: IrcConfig -> IrcBot ()
 initBotServerConnection config = do
@@ -109,28 +86,24 @@ initBotServerConnection config = do
     put $ ircBot { ircBotServerState = ircServerState' }
 
 -- | Run the IrcBot's main loop
-ircBotLoop :: IrcBot ()
-ircBotLoop = forever $ do
+ircBotCore :: IrcBot (BotStatus)
+ircBotCore = do
     chan <- gets ircBotChan
     msg  <- liftIO $ readChan chan
     case msg of
         InIrcMsg inIrcMsg   -> dispatchMessage $ InIrcMsg inIrcMsg
         OutIrcMsg outIrcMsg -> sendThisMessage outIrcMsg
-        IntIrcCmd intIrcCmd -> do
-            reboot <- processInternalCommand $ IntIrcCmd intIrcCmd
-            reportUpdate
-            if reboot == BotReboot
-              then processRebootCommand
-              else return ()
+        IntIrcCmd intIrcCmd -> processInternalCommand $ IntIrcCmd intIrcCmd
   where
-    sendThisMessage :: IrcMsg -> IrcBot ()
+    sendThisMessage :: IrcMsg -> IrcBot (BotStatus)
     sendThisMessage outputMsg = do
         let str = serializeIrcMsg outputMsg
         handle <- gets ircBotHandle
         liftIO $ hPutStr handle (str ++ "\r\n")
+        return BotContinue
 
 -- | Dispatches an input message
-dispatchMessage :: IrcBotMsg -> IrcBot ()
+dispatchMessage :: IrcBotMsg -> IrcBot (BotStatus)
 dispatchMessage (InIrcMsg inIrcMsg) = do
     config  <- gets ircBotConfig
     plugins <- gets ircBotPlugins
@@ -143,6 +116,7 @@ dispatchMessage (InIrcMsg inIrcMsg) = do
         in mapM_ (sendRunCommand (tail getMsgContent) . first) plugins'
       else
         mapM_ (sendToPlugin (InIrcMsg inIrcMsg) . first) (M.elems plugins)
+    return BotContinue
   where
     isPluginCommand :: IrcConfig -> Bool
     isPluginCommand config =
@@ -152,26 +126,5 @@ dispatchMessage (InIrcMsg inIrcMsg) = do
     sendRunCommand cmd plugin =  sendToPlugin (IntIrcCmd $ IrcCmd "RUN" "CORE" (ircPluginName plugin) cmd inIrcMsg) plugin
     getMsgContent :: String
     getMsgContent = unwords . tail $ ircMsgParameters inIrcMsg
-dispatchMessage _ = return ()
-
--- | Reports an update to the master bot
-reportUpdate :: IrcBot ()
-reportUpdate = do
-    ircbot <- get
-    let masterChan = ircBotMasterChan ircbot
-        msg = UpdMsg $ ResMsg { resMsgFrom = ircConfigName $ ircBotConfig ircbot
-                              , resMsgData = ircBotResumeData ircbot }
-    liftIO $ writeChan masterChan msg
-
--- | Process a reboot command
-processRebootCommand :: IrcBot ()
-processRebootCommand = do
-    ircbot <- get
-    let masterChan = ircBotMasterChan ircbot
-        msg = IntMsg $ Msg { msgType  = "REBOOT"
-                           , msgFrom  = ircConfigName $ ircBotConfig ircbot
-                           , msgTo    = "CORE"
-                           , msgStuff = show $ ircBotResumeData ircbot
-                           }
-    liftIO $ writeChan masterChan msg
+dispatchMessage _ = return (BotContinue)
 
diff --git a/HsbotIrcBot/Hsbot/Irc/Message.hs b/HsbotIrcBot/Hsbot/Irc/Message.hs
index e92a9d0..8ab23d8 100644
--- a/HsbotIrcBot/Hsbot/Irc/Message.hs
+++ b/HsbotIrcBot/Hsbot/Irc/Message.hs
@@ -41,7 +41,7 @@ pMsg = do
     cmd <- pCommand
     params <- many (char ' ' >> (pLongParam <|> pShortParam))
     _ <- char '\r'
-    --eof
+    eof
     return $ IrcMsg pfx cmd params
 
 pPrefix :: ParsecT String u Identity [Char]
diff --git a/HsbotIrcBot/Hsbot/Irc/Plugin.hs b/HsbotIrcBot/Hsbot/Irc/Plugin.hs
index 2c8e84b..40facbe 100644
--- a/HsbotIrcBot/Hsbot/Irc/Plugin.hs
+++ b/HsbotIrcBot/Hsbot/Irc/Plugin.hs
@@ -47,7 +47,6 @@ loadIrcPlugin pluginName = do
                         "Quote" -> ircBotPluginQuote
                         _       -> ircBotPluginDummy
     let oldPlugins    = ircBotPlugins ircbot
-        oldResumeData = ircBotResumeData ircbot
     -- We check for unicity
     case M.lookup pluginName oldPlugins of
         Just _  -> return ()
@@ -58,9 +57,7 @@ loadIrcPlugin pluginName = do
                                                , ircPluginChan       = pluginChan
                                                , ircPluginMasterChan = masterChan }
                 newPlugins    = M.insert pluginName (plugin, mvar, threadId) oldPlugins
-                newResumeData = M.insert "PLUGINS" (show $ M.keys newPlugins) oldResumeData
-            put $ ircbot { ircBotPlugins    = newPlugins
-                         , ircBotResumeData = newResumeData }
+            put $ ircbot { ircBotPlugins    = newPlugins }
 
 -- | Sends a list of loaded plugins
 listPlugins :: IrcMsg -> String -> IrcBot ()
@@ -73,13 +70,7 @@ listPlugins originalRequest dest = do
 
 -- | Unloads a plugin
 unloadIrcPlugin :: String -> IrcBot ()
-unloadIrcPlugin name = do
-    killIrcPlugin name
-    ircbot <- get
-    let oldResumeData = ircBotResumeData ircbot
-        newPlugins    = M.keys $ ircBotPlugins ircbot
-        newResumeData = M.insert "PLUGINS" (show newPlugins) oldResumeData
-    put $ ircbot { ircBotResumeData = newResumeData }
+unloadIrcPlugin name = killIrcPlugin name
 
 -- | kills a plugin
 killIrcPlugin :: String -> IrcBot ()
diff --git a/HsbotIrcBot/Hsbot/Irc/Types.hs b/HsbotIrcBot/Hsbot/Irc/Types.hs
index 63411df..e7a64ea 100644
--- a/HsbotIrcBot/Hsbot/Irc/Types.hs
+++ b/HsbotIrcBot/Hsbot/Irc/Types.hs
@@ -1,5 +1,6 @@
 module Hsbot.Irc.Types
-    ( IrcBot
+    ( BotStatus (..)
+    , IrcBot
     , IrcBotState (..)
     , IrcServer
     , IrcServerState (..)
@@ -14,7 +15,6 @@ import System.IO
 import Hsbot.Irc.Config
 import Hsbot.Irc.Message
 import Hsbot.Irc.Plugin.Utils
-import Hsbot.Types
 
 -- | The Ircbot monad
 type IrcBot = StateT IrcBotState IO
@@ -24,13 +24,14 @@ data IrcBotState = IrcBotState
     { ircBotPlugins              :: M.Map String (IrcPluginState, MVar (), ThreadId) -- Loaded plugins
     , ircBotCommands             :: M.Map String [String]   -- Loaded plugins
     , ircBotChan                 :: Chan IrcBotMsg          -- The IrcBot's communication channel
-    , ircBotMasterChan           :: Chan BotMsg             -- The Hsbot communication channel
     , ircBotServerState          :: IrcServerState          -- The state of the IrcServer
     , ircBotHandle               :: Handle                  -- The server's socket/handle
     , ircBotConfig               :: IrcConfig               -- The starting configuration
-    , ircBotResumeData           :: ResumeData              -- the necessary data to resume the bot's operations on reboot
     }
 
+-- | how we exit from the botLoop
+data BotStatus = BotExit | BotReboot | BotContinue deriving (Eq)
+
 -- | The IrcServer monad
 type IrcServer = StateT IrcServerState IrcBot
 
@@ -46,3 +47,4 @@ data IrcServerState = IrcServerState
 -- | Utilities for triplets
 first :: (a, b, c) -> a
 first (a, _, _) = a
+
diff --git a/HsbotIrcBot/Main.hs b/HsbotIrcBot/Main.hs
index 8c371bd..97e7052 100644
--- a/HsbotIrcBot/Main.hs
+++ b/HsbotIrcBot/Main.hs
@@ -8,6 +8,7 @@ import System.IO
 
 import Hsbot.Irc.CLI
 import Hsbot.Irc.Config
+import Hsbot.Irc.Core
 
 -- | Main function
 main :: IO ()
@@ -26,4 +27,8 @@ main = do
     -- We find and parse the config file
     ircConfig <- getIrcConfig $ optConfigFile opts
     when (optDebug opts) . putStrLn $ "[hsbot-irc] Compiled config :\n" ++ (show ircConfig)
+    -- Finally we get into the ircbot stuff
+    case optDebug opts of
+        True -> startIrcbot opts ircConfig
+        False -> startIrcbot opts ircConfig -- TODO : fork process in background
 
diff --git a/HsbotIrcBot/hsbot-irc.cabal b/HsbotIrcBot/hsbot-irc.cabal
index c19038f..818d20e 100644
--- a/HsbotIrcBot/hsbot-irc.cabal
+++ b/HsbotIrcBot/hsbot-irc.cabal
@@ -22,8 +22,16 @@ Executable hsbot-irc
   Extensions:       DeriveDataTypeable ScopedTypeVariables
   Build-depends:    base >= 4.1 && < 5,
                     ConfigFile,
+                    containers,
+                    directory,
+                    filepath,
+                    haskell98,
                     MissingH,
                     mtl,
                     network,
+                    parsec >= 3,
+                    random,
+                    text,
+                    time,
                     unix
 
-- 
cgit v1.2.3