summaryrefslogtreecommitdiff
path: root/HsbotIrcBot/Hsbot/Irc/Config.hs
blob: 186f1395c709fff6d42a001df377e258fa9679c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
module Hsbot.Irc.Config
    ( IrcConfig(..)
    , ircDefaultConfig
    , getIrcConfig
    ) where

import Control.Monad.Error
import Data.Char (isDigit)
import qualified Data.ConfigFile as C
import Data.Either.Utils
import Network
import System.Exit
import System.Posix.Files

-- | Configuration data type
data IrcConfig = IrcConfig
    { ircConfigAddress       :: String   -- the server's address
    , ircConfigPort          :: PortID   -- the server's port
    , ircConfigChannels      :: [String] -- the Channels to join on start
    , ircConfigNickname      :: String   -- the hsbot's nickname
    , ircConfigPassword      :: String   -- the hsbot's password, optional
    , ircConfigRealname      :: String   -- the hsbot's real name, optional
    , ircConfigCommandPrefix :: Char     -- the prefix the ircbot will recognize as commands
    , ircConfigPlugins       :: [String] -- the ircPlugins to load
    }

instance Show IrcConfig where
    show (IrcConfig address port channels nickname password realname commandPrefix plugins) = unlines $
        concat [ "Address: ", address ] :
        concat [ "Port: ", case port of
                            PortNumber num -> show num
                            Service s      -> show s
                            UnixSocket u   -> show u ] :
        concat [ "Channels: ", show channels ] :
        concat [ "Nickname: ", nickname ] :
        concat [ "Password: ", password ] :
        concat [ "Realname: ", realname ] :
        concat [ "CommandPrefix: ", show commandPrefix ] :
        [ "Plugins: ", show plugins ]

-- | User configuration
ircDefaultConfig :: IrcConfig
ircDefaultConfig = IrcConfig
    { ircConfigAddress       = "localhost"
    , ircConfigPort          = PortNumber 6667
    , ircConfigChannels      = ["#hsbot"]
    , ircConfigNickname      = "hsbot"
    , ircConfigPassword      = ""
    , ircConfigRealname      = "The One True bot, with it's haskell soul."
    , ircConfigCommandPrefix = '@'
    , ircConfigPlugins       = ["Ping", "Core"]
    }

-- | config file retrieving
getIrcConfig :: Maybe String -> IO (IrcConfig)
getIrcConfig maybePath =
    case maybePath of
        Just path -> do
            doesFileExists <- fileExist path
            case doesFileExists of
                True -> do
                    fileStatus <- getFileStatus path
                    case isRegularFile $ fileStatus of
                        True -> getConfigFromFile path
                        False -> do
                            putStrLn "Invalid configuration file path."
                            exitWith $ ExitFailure 1
                False -> do
                    putStrLn "The specified configuration file does not exists."
                    exitWith $ ExitFailure 1
        Nothing -> return ircDefaultConfig  -- TODO : try defaults like $HOME/.hsbotrc, /etc/hsbotrc or /usr/local/etc/hsbotrc

-- | Get configuration from config file
getConfigFromFile :: FilePath -> IO IrcConfig
getConfigFromFile fname = readfile C.emptyCP fname >>= extractConfig . forceEither

-- | A version of readfile that treats the file as UTF-8
readfile :: MonadError C.CPError m => C.ConfigParser -> FilePath -> IO (m C.ConfigParser)
readfile cp path' = do
    contents <- readFile path'
    return $ C.readstring cp contents

-- | config file processing
extractConfig :: C.ConfigParser -> IO IrcConfig
extractConfig cp = do
    config' <- runErrorT $ do
        cfAddress <- getit "address"
        cfPort <- getit "port"
        cfChannels <- getit "channels"
        cfNickname <- getit "nickname"
        cfPassword <- getit "password"
        cfRealname <- getit "realname"
        cfCommandPrefix <- getit "commandprefix"
        cfPlugins <- getit "plugins"
        return $! IrcConfig {
                      ircConfigAddress       = cfAddress
                    , ircConfigPort          = PortNumber . fromIntegral $ readInteger "port" cfPort
                    , ircConfigChannels      = splitCommaList cfChannels
                    , ircConfigNickname      = cfNickname
                    , ircConfigPassword      = cfPassword
                    , ircConfigRealname      = cfRealname
                    , ircConfigCommandPrefix = readChar "commandprefix" cfCommandPrefix
                    , ircConfigPlugins       = splitCommaList cfPlugins }
    case config' of
         Left (C.ParseError e, e') -> error $ "Parse error: " ++ e ++ "\n" ++ e'
         Left e                  -> error (show e)
         Right c                 -> return c
  where
    getit = C.get cp "IRC"

readChar :: String -> String -> Char
readChar _ x | length x == 1 = head x
readChar opt _ = error $ opt ++ " must be one character long."

readInteger :: String -> String -> Int
readInteger _   x | all isDigit x = read x ::Int
readInteger opt _ = error $ opt ++ " must be an integer."

-- readNumber :: (Num a, Read a) => String -> String -> a
-- readNumber _   x | all isDigit x = read x
-- readNumber opt _ = error $ opt ++ " must be a number."

splitCommaList :: String -> [String]
splitCommaList l =
    let (first, rest) = break (== ',') l
        first' = lrStrip first
    in case rest of
            []     -> if null first' then [] else [first']
            (_:rs) -> first' : splitCommaList rs

lrStrip :: String -> String
lrStrip = reverse . dropWhile isWhitespace . reverse . dropWhile isWhitespace
    where isWhitespace = (`elem` " \t\n")