Archived
1
0
Fork 0

Got a working hsbot-irc back online!

This commit is contained in:
Julien Dessaux 2010-08-01 23:29:48 +02:00
parent 03795ac2f7
commit 8c59b45dc7
7 changed files with 59 additions and 111 deletions

View file

@ -12,7 +12,6 @@ import Data.Maybe
import Hsbot.Irc.Message import Hsbot.Irc.Message
import Hsbot.Irc.Plugin import Hsbot.Irc.Plugin
import Hsbot.Irc.Types import Hsbot.Irc.Types
import Hsbot.Types
-- | Registers a plugin's command -- | Registers a plugin's command
registerCommand :: String -> String -> IrcBot () registerCommand :: String -> String -> IrcBot ()
@ -57,18 +56,8 @@ processCoreCommand ircCmd = do
"REGISTER" -> registerCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd) "REGISTER" -> registerCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd)
"UNLOAD" -> unloadIrcPlugin $ ircCmdMsg ircCmd "UNLOAD" -> unloadIrcPlugin $ ircCmdMsg ircCmd
"UNREGISTER" -> unregisterCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd) "UNREGISTER" -> unregisterCommand (ircCmdMsg ircCmd) (ircCmdFrom ircCmd)
"UPDATE" -> processUpdateCommand ircCmd
_ -> return () _ -> return ()
if command' == "REBOOT" if command' == "REBOOT"
then return BotReboot then return BotReboot
else return BotContinue 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 }

View file

@ -10,72 +10,56 @@ import Data.Maybe (fromMaybe)
import Network import Network
import Prelude hiding (catch) import Prelude hiding (catch)
import System.IO 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.Command
import Hsbot.Irc.Config import Hsbot.Irc.Config
import Hsbot.Irc.Message import Hsbot.Irc.Message
import Hsbot.Irc.Plugin import Hsbot.Irc.Plugin
import Hsbot.Irc.Server import Hsbot.Irc.Server
import Hsbot.Irc.Types import Hsbot.Irc.Types
import Hsbot.Types
-- | IrcBot's main entry point -- | IrcBot's main entry point
startIrcbot :: IrcConfig -> Chan BotMsg -> Chan BotMsg -> Maybe String -> IO () startIrcbot :: Options -> IrcConfig -> IO ()
startIrcbot config masterChan myChan txtResumeData = do startIrcbot opts ircConfig = do
let resumeData = case txtResumeData of when (optDebug opts) $ putStrLn "[IrcBot] Opening communication channel... "
Just txtData -> read txtData :: ResumeData -- TODO : catch exception
Nothing -> M.empty :: ResumeData
print resumeData
putStrLn "[IrcBot] Opening communication channel... "
chan <- newChan :: IO (Chan IrcBotMsg) chan <- newChan :: IO (Chan IrcBotMsg)
handle <- case M.lookup "HANDLE" resumeData of when (optDebug opts) . putStrLn $ concat ["[IrcBot] Connecting to ", ircConfigAddress ircConfig, "... "]
Just txtFd -> do handle <- connectTo (ircConfigAddress ircConfig) (ircConfigPort ircConfig)
let fd = read txtFd :: Fd hSetBuffering handle NoBuffering
fdToHandle fd hSetEncoding handle utf8
Nothing -> do when (optDebug opts) $ putStrLn "[IrcBot] Spawning reader thread..."
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..."
myOwnThreadId <- myThreadId myOwnThreadId <- myThreadId
readerThreadId <- forkIO $ ircBotReader handle chan myOwnThreadId readerThreadId <- forkIO $ ircBotReader handle chan myOwnThreadId
masterReaderThreadId <- forkIO $ ircBotMasterReader myChan chan when (optDebug opts) $ putStrLn "[IrcBot] Initializing server connection..."
putStrLn "[IrcBot] Initializing server connection..." let ircServerState = IrcServerState { ircServerId = ircConfigAddress ircConfig
let ircServerState = IrcServerState { ircServerId = ircConfigAddress config
, ircServerChannels = [] , ircServerChannels = []
, ircServerNickname = ircConfigNickname config , ircServerNickname = ircConfigNickname ircConfig
, ircServerCommandPrefix = ircConfigCommandPrefix config , ircServerCommandPrefix = ircConfigCommandPrefix ircConfig
, ircServerChan = chan } , ircServerChan = chan }
ircBotState = IrcBotState { ircBotPlugins = M.empty ircBotState = IrcBotState { ircBotPlugins = M.empty
, ircBotCommands = M.empty , ircBotCommands = M.empty
, ircBotChan = chan , ircBotChan = chan
, ircBotMasterChan = masterChan
, ircBotServerState = ircServerState , ircBotServerState = ircServerState
, ircBotHandle = handle , ircBotHandle = handle
, ircBotConfig = config , ircBotConfig = ircConfig }
, ircBotResumeData = M.singleton "HANDLE" (show fd) } ircBotState' <- execStateT (initBotServerConnection ircConfig) ircBotState
ircBotState' <- execStateT (initBotServerConnection config) ircBotState when (optDebug opts) $ putStrLn "[IrcBot] Spawning plugins..."
putStrLn "[IrcBot] Spawning plugins..."
ircBotState'' <- execStateT spawnIrcPlugins ircBotState' ircBotState'' <- execStateT spawnIrcPlugins ircBotState'
putStrLn "[IrcBot] Entering Core loop... " when (optDebug opts) $ putStrLn "[IrcBot] Entering Core loop... "
ircBotState''' <- (execStateT ircBotLoop ircBotState'') `catches` [ Handler (\ (_ :: IOException) -> return (ircBotState'')) (_, ircBotState''') <- runLoop ircBotState''
, Handler (\ (_ :: AsyncException) -> return (ircBotState'')) ] when (optDebug opts) $ putStrLn "[IrcBot] Killing reader thread..."
putStrLn "[IrcBot] Killing reader threads..."
killThread readerThreadId killThread readerThreadId
killThread masterReaderThreadId when (optDebug opts) $ putStrLn "[IrcBot] Killing active plugins... "
putStrLn "[IrcBot] Killing active plugins... " evalStateT (mapM_ killIrcPlugin . M.keys $ ircBotPlugins ircBotState''') ircBotState'''
let resumeData' = ircBotResumeData ircBotState''' where
ircPlugins = read (fromMaybe [] (M.lookup "PLUGINS" resumeData')) :: [String] runLoop :: IrcBotState -> IO (BotStatus, IrcBotState)
evalStateT (mapM_ killIrcPlugin ircPlugins) ircBotState''' runLoop botState = do
return () (status, botState') <- (runStateT ircBotCore botState) `catches` [ Handler (\ (_ :: IOException) -> return (BotExit, botState))
, Handler (\ (_ :: AsyncException) -> return (BotExit, botState)) ]
--resumeIrcBot case status of
--resumeIrcBot BotContinue -> runLoop botState'
_ -> return (status, botState')
-- | Runs the IrcBot's reader loop -- | Runs the IrcBot's reader loop
ircBotReader :: Handle -> Chan IrcBotMsg -> ThreadId -> IO () ircBotReader :: Handle -> Chan IrcBotMsg -> ThreadId -> IO ()
@ -93,13 +77,6 @@ ircBotReader handle chan fatherThreadId = forever $ do
killThread myId killThread myId
return "" 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 -- | Initialize the bot's server connection
initBotServerConnection :: IrcConfig -> IrcBot () initBotServerConnection :: IrcConfig -> IrcBot ()
initBotServerConnection config = do initBotServerConnection config = do
@ -109,28 +86,24 @@ initBotServerConnection config = do
put $ ircBot { ircBotServerState = ircServerState' } put $ ircBot { ircBotServerState = ircServerState' }
-- | Run the IrcBot's main loop -- | Run the IrcBot's main loop
ircBotLoop :: IrcBot () ircBotCore :: IrcBot (BotStatus)
ircBotLoop = forever $ do ircBotCore = do
chan <- gets ircBotChan chan <- gets ircBotChan
msg <- liftIO $ readChan chan msg <- liftIO $ readChan chan
case msg of case msg of
InIrcMsg inIrcMsg -> dispatchMessage $ InIrcMsg inIrcMsg InIrcMsg inIrcMsg -> dispatchMessage $ InIrcMsg inIrcMsg
OutIrcMsg outIrcMsg -> sendThisMessage outIrcMsg OutIrcMsg outIrcMsg -> sendThisMessage outIrcMsg
IntIrcCmd intIrcCmd -> do IntIrcCmd intIrcCmd -> processInternalCommand $ IntIrcCmd intIrcCmd
reboot <- processInternalCommand $ IntIrcCmd intIrcCmd
reportUpdate
if reboot == BotReboot
then processRebootCommand
else return ()
where where
sendThisMessage :: IrcMsg -> IrcBot () sendThisMessage :: IrcMsg -> IrcBot (BotStatus)
sendThisMessage outputMsg = do sendThisMessage outputMsg = do
let str = serializeIrcMsg outputMsg let str = serializeIrcMsg outputMsg
handle <- gets ircBotHandle handle <- gets ircBotHandle
liftIO $ hPutStr handle (str ++ "\r\n") liftIO $ hPutStr handle (str ++ "\r\n")
return BotContinue
-- | Dispatches an input message -- | Dispatches an input message
dispatchMessage :: IrcBotMsg -> IrcBot () dispatchMessage :: IrcBotMsg -> IrcBot (BotStatus)
dispatchMessage (InIrcMsg inIrcMsg) = do dispatchMessage (InIrcMsg inIrcMsg) = do
config <- gets ircBotConfig config <- gets ircBotConfig
plugins <- gets ircBotPlugins plugins <- gets ircBotPlugins
@ -143,6 +116,7 @@ dispatchMessage (InIrcMsg inIrcMsg) = do
in mapM_ (sendRunCommand (tail getMsgContent) . first) plugins' in mapM_ (sendRunCommand (tail getMsgContent) . first) plugins'
else else
mapM_ (sendToPlugin (InIrcMsg inIrcMsg) . first) (M.elems plugins) mapM_ (sendToPlugin (InIrcMsg inIrcMsg) . first) (M.elems plugins)
return BotContinue
where where
isPluginCommand :: IrcConfig -> Bool isPluginCommand :: IrcConfig -> Bool
isPluginCommand config = isPluginCommand config =
@ -152,26 +126,5 @@ dispatchMessage (InIrcMsg inIrcMsg) = do
sendRunCommand cmd plugin = sendToPlugin (IntIrcCmd $ IrcCmd "RUN" "CORE" (ircPluginName plugin) cmd inIrcMsg) plugin sendRunCommand cmd plugin = sendToPlugin (IntIrcCmd $ IrcCmd "RUN" "CORE" (ircPluginName plugin) cmd inIrcMsg) plugin
getMsgContent :: String getMsgContent :: String
getMsgContent = unwords . tail $ ircMsgParameters inIrcMsg getMsgContent = unwords . tail $ ircMsgParameters inIrcMsg
dispatchMessage _ = return () dispatchMessage _ = return (BotContinue)
-- | 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

View file

@ -41,7 +41,7 @@ pMsg = do
cmd <- pCommand cmd <- pCommand
params <- many (char ' ' >> (pLongParam <|> pShortParam)) params <- many (char ' ' >> (pLongParam <|> pShortParam))
_ <- char '\r' _ <- char '\r'
--eof eof
return $ IrcMsg pfx cmd params return $ IrcMsg pfx cmd params
pPrefix :: ParsecT String u Identity [Char] pPrefix :: ParsecT String u Identity [Char]

View file

@ -47,7 +47,6 @@ loadIrcPlugin pluginName = do
"Quote" -> ircBotPluginQuote "Quote" -> ircBotPluginQuote
_ -> ircBotPluginDummy _ -> ircBotPluginDummy
let oldPlugins = ircBotPlugins ircbot let oldPlugins = ircBotPlugins ircbot
oldResumeData = ircBotResumeData ircbot
-- We check for unicity -- We check for unicity
case M.lookup pluginName oldPlugins of case M.lookup pluginName oldPlugins of
Just _ -> return () Just _ -> return ()
@ -58,9 +57,7 @@ loadIrcPlugin pluginName = do
, ircPluginChan = pluginChan , ircPluginChan = pluginChan
, ircPluginMasterChan = masterChan } , ircPluginMasterChan = masterChan }
newPlugins = M.insert pluginName (plugin, mvar, threadId) oldPlugins newPlugins = M.insert pluginName (plugin, mvar, threadId) oldPlugins
newResumeData = M.insert "PLUGINS" (show $ M.keys newPlugins) oldResumeData put $ ircbot { ircBotPlugins = newPlugins }
put $ ircbot { ircBotPlugins = newPlugins
, ircBotResumeData = newResumeData }
-- | Sends a list of loaded plugins -- | Sends a list of loaded plugins
listPlugins :: IrcMsg -> String -> IrcBot () listPlugins :: IrcMsg -> String -> IrcBot ()
@ -73,13 +70,7 @@ listPlugins originalRequest dest = do
-- | Unloads a plugin -- | Unloads a plugin
unloadIrcPlugin :: String -> IrcBot () unloadIrcPlugin :: String -> IrcBot ()
unloadIrcPlugin name = do unloadIrcPlugin name = killIrcPlugin name
killIrcPlugin name
ircbot <- get
let oldResumeData = ircBotResumeData ircbot
newPlugins = M.keys $ ircBotPlugins ircbot
newResumeData = M.insert "PLUGINS" (show newPlugins) oldResumeData
put $ ircbot { ircBotResumeData = newResumeData }
-- | kills a plugin -- | kills a plugin
killIrcPlugin :: String -> IrcBot () killIrcPlugin :: String -> IrcBot ()

View file

@ -1,5 +1,6 @@
module Hsbot.Irc.Types module Hsbot.Irc.Types
( IrcBot ( BotStatus (..)
, IrcBot
, IrcBotState (..) , IrcBotState (..)
, IrcServer , IrcServer
, IrcServerState (..) , IrcServerState (..)
@ -14,7 +15,6 @@ import System.IO
import Hsbot.Irc.Config import Hsbot.Irc.Config
import Hsbot.Irc.Message import Hsbot.Irc.Message
import Hsbot.Irc.Plugin.Utils import Hsbot.Irc.Plugin.Utils
import Hsbot.Types
-- | The Ircbot monad -- | The Ircbot monad
type IrcBot = StateT IrcBotState IO type IrcBot = StateT IrcBotState IO
@ -24,13 +24,14 @@ data IrcBotState = IrcBotState
{ ircBotPlugins :: M.Map String (IrcPluginState, MVar (), ThreadId) -- Loaded plugins { ircBotPlugins :: M.Map String (IrcPluginState, MVar (), ThreadId) -- Loaded plugins
, ircBotCommands :: M.Map String [String] -- Loaded plugins , ircBotCommands :: M.Map String [String] -- Loaded plugins
, ircBotChan :: Chan IrcBotMsg -- The IrcBot's communication channel , ircBotChan :: Chan IrcBotMsg -- The IrcBot's communication channel
, ircBotMasterChan :: Chan BotMsg -- The Hsbot communication channel
, ircBotServerState :: IrcServerState -- The state of the IrcServer , ircBotServerState :: IrcServerState -- The state of the IrcServer
, ircBotHandle :: Handle -- The server's socket/handle , ircBotHandle :: Handle -- The server's socket/handle
, ircBotConfig :: IrcConfig -- The starting configuration , 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 -- | The IrcServer monad
type IrcServer = StateT IrcServerState IrcBot type IrcServer = StateT IrcServerState IrcBot
@ -46,3 +47,4 @@ data IrcServerState = IrcServerState
-- | Utilities for triplets -- | Utilities for triplets
first :: (a, b, c) -> a first :: (a, b, c) -> a
first (a, _, _) = a first (a, _, _) = a

View file

@ -8,6 +8,7 @@ import System.IO
import Hsbot.Irc.CLI import Hsbot.Irc.CLI
import Hsbot.Irc.Config import Hsbot.Irc.Config
import Hsbot.Irc.Core
-- | Main function -- | Main function
main :: IO () main :: IO ()
@ -26,4 +27,8 @@ main = do
-- We find and parse the config file -- We find and parse the config file
ircConfig <- getIrcConfig $ optConfigFile opts ircConfig <- getIrcConfig $ optConfigFile opts
when (optDebug opts) . putStrLn $ "[hsbot-irc] Compiled config :\n" ++ (show ircConfig) 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

View file

@ -22,8 +22,16 @@ Executable hsbot-irc
Extensions: DeriveDataTypeable ScopedTypeVariables Extensions: DeriveDataTypeable ScopedTypeVariables
Build-depends: base >= 4.1 && < 5, Build-depends: base >= 4.1 && < 5,
ConfigFile, ConfigFile,
containers,
directory,
filepath,
haskell98,
MissingH, MissingH,
mtl, mtl,
network, network,
parsec >= 3,
random,
text,
time,
unix unix