added state monad and it is possible now to save key,values
This commit is contained in:
parent
46ff474bfd
commit
caef7b2ca5
1 changed files with 202 additions and 26 deletions
228
cnb.hs
228
cnb.hs
|
@ -3,46 +3,222 @@ import System.IO
|
||||||
import Text.Printf
|
import Text.Printf
|
||||||
import Data.List
|
import Data.List
|
||||||
import System.Exit
|
import System.Exit
|
||||||
|
import Control.Monad.State
|
||||||
|
import Text.Regex.Posix
|
||||||
|
import Data.Char (isSpace)
|
||||||
|
|
||||||
server = "irc.epd-me.net"
|
server = "irc.epd-me.net"
|
||||||
port = 6667
|
port = 6667
|
||||||
chan = "#jeena"
|
chan = "#selfhtml"
|
||||||
nick = "cnb"
|
nick = "cnb"
|
||||||
|
rname = "clynx Nerv-Bot"
|
||||||
|
|
||||||
|
type Nick = String
|
||||||
|
data Type = Kick | Invite | Privmsg | Unknown
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
type Text = String
|
||||||
|
data Message = Message Nick Type Text
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
type Key = String
|
||||||
|
type Value = String
|
||||||
|
type Store = [(Key, Value)]
|
||||||
|
|
||||||
|
type MyState = (Store, Handle)
|
||||||
|
type MyStateM = StateT MyState IO
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
h <- connectTo server (PortNumber (fromIntegral port))
|
h <- connectTo server (PortNumber (fromIntegral port))
|
||||||
hSetBuffering h NoBuffering
|
hSetBuffering h NoBuffering
|
||||||
write h "NICK" nick
|
let state = (startStore, h)
|
||||||
write h "USER" (nick ++ " 0 * :clynx Nerv-Bot")
|
runStateT (login >> listen) state >> return ()
|
||||||
write h "JOIN" chan
|
|
||||||
listen h
|
|
||||||
|
|
||||||
write :: Handle -> String -> String -> IO ()
|
login :: MyStateM ()
|
||||||
write h s t = do
|
login = do
|
||||||
hPrintf h "%s %s\r\n" s t
|
write "NICK" nick
|
||||||
printf "> %s %s\n" s t
|
write "USER" (nick ++ " 0 * :" ++ rname)
|
||||||
|
joinChan
|
||||||
|
return ()
|
||||||
|
|
||||||
listen :: Handle -> IO ()
|
joinChan :: MyStateM ()
|
||||||
listen h = forever $ do
|
joinChan = do
|
||||||
t <- hGetLine h
|
write "JOIN" chan
|
||||||
|
|
||||||
|
getHandle :: MyStateM (Handle)
|
||||||
|
getHandle = do
|
||||||
|
(_, h) <- get
|
||||||
|
return h
|
||||||
|
|
||||||
|
putHandle :: Handle -> MyStateM ()
|
||||||
|
putHandle h = do
|
||||||
|
(s, _) <- get
|
||||||
|
put (s, h)
|
||||||
|
return ()
|
||||||
|
|
||||||
|
getStore :: MyStateM (Store)
|
||||||
|
getStore = do
|
||||||
|
(s, _) <- get
|
||||||
|
return s
|
||||||
|
|
||||||
|
putStore :: Store -> MyStateM ()
|
||||||
|
putStore s = do
|
||||||
|
(_, h) <- get
|
||||||
|
put (s, h)
|
||||||
|
return ()
|
||||||
|
|
||||||
|
putKeyValue :: String -> String -> MyStateM ()
|
||||||
|
putKeyValue k v = do
|
||||||
|
s <- getStore
|
||||||
|
putStore (s ++ [(k,v)])
|
||||||
|
return ()
|
||||||
|
|
||||||
|
deleteKeyValue :: String -> MyStateM ()
|
||||||
|
deleteKeyValue key = do
|
||||||
|
s <- getStore
|
||||||
|
let sn = [(k,v) | (k,v) <- s, k /= key]
|
||||||
|
putStore (sn)
|
||||||
|
|
||||||
|
match :: String -> MyStateM (Maybe Value)
|
||||||
|
match s = do
|
||||||
|
st <- getStore
|
||||||
|
case (find (\(key, value) -> s =~ key) st) of
|
||||||
|
Nothing -> return Nothing
|
||||||
|
Just (k,v) -> return (Just v)
|
||||||
|
|
||||||
|
write :: String -> String -> MyStateM ()
|
||||||
|
write s t = do
|
||||||
|
h <- getHandle
|
||||||
|
liftIO $ hPrintf h "%s %s\r\n" s t
|
||||||
|
|
||||||
|
listen :: MyStateM ()
|
||||||
|
listen = forever $ do
|
||||||
|
h <- getHandle
|
||||||
|
t <- liftIO $ hGetLine h
|
||||||
let s = init t
|
let s = init t
|
||||||
if ping s then pong s else eval h (clean s)
|
if ping s
|
||||||
putStrLn s
|
then pong s
|
||||||
|
else do eval (parse s)
|
||||||
|
liftIO $ putStrLn s
|
||||||
|
return ()
|
||||||
where
|
where
|
||||||
forever a = a >> forever a
|
forever a = a >> forever a
|
||||||
clean = drop 1 . dropWhile (/= ':') . drop 1
|
clean = drop 1 . dropWhile (/= ':') . drop 1
|
||||||
ping x = "PING :" `isPrefixOf` x
|
ping x = "PING :" `isPrefixOf` x
|
||||||
pong x = write h "PONG" (':' : drop 6 x)
|
pong x = write "PONG" (':' : drop 6 x)
|
||||||
|
|
||||||
eval :: Handle -> String -> IO ()
|
eval :: Maybe Message -> MyStateM ()
|
||||||
eval h "!quit" = write h "QUIT" ":Exiting" >> exitWith ExitSuccess
|
eval (Just m@(Message n Privmsg te)) = evalPrivmsg (Just m)
|
||||||
eval h "!jump" = privmsg h "The quick brown clynx jumps over the lazy oak."
|
eval (Just (Message _ Invite _)) = invited
|
||||||
eval h "!implode" = write h "QUIT" ":whhooooooshhhh ..."
|
eval _ = return ()
|
||||||
eval h "!explode" = write h "QUIT" ":kaBOOooOOOOoommm ..."
|
|
||||||
eval h x | (nick ++ ":") `isPrefixOf` x = privmsg h "Halt die Fresse!"
|
|
||||||
eval h x | and [(not (isInfixOf "@clynx" x)), (isInfixOf "clynx" x)] =
|
|
||||||
privmsg h "Der clynx kann nix!"
|
|
||||||
eval _ _ = return ()
|
|
||||||
|
|
||||||
privmsg :: Handle -> String -> IO ()
|
evalPrivmsg :: Maybe Message -> MyStateM ()
|
||||||
privmsg h s = write h "PRIVMSG" (chan ++ " :" ++s)
|
evalPrivmsg Nothing = return ()
|
||||||
|
evalPrivmsg (Just m@(Message n _ te)) = do
|
||||||
|
let pat = nick ++ ":(.*)"
|
||||||
|
let r = te =~ pat :: [[String]]
|
||||||
|
case r of
|
||||||
|
[] -> do m <- match te
|
||||||
|
case m of
|
||||||
|
Just v -> privmsg v
|
||||||
|
Nothing -> return ()
|
||||||
|
a -> do let s = last $ head a
|
||||||
|
action (trim s) m
|
||||||
|
return ()
|
||||||
|
|
||||||
|
action :: String -> Message -> MyStateM ()
|
||||||
|
action s (Message n _ _) | "add " `isPrefixOf` s = do
|
||||||
|
let (k,v) = parseKeyVal $ rest "add" s
|
||||||
|
putKeyValue k v
|
||||||
|
msg (k ++ "~" ++ v ++ " added") n
|
||||||
|
return ()
|
||||||
|
action s (Message n _ _) | "delete " `isPrefixOf` s = do
|
||||||
|
let k = rest "delete" s
|
||||||
|
deleteKeyValue k
|
||||||
|
msg (k ++ " deleted") n
|
||||||
|
return ()
|
||||||
|
action "list" (Message n _ _) = do
|
||||||
|
st <- getStore
|
||||||
|
mapM (\(k,v) -> msg (k ++ "~" ++ v) n) st
|
||||||
|
return ()
|
||||||
|
action "leave" (Message n _ _) = do
|
||||||
|
leave ("Fuck you " ++ n ++ ", I'm quitting!")
|
||||||
|
action "help" (Message n _ _) = help n
|
||||||
|
action s (Message n _ _) = do
|
||||||
|
case (length s) of
|
||||||
|
0 -> privmsg "What?!"
|
||||||
|
_ -> privmsg (n ++ " is boooring! What the fuck do you want me to do with "
|
||||||
|
++ (show s) ++ "?")
|
||||||
|
|
||||||
|
|
||||||
|
rest :: String -> String -> String
|
||||||
|
rest k s = drop (length k + 1) s
|
||||||
|
|
||||||
|
privmsg :: String -> MyStateM ()
|
||||||
|
privmsg s = write "PRIVMSG" (chan ++ " :" ++ s)
|
||||||
|
|
||||||
|
msg :: String -> Nick -> MyStateM ()
|
||||||
|
msg s n = write "PRIVMSG" (n ++ " :" ++ s)
|
||||||
|
|
||||||
|
leave :: String -> MyStateM ()
|
||||||
|
leave s = do
|
||||||
|
privmsg s
|
||||||
|
write "PART" (chan ++ " :")
|
||||||
|
|
||||||
|
invited :: MyStateM ()
|
||||||
|
invited = joinChan
|
||||||
|
|
||||||
|
parse :: String -> Maybe Message
|
||||||
|
parse s = do
|
||||||
|
-- :Jeena!~Jeena@host-3AB762DD.defunced.de PRIVMSG #jeena :test
|
||||||
|
let pat = ":([a-zA-Z0-9].+)!.+ ([A-Z].+) " ++ chan ++ " :(.*)"
|
||||||
|
let q = s =~ pat :: [[String]]
|
||||||
|
-- :Jeena!~Jeena@host-3AB762DD.defunced.de INVITE cnb :#jeena
|
||||||
|
let pat2 = ":([a-zA-Z0-9].+)!.+ ([A-Z].+) " ++ nick ++ " :" ++ chan
|
||||||
|
let q2 = s =~ pat2 :: [[String]]
|
||||||
|
case q of
|
||||||
|
[] -> do case q2 of
|
||||||
|
[] -> Nothing
|
||||||
|
r2 -> do let a2 = tail $ head r2
|
||||||
|
case (head $ tail a2) of
|
||||||
|
"INVITE" -> Just (Message (head a2) Invite (last a2))
|
||||||
|
_ -> Nothing
|
||||||
|
r -> do let a = tail $ head r
|
||||||
|
case (head $ tail a) of
|
||||||
|
"KICK" -> Just (Message (head a) Kick (last a))
|
||||||
|
"PRIVMSG" -> Just (Message (head a) Privmsg (last a))
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
parseKeyVal :: String -> (Key, Value)
|
||||||
|
parseKeyVal s = do
|
||||||
|
let a = wordsBy (=='~') s
|
||||||
|
(head a, unwords $ tail a)
|
||||||
|
|
||||||
|
wordsBy :: (a -> Bool) -> [a] -> [[a]]
|
||||||
|
wordsBy sep str = s_skip str
|
||||||
|
where s_skip [] = []
|
||||||
|
s_skip (c:cs) = if sep c then s_skip cs else s_word cs [c]
|
||||||
|
s_word [] w = [reverse w]
|
||||||
|
s_word (c:cs) w = if sep c then reverse w : s_skip cs
|
||||||
|
else s_word cs (c:w)
|
||||||
|
|
||||||
|
trim :: String -> String
|
||||||
|
trim = f . f
|
||||||
|
where f = reverse . dropWhile isSpace
|
||||||
|
|
||||||
|
help :: Nick -> MyStateM ()
|
||||||
|
help n = do
|
||||||
|
privmsg "Hilf dir selbst, dann hilft dir Gott."
|
||||||
|
msg ("usage:") n
|
||||||
|
msg (" " ++ nick ++ ": help -> this help") n
|
||||||
|
msg (" " ++ nick ++ ": add regex~value -> adds a value for a regex") n
|
||||||
|
msg (" " ++ nick ++ ": delete regex -> deletes the regex-value pair") n
|
||||||
|
msg (" " ++ nick ++ ": list -> lists all available regex-value pairs") n
|
||||||
|
msg (" " ++ nick ++ ": leave -> bot leaves the chanel") n
|
||||||
|
msg (" /invite " ++ nick ++ " " ++ chan ++ " -> invites the bot back to the chanel") n
|
||||||
|
|
||||||
|
startStore :: Store
|
||||||
|
startStore = [
|
||||||
|
("jump", "The quick brown clynx jumps over the lazy oak.")
|
||||||
|
]
|
Loading…
Add table
Add a link
Reference in a new issue