{-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} -- | The guts of lambdabot. -- -- The LB/Lambdabot monad -- Generic server connection,disconnection -- The module typeclass, type and operations on modules module Lambdabot.Bot ( ircLoadModule , ircUnloadModule , checkPrivs , checkIgnore , ircCodepage , ircGetChannels , ircQuit , ircReconnect , ircPrivmsg , ircPrivmsg' ) where import Lambdabot.ChanName import Lambdabot.Config import Lambdabot.Config.Core import Lambdabot.IRC import Lambdabot.Logging import Lambdabot.Message import Lambdabot.Module import Lambdabot.Monad import Lambdabot.Nick import Lambdabot.State import Control.Concurrent import Control.Exception.Lifted as E import Control.Monad.Error import Control.Monad.Reader import Control.Monad.State import qualified Data.Map as M import Data.Random.Source import qualified Data.Set as S ------------------------------------------------------------------------ -- -- | Register a module in the irc state -- ircLoadModule :: String -> Module st -> LB () ircLoadModule mName m = do infoM ("Loading module " ++ show mName) savedState <- readGlobalState m mName mState <- maybe (moduleDefState m) return savedState mInfo <- registerModule mName m mState flip runModuleT mInfo (do moduleInit m registerCommands =<< moduleCmds m) `E.catch` \e@SomeException{} -> do errorM ("Module " ++ show mName ++ " failed to load. Exception thrown: " ++ show e) unregisterModule mName fail "Refusing to load due to a broken plugin" -- -- | Unregister a module's entry in the irc state -- ircUnloadModule :: String -> LB () ircUnloadModule mName = do infoM ("Unloading module " ++ show mName) inModuleNamed mName (fail "module not loaded") $ do m <- asks theModule when (moduleSticky m) $ fail "module is sticky" moduleExit m `E.catch` \e@SomeException{} -> errorM ("Module " ++ show mName ++ " threw the following exception in moduleExit: " ++ show e) writeGlobalState unregisterModule mName ------------------------------------------------------------------------ -- | Checks whether the given user has admin permissions checkPrivs :: IrcMessage -> LB Bool checkPrivs msg = gets (S.member (nick msg) . ircPrivilegedUsers) -- | Checks whether the given user is being ignored. -- Privileged users can't be ignored. checkIgnore :: IrcMessage -> LB Bool checkIgnore msg = liftM2 (&&) (liftM not (checkPrivs msg)) (gets (S.member (nick msg) . ircIgnoredUsers)) ------------------------------------------------------------------------ -- Some generic server operations -- Send a CODEPAGE command to set encoding for current session. -- Some IRC networks don't provide UTF-8 ports, but allow -- switching it in runtime ircCodepage :: String -> String -> LB () ircCodepage svr cpage = do send $ codepage svr cpage ircGetChannels :: LB [Nick] ircGetChannels = (map getCN . M.keys) `fmap` gets ircChannels -- Send a quit message, settle and wait for the server to drop our -- handle. At which point the main thread gets a closed handle eof -- exceptoin, we clean up and go home ircQuit :: String -> String -> LB () ircQuit svr msg = do modify $ \state' -> state' { ircPersists = M.delete svr $ ircPersists state' } send $ quit svr msg liftIO $ threadDelay 1000 noticeM "Quitting" ircReconnect :: String -> String -> LB () ircReconnect svr msg = do modify $ \state' -> state' { ircPersists = M.insertWith (flip const) svr False $ ircPersists state' } send $ quit svr msg liftIO $ threadDelay 1000 -- | Send a message to a channel\/user, applying all output filters ircPrivmsg :: Nick -- ^ The channel\/user. -> String -- ^ The message. -> LB () ircPrivmsg who msg = do sendlines <- applyOutputFilters who msg w <- getConfig textWidth mapM_ (\s -> ircPrivmsg' who (take w s)) (take 10 sendlines) -- A raw send version (bypasses output filters) ircPrivmsg' :: Nick -> String -> LB () ircPrivmsg' who "" = ircPrivmsg' who " " ircPrivmsg' who msg = send $ privmsg who msg ------------------------------------------------------------------------ monadRandom [d| instance MonadRandom LB where getRandomWord8 = liftIO getRandomWord8 getRandomWord16 = liftIO getRandomWord16 getRandomWord32 = liftIO getRandomWord32 getRandomWord64 = liftIO getRandomWord64 getRandomDouble = liftIO getRandomDouble getRandomNByteInteger n = liftIO (getRandomNByteInteger n) |]