packages feed

mahoro-0.1.1: Main.hs

-- | Main.hs
-- Main module, contains running MACID and XMPP.

import Config
import Threads
import Help
import DB
import DBState

import Happstack.State
import Control.Concurrent
import System.Time
import Network
import Network.XMPP

import Data.Char
import Data.List
import Text.Regex.Posix


-- | Read config, run macid and run XMPP in another thread.
main = do
    cfs <- readConfig ".mahororc"
    putStrLn . ( "starting happs stare" ++ ) =<< time
    control <- startSystemState stateProxy -- start the HAppS state system

    putStrLn . ( "happs state started" ++ ) =<< time
    tid <- forkIO $ doNetwork cfs
    putStrLn . ( ( "XMPP started\n" ++
               "shut down with ctrl-c" ) ++) =<< time
    waitForTermination
    killThread tid
    putStrLn . ( "creating checkpoint: " ++ ) =<< time
    createCheckpoint control

    putStrLn .  ( "shutting down system: " ++ ) =<< time
    shutdownSystem control 
    putStrLn .  ( "exiting: " ++ ) =<< time
  where
    time = return . ("\ntime: " ++ ) . show  =<< getClockTime
    stateProxy :: Proxy DBWrap
    stateProxy = Proxy


-- | Open connection and run XMPP.
doNetwork :: (Config, Chans) -> IO ()
doNetwork (cf, chans) = withSocketsDo $ do
    c <- openStream (cserver cf)
    getStreamStart c

    runXMPP c $ do
       err <- startAuth (cusername cf) (cserver cf)
                       (cpassword cf) "mahoro"
       if err /= 0
         then error $ "can't auth: "++(show err)++" error"
         else liftIO $ putStrLn "connected"
       sendPresence
       handleVersion "mahoro: chans to xmpp gate" (cversion cf) "PumpleOS"
       liftIO $ forkIO $ runLoop c cf chans

       addHandler (isChat `conj` hasBody) (messageCB chans) True
       addHandler (isPresence `conj` isSubscribe) subscribeCB True
    closeConnection c
  where
    isSubscribe stanza = attributeMatches "type" (=="subscribe") stanza


-- | Subscribed user if he wants.
subscribeCB :: StanzaHandler
subscribeCB stanza = do
    let from = maybe "" id (getAttr "from" stanza)
    sendStanza $ XML "presence" [("to", from), ("type", "subscribed")] []


-- | Parse commands.
messageCB :: Chans -> StanzaHandler
messageCB chans stanza = do
    let from = maybe "" id (getAttr "from" stanza)
        jid = getBareJid from
        body = maybe "" id (getMessageBody stanza)

        args = words body
        args' = if null args
                  then args
                  else (map toLower $ head args):(tail args)

        parseArg arg f =
          case (readMaybe arg)::Maybe Int of
            Just val | val > 0 -> do
              db <- query GetDB
              let threads = getUserThreads (JID jid) db
              if val <= length threads
                then update $ f (fst $ threads!!(val - 1)) (JID jid)
                else return "Wrong number. Print `ls'."
            _ ->
              update $ f (URL arg) (JID jid)

    answer <- case args' of
        ["touch", url] ->
            if isCorrectURL url chans
              then update $ AddThread' (URL $ fixURL url) (JID jid)
              else return "Wrong url. Print `lsmod'."
        ["touch", url, descr] ->
            if isCorrectURL url chans
              then do let u = (URL $ fixURL url)
                          j = (JID jid)
                      update $ AddThread' u j
                      update $ SetDescription' descr u j
              else return "Wrong url. Print `lsmod'."

        ["rm", arg] -> parseArg arg DelThread'
        
        ["ln", arg] -> parseArg arg (SetDescription' "")
        ["ln", arg, descr] -> parseArg arg (SetDescription' descr)

        ["ls"] -> do
            db <- query GetDB
            let threads = getUserThreads (JID jid) db
                showTs = intercalate "\n" . map showL . zip [1..]
                showL (n, (u, d)) = show n++". "++show u++" "++d
            return $ "You've subscribed on:\n"++showTs threads

        ["lsmod"] -> do
            let chs = map (\(r, c) -> cname c++" ("++r++")") chans
            return $ "Avialable chans:\n"++intercalate "\n" chs

        ["logout"] -> do
            update $ Bye' (JID jid)
            sendStanza $ XML "presence" [("to", from),("type", "unsubscribed")]
                                        []
            return "Bye-bye."

        ["help"] -> return help
        ["info"] -> return info

        ["nya"] -> return $ "Nya-nya nya-nya nihao nya coda "++
                           "tsugeraha tsude karu saa!"

        -- temporary hack for db maintenance
        ["dump"] | jid == "anon0@jabber.ru" ->
            return . dumpDB =<< query GetDB

        _ -> return "Nyaaaa? Print \"help\"."

    -- block
    liftIO $ waitQSem q
    -- send
    if null answer
      then return ()
      else sendMessage from answer
    -- unblock
    liftIO $ signalQSem q


-- Helpers.

fixURL :: String -> String
fixURL url = if '#' `elem` url
               then reverse $ tail $ dropWhile (/='#') $ reverse url
               else url

isCorrectURL :: String -> Chans -> Bool
isCorrectURL url = any (\r -> (url =~ fst r)::Bool)

readMaybe :: (Read a) => String -> Maybe a
readMaybe str = if (not $ null rd) && (null leftStr) then Just val else Nothing
    where rd@(~[(val, leftStr)]) = reads str