diff --git a/README.md b/README.md index 9548b34..f2d0385 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ # haskell-basic-irc-bot + + + +## doc + +example.json > .connection.json than you are ready to go diff --git a/app/Main.hs b/app/Main.hs index d6570e0..2d6c087 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,5 +1,5 @@ {-# LANGUAGE MultiWayIf #-} - +{-# LANGUAGE TemplateHaskell #-} import IrcBot.IrcBot as I import IrcBot.JsonConfigDecoder @@ -7,8 +7,9 @@ import qualified Data.ByteString.Lazy as C import Data.Aeson import Data.Maybe import qualified Control.Concurrent as T - - +import IrcBot.Definitions.Options +import IrcBot.Definitions.ServerAddress +import Control.Lens initWithOptions:: Maybe IOptions -> IO() @@ -25,6 +26,9 @@ mainLoop = do T.threadDelay 1000000 mainLoop + +makeLenses ''IServerAddress + main :: IO () main = do diff --git a/example.json b/example.json index 1eb4f85..92fb18d 100644 --- a/example.json +++ b/example.json @@ -1,10 +1,14 @@ { + "admins": [ + "testnick" + ], "servers": [ { "server": "irc.freenode.net", "port": 6697, "ssl": true, "nickname": "hBotTest1", + "password": "password", "channels": [ "#123123123123_test_channel" ] @@ -14,6 +18,7 @@ "port": 6697, "ssl": true, "nickname": "hBotTest", + "password": "password", "channels": [ "#123123123123_test_channel1" ] diff --git a/haskell-basic-irc-bot.cabal b/haskell-basic-irc-bot.cabal index b3819b3..5a9936a 100644 --- a/haskell-basic-irc-bot.cabal +++ b/haskell-basic-irc-bot.cabal @@ -36,6 +36,10 @@ library , IrcBot.IrcBot , IrcBot.RandomFileFinder , IrcBot.BotCustomCommands + , IrcBot.Definitions.ServerResponse + , IrcBot.Definitions.ServerAddress + , IrcBot.Definitions.Options + , IrcBot.Config.ConfigHelper other-modules: Paths_haskell_basic_irc_bot hs-source-dirs: @@ -52,6 +56,8 @@ library , json , aeson , MissingH + , split + , lens default-language: Haskell2010 executable haskell-basic-irc-bot-exe @@ -72,6 +78,8 @@ executable haskell-basic-irc-bot-exe , connection , json , aeson + , split + , lens default-language: Haskell2010 test-suite haskell-basic-irc-bot-test diff --git a/src/IrcBot/BotActions.hs b/src/IrcBot/BotActions.hs index f4d38ea..7db16bc 100644 --- a/src/IrcBot/BotActions.hs +++ b/src/IrcBot/BotActions.hs @@ -10,34 +10,80 @@ import IrcBot.BotNetwork import IrcBot.BotNetworkCommands import IrcBot.MessageParser import IrcBot.BotCustomCommands +import IrcBot.Definitions.ServerResponse +import IrcBot.Definitions.ServerAddress +import qualified Data.ByteString.Lazy as L +import IrcBot.Definitions.Options +import qualified Data.Aeson as A +import Data.Maybe +import qualified Data.List as DL -messageParse:: Connection -> C.ByteString -> IO() + -- its kind of interface for actionFunctions callActionFunction :: (IServerResponse -> IO()) -> IServerResponse -> IO () callActionFunction myFunc parameter1 = myFunc parameter1 -messageParse sock stringData = - do +callByMethodDefinition :: MethodDefinition -> Connection -> IServerResponse -> IO () +callByMethodDefinition methodDefinition sock serverResponse = do + let commandCallerString = (callerString methodDefinition) + let commandMethod = (callableFunction methodDefinition) + if hasStringExist commandCallerString (readDataByteString serverResponse) && (commandType methodDefinition) /= NON_RETURN_ACTION then + do + resultString <- commandMethod serverResponse + writeToSocket sock resultString + + else if hasStringExist commandCallerString (C.pack (messageType serverResponse)) && (commandType methodDefinition) == NON_RETURN_ACTION then + do + resultString <- commandMethod serverResponse + writeToSocket sock resultString + else + print "COMMAND DOESNT EXIST" + + +createServerReponse :: C.ByteString -> IServerAddress -> IO IServerResponse +createServerReponse stringData serverAddress = + do let unpackedByteString = C.unpack stringData let debugData = "READ DATA FROM SOCKET: " ++ unpackedByteString + print debugData + let messageParsedMeta = words $ parseMessageMeta unpackedByteString + let messageType = parseMessageType messageParsedMeta + print messageParsedMeta + + let userNick = parseNick unpackedByteString let channelName = parseChannelName unpackedByteString + + -- let targetNick = parseMessageTargetFromMeta messageParsedMeta let messageText = parseMessageText unpackedByteString - let newServerResponse = IServerResponse { - readDataString = unpackedByteString, - readDataByteString = stringData, - sock = sock, - channelName = channelName, - nick = userNick, - messageText = messageText - } - mapM (\x -> callActionFunction (x) newServerResponse) (methods myMethodList) - let d = "PARSE PURSE MESASGE: " ++ messageText - print d - print debugData + + + let target = targetDeterminer userNick channelName + return $ IServerResponse { + serverName = (server serverAddress), + readDataString = unpackedByteString, + readDataByteString = stringData, + channelName = channelName, + nick = userNick, + messageText = messageText, + messageTarget = target, + messageType = messageType + } + + + +messageParse :: Connection -> C.ByteString -> IServerAddress -> IO() +messageParse sock stringData serverAddress = + do + + + newServerResponse <- createServerReponse stringData serverAddress + determineSockOutputOperation sock newServerResponse + + -- mapM (\x -> callActionFunction (x) newServerResponse) (methods myMethodList) -- if | hasStringExist "PING " stringData -> pong sock unpackedByteString -- | hasStringExist ".sup" stringData -> sendMessage sock channelName ("sup " ++ userNick) -- | hasStringExist " KICK #" stringData -> joinChannel sock channelName >> {-- auto connect in case of kick --} sendMessage sock channelName "fuck you" @@ -46,18 +92,35 @@ messageParse sock stringData = -- | hasStringExist ".leave" stringData -> disconnectFromChannel sock channelName -- | otherwise -> print "NO CONDITION FOUND TO PARSE" +determineSockOutputOperation :: Connection -> IServerResponse -> IO () +determineSockOutputOperation sock newServerResponse = do + + output <- L.readFile ".connection.json" + let option = fromJust (A.decode output :: Maybe IOptions) + if elem (nick newServerResponse) (admins option) + then do + mapM (\x -> callByMethodDefinition x sock newServerResponse) (methods myAdminMethodList) + mapM (\x -> callByMethodDefinition x sock newServerResponse) (methods myMethodList) + print "command from admin" + else do + mapM (\x -> callByMethodDefinition x sock newServerResponse) (methods myMethodList) + print "command from normal user" + + + + connectToServer :: String -> PortNumber -> Bool -> IO Connection connectToServer server port ssl = do open server port ssl -connectionLoop :: Connection -> IO Connection -connectionLoop sock = +connectionLoop :: Connection -> IServerAddress -> IO Connection +connectionLoop sock serverAddress = do stringData <- readFromSocket sock - messageParse sock stringData + messageParse sock stringData serverAddress case stringData == C.empty of False -> do - connectionLoop sock + connectionLoop sock serverAddress True -> do return sock diff --git a/src/IrcBot/BotCustomCommands.hs b/src/IrcBot/BotCustomCommands.hs index 84e8e23..efd9541 100644 --- a/src/IrcBot/BotCustomCommands.hs +++ b/src/IrcBot/BotCustomCommands.hs @@ -7,22 +7,26 @@ import qualified Data.ByteString.Char8 as C import IrcBot.BotNetwork import IrcBot.BotNetworkCommands import IrcBot.MessageParser +import Data.List.Split +import Data.Maybe +import IrcBot.Definitions.ServerResponse +import IrcBot.Definitions.Options +import IrcBot.Definitions.ServerAddress +import qualified Data.Aeson as A +import qualified Data.ByteString.Lazy as L +import IrcBot.JsonConfigDecoder +import GHC.Generics +import IrcBot.Config.ConfigHelper +import System.IO +import System.Directory -data IServerResponse = IServerResponse - { - readDataString :: String, - readDataByteString :: C.ByteString, - sock :: Connection, - channelName :: String, - nick :: String, - messageText :: String - } + -data MethodList = MethodList { methods :: [IServerResponse -> IO()]} +data MethodList = MethodList { methods :: [MethodDefinition]} -hasStringExist:: String -> C.ByteString -> Bool +hasStringExist :: String -> C.ByteString -> Bool hasStringExist search source = do let byteStringSearch = C.pack search @@ -30,43 +34,91 @@ hasStringExist search source = -basicEcho :: IServerResponse -> IO () +basicEcho :: IServerResponse -> IO String basicEcho serverResponse = do - if | hasStringExist ".echo" (readDataByteString serverResponse) -> sendMessage (sock serverResponse) (channelName serverResponse) "EXAMPLE MESSAGE" - | otherwise -> print "NO CONDITION FOUND TO PARSE" + return $ prepareSendMessage (messageTarget serverResponse) "EXAMPLE MESSAGE" -ping :: IServerResponse -> IO () +ping :: IServerResponse -> IO String ping serverResponse = do - if | hasStringExist "PING " (readDataByteString serverResponse) -> pong (sock serverResponse) (readDataString serverResponse) - | otherwise -> print "NO CONDITION FOUND TO PARSE" + return $ preparePong (readDataString serverResponse) +join :: IServerResponse -> IO String +join serverResponse = do + let splittedData = splitOn " " (messageText serverResponse) + output <- L.readFile ".connection.json" + let option = fromJust (A.decode output :: Maybe IOptions) + let myServers = (servers option) + let targetChannelName = (splittedData!!1) + result <- mapM (\x -> if (serverName serverResponse) == (server x) then return $ x { channels = ((channels x) ++ [targetChannelName]) } else return x ) (myServers) + -- let newConnectionJson = IOptions { servers = myServers } + -- writeConfigFile newConnectionJson + writeConfigFile option result + return $ prepareJoinChannel (channelName serverResponse) -autoconnect :: IServerResponse -> IO () + +-- filterExample :: String -> [String] -> [String] +-- filterExample a b = filter (\n -> n /= a) b + +leftCommand :: IServerResponse -> IO String +leftCommand serverResponse = do + let currentChannelName = (channelName serverResponse) + output <- L.readFile ".connection.json" + let option = fromJust (A.decode output :: Maybe IOptions) + let myServers = (servers option) + result <- mapM (\x -> if (serverName serverResponse) == (server x) then return $ x { channels = (filter (\n -> n /= currentChannelName) (channels x)) } else return x ) (myServers) + writeConfigFile option result + return $ "just returning string io" + +autoconnect :: IServerResponse -> IO String autoconnect serverResponse = do - if | hasStringExist " KICK #" (readDataByteString serverResponse) -> joinChannel (sock serverResponse) (channelName serverResponse) >> {-- auto connect in case of kick --} sendMessage (sock serverResponse) (channelName serverResponse) "automated rejoin, if you want me to leave use .leave" - | otherwise -> print "NO CONDITION FOUND TO PARSE" + return $ prepareJoinChannel (channelName serverResponse) -randomImage :: IServerResponse -> IO () +randomImage :: IServerResponse -> IO String randomImage serverResponse = do - if | hasStringExist ".random" (readDataByteString serverResponse) -> basicRandomImplementation (readDataString serverResponse) >>= \commandOutput -> sendMessage (sock serverResponse) (channelName serverResponse) commandOutput - | otherwise -> print "NO CONDITION FOUND TO PARSE" + x <- basicRandomImplementation (messageText serverResponse) + return $ prepareSendMessage (channelName serverResponse) x + + + +botsCommand :: IServerResponse -> IO String +botsCommand serverResponse = do + does <- doesFileExist ".bots.txt" + if does then do + handle <- openFile ".bots.txt" ReadMode + content <- hGetContents handle + if length content > 0 then + return $ prepareSendMessage (channelName serverResponse) content + else return $ prepareSendMessage (channelName serverResponse) "bots.txt is empty" + else return $ prepareSendMessage (channelName serverResponse) "bots.txt doesnt exist" -quit :: IServerResponse -> IO () -quit serverResponse = do - if | hasStringExist ".quit" (readDataByteString serverResponse) -> quitFromServer (sock serverResponse) (channelName serverResponse) - | otherwise -> print "NO CONDITION FOUND TO PARSE" +quitCommand :: IServerResponse -> IO String +quitCommand serverResponse = do + return $ prepareQuit -leave :: IServerResponse -> IO () -leave serverResponse = do - if | hasStringExist ".leave" (readDataByteString serverResponse) -> disconnectFromChannel (sock serverResponse) (channelName serverResponse) - | otherwise -> print "NO CONDITION FOUND TO PARSE" +leaveCommand :: IServerResponse -> IO String +leaveCommand serverResponse = do + return $ prepareDisconnectFromChannel (channelName serverResponse) +data CommandTypes = SERVER | USER | NON_RETURN_ACTION deriving(Eq) -myMethodList = MethodList {methods = [basicEcho, ping, autoconnect, randomImage, quit, leave] } \ No newline at end of file + +data MethodDefinition = MethodDefinition { callerString :: String, callableFunction :: IServerResponse -> IO String, commandType :: CommandTypes } + +basicEchoDefinition = MethodDefinition { callerString = ".echo", callableFunction = basicEcho, commandType = USER } +pingDefinition = MethodDefinition { callerString = "PING", callableFunction = ping, commandType = SERVER } +bostDefinition = MethodDefinition { callerString = ".bots", callableFunction = botsCommand, commandType = USER } +quitDefinition = MethodDefinition { callerString = ".quit", callableFunction = quitCommand, commandType = USER } +leaveDefinition = MethodDefinition { callerString = ".leave", callableFunction = leaveCommand, commandType = USER } +leftDefinition = MethodDefinition { callerString = ".left", callableFunction = leftCommand, commandType = NON_RETURN_ACTION } +autoconnectDefinition = MethodDefinition { callerString = "KICK", callableFunction = autoconnect, commandType = NON_RETURN_ACTION } + + +myMethodList = MethodList {methods = [basicEchoDefinition,pingDefinition,bostDefinition,quitDefinition,leaveDefinition,autoconnectDefinition] } +myAdminMethodList = MethodList { methods = []} \ No newline at end of file diff --git a/src/IrcBot/BotNetwork.hs b/src/IrcBot/BotNetwork.hs index ba98a0f..140ebd4 100644 --- a/src/IrcBot/BotNetwork.hs +++ b/src/IrcBot/BotNetwork.hs @@ -7,8 +7,8 @@ import Network.Socket.ByteString (recv, sendAll) import qualified Data.ByteString.Char8 as C import Network.Connection -readFromSocket:: Connection -> IO C.ByteString -writeToSocket:: Connection -> String -> IO() +readFromSocket :: Connection -> IO C.ByteString +writeToSocket :: Connection -> String -> IO() writeToSocket sock stringData = @@ -29,7 +29,7 @@ readFromSocket sock = return C.empty -open:: String -> PortNumber -> Bool -> IO Connection +open :: String -> PortNumber -> Bool -> IO Connection open server port ssl = do ctx <- initConnectionContext diff --git a/src/IrcBot/BotNetworkCommands.hs b/src/IrcBot/BotNetworkCommands.hs index 0f5559e..fcf16c1 100644 --- a/src/IrcBot/BotNetworkCommands.hs +++ b/src/IrcBot/BotNetworkCommands.hs @@ -6,11 +6,15 @@ import Network.Connection import Data.List.Utils (replace) sendMessage :: Connection -> String -> String -> IO () - sendMessage sock targetChannel stringData = do - let combiendStringData = "PRIVMSG " ++ targetChannel ++ " :" ++ stringData - writeToSocket sock combiendStringData + -- print "SENT STRING: " + -- print combiendStringData + writeToSocket sock (prepareSendMessage targetChannel stringData) + + +prepareSendMessage :: String -> String -> String +prepareSendMessage targetChannel stringData = "PRIVMSG " ++ targetChannel ++ " :" ++ stringData @@ -18,42 +22,60 @@ quitFromServer :: Connection -> String -> IO () quitFromServer sock targetChannel = do - writeToSocket sock "QUIT" + writeToSocket sock prepareQuit + +prepareQuit :: String +prepareQuit = "QUIT" + disconnectFromChannel :: Connection -> String -> IO () - disconnectFromChannel sock targetChannel = do - writeToSocket sock ("PART " ++ targetChannel) + writeToSocket sock (prepareDisconnectFromChannel targetChannel) +prepareDisconnectFromChannel :: String -> String +prepareDisconnectFromChannel targetChannel = ("PART " ++ targetChannel) + + +initAuthNickServ :: Connection -> String -> String -> IO() +initAuthNickServ sock userName password = + do + sendMessage sock "NickServ" (prepareInitAuthNickServ userName password) + + +prepareInitAuthNickServ :: String -> String -> String +prepareInitAuthNickServ userName password = "IDENTIFY " ++ userName ++ " " ++ password initBotName :: Connection -> String -> IO () - -initBotName sock botName = - do - let dataString = "USER "++ botName ++" "++ botName ++" "++ botName ++" :learning purpose bot" - writeToSocket sock dataString +initBotName sock botName = writeToSocket sock (prepareInitBotName botName) -initBotNick :: Connection -> String -> IO () +prepareInitBotName :: String -> String +prepareInitBotName botName = "USER "++ botName ++" "++ botName ++" "++ botName ++" :learning purpose bot" +initBotNick :: Connection -> String -> IO () initBotNick sock botNick = do - let nick = "NICK " ++ botNick - writeToSocket sock nick + writeToSocket sock (preparetInitBotNick botNick) + + +preparetInitBotNick :: String -> String +preparetInitBotNick botNick = "NICK " ++ botNick joinChannel :: Connection -> String -> IO () - joinChannel sock channelName = do - let combinedString = "JOIN " ++ channelName - writeToSocket sock combinedString + writeToSocket sock (prepareJoinChannel channelName) + +prepareJoinChannel :: String -> String +prepareJoinChannel channelName = "JOIN " ++ channelName pong :: Connection -> String -> IO () - pong sock receivedStringData = do - let replacedText = replace "PING" "PONG" receivedStringData - writeToSocket sock replacedText \ No newline at end of file + writeToSocket sock (preparePong receivedStringData) + +preparePong :: String -> String +preparePong receivedStringData = replace "PING" "PONG" receivedStringData \ No newline at end of file diff --git a/src/IrcBot/Config/ConfigHelper.hs b/src/IrcBot/Config/ConfigHelper.hs new file mode 100644 index 0000000..08a7499 --- /dev/null +++ b/src/IrcBot/Config/ConfigHelper.hs @@ -0,0 +1,18 @@ + +module IrcBot.Config.ConfigHelper where + + +import qualified Data.ByteString.Lazy as L +import Data.List.Split +import Data.Maybe +import IrcBot.Definitions.Options +import IrcBot.Definitions.ServerAddress +import qualified Data.Aeson as A +import IrcBot.JsonConfigDecoder + +writeConfigFile :: IOptions -> [IServerAddress] -> IO () + +writeConfigFile oldOptions result = do + let options = oldOptions { servers = result } + let encodedJson = A.encode options + L.writeFile ".connection.json" encodedJson \ No newline at end of file diff --git a/src/IrcBot/Definitions/Options.hs b/src/IrcBot/Definitions/Options.hs new file mode 100644 index 0000000..1198e4e --- /dev/null +++ b/src/IrcBot/Definitions/Options.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE DeriveGeneric #-} + +module IrcBot.Definitions.Options where + +import IrcBot.Definitions.ServerAddress +import GHC.Generics + +data IOptions = IOptions + { + admins :: [String], + servers :: [IServerAddress] + } deriving (Show,Generic) \ No newline at end of file diff --git a/src/IrcBot/Definitions/ServerAddress.hs b/src/IrcBot/Definitions/ServerAddress.hs new file mode 100644 index 0000000..11cdb53 --- /dev/null +++ b/src/IrcBot/Definitions/ServerAddress.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveGeneric #-} + +module IrcBot.Definitions.ServerAddress where + +import GHC.Generics + + +data IServerAddress = IServerAddress + { + server :: String , + port :: Int, + ssl :: Bool, + nickname :: String, + password :: String, + channels :: [String] + } deriving (Show,Generic) \ No newline at end of file diff --git a/src/IrcBot/Definitions/ServerResponse.hs b/src/IrcBot/Definitions/ServerResponse.hs new file mode 100644 index 0000000..387e6ac --- /dev/null +++ b/src/IrcBot/Definitions/ServerResponse.hs @@ -0,0 +1,17 @@ +module IrcBot.Definitions.ServerResponse where + +import qualified Data.ByteString.Char8 as C +import Network.Connection + + +data IServerResponse = IServerResponse + { + serverName :: String, + readDataString :: String, + readDataByteString :: C.ByteString, + channelName :: String, + nick :: String, + messageText :: String, + messageTarget :: String, + messageType :: String + } \ No newline at end of file diff --git a/src/IrcBot/IrcBot.hs b/src/IrcBot/IrcBot.hs index 2b3accd..d15093f 100644 --- a/src/IrcBot/IrcBot.hs +++ b/src/IrcBot/IrcBot.hs @@ -9,37 +9,25 @@ import IrcBot.BotNetwork import Network.Connection import qualified Control.Concurrent as T import qualified Data.ByteString.Char8 as C - -data IServerAddress = IServerAddress - { - server :: String , - port :: Int, - ssl :: Bool, - nickname :: String, - channels :: [String] - } deriving (Show) - -data IOptions = IOptions - { - servers :: [Maybe IServerAddress] - } deriving (Show) +import IrcBot.Definitions.ServerAddress - - -connectToIRCServer :: Maybe IServerAddress -> IO () -connectToIRCServer (Just serverAddress) = do +connectToIRCServer :: IServerAddress -> IO () +connectToIRCServer serverAddress = do newSock <- connectToServer (server serverAddress) (fromIntegral (port serverAddress)) (ssl serverAddress) print "[connected to server]" initBotName newSock (nickname serverAddress) initBotNick newSock (nickname serverAddress) + initAuthNickServ newSock (nickname serverAddress) (password serverAddress) + print "waiting for identification with very stupid way" + T.threadDelay (1000000 * 15) --wait wait let channelsToJoin = channels serverAddress mapM (\x -> joinChannel newSock x) channelsToJoin - connectionLoop newSock + connectionLoop newSock serverAddress print "sleeping 15 sec for reconnect" -- 1000000 is 1 second T.threadDelay (1000000 * 15) print "reconnecting sequence initializing" -- this is a recursive function and its maintaining own connectivity -- how deep can i call this function? or just compiler optimizes for me? no idea - connectToIRCServer (Just serverAddress) \ No newline at end of file + connectToIRCServer (serverAddress) \ No newline at end of file diff --git a/src/IrcBot/JsonConfigDecoder.hs b/src/IrcBot/JsonConfigDecoder.hs index 5e5c146..5fd1561 100644 --- a/src/IrcBot/JsonConfigDecoder.hs +++ b/src/IrcBot/JsonConfigDecoder.hs @@ -1,11 +1,14 @@ {-# LANGUAGE OverloadedStrings #-} + module IrcBot.JsonConfigDecoder where import Data.Aeson import Network.Socket -import IrcBot.IrcBot import qualified Data.ByteString.Lazy as C +import IrcBot.Definitions.Options +import IrcBot.Definitions.ServerAddress +import GHC.Generics instance FromJSON IServerAddress where @@ -14,15 +17,18 @@ instance FromJSON IServerAddress where port_ <- o .: "port" ssl_ <- o .: "ssl" nickname_ <- o .: "nickname" + password_ <- o .: "password" channels_ <- o .: "channels" - return $ IServerAddress server_ port_ ssl_ nickname_ channels_ - + return $ IServerAddress server_ port_ ssl_ nickname_ password_ channels_ instance FromJSON IOptions where parseJSON = withObject "IOptions" $ \o -> do + admins_ <- o .: "admins" servers_ <- o .: "servers" - return $ IOptions servers_ + return $ IOptions admins_ servers_ +instance ToJSON IServerAddress +instance ToJSON IOptions --readConfigFile :: Maybe IServerAddress --readConfigFile = C.readFile ".connection.json" >>= \output -> decode output :: Maybe IServerAddress diff --git a/src/IrcBot/MessageParser.hs b/src/IrcBot/MessageParser.hs index 63a68c2..50a7b16 100644 --- a/src/IrcBot/MessageParser.hs +++ b/src/IrcBot/MessageParser.hs @@ -16,8 +16,40 @@ slice :: Int -> Int -> String -> String slice start end = take (end-start-1) . drop (start+1) +parseMessageMeta :: String -> String +parseMessageMeta messageMeta = do + let startEndIndexes = elemIndices ':' messageMeta + if length startEndIndexes >= 2 then do + let startIndex = startEndIndexes !! 0 + let endIndex = startEndIndexes !! 1 + slice (startIndex) (endIndex) messageMeta + else "" --- :YOUR_NICK!~YOUR_NICK@YOUR_IP PRIVMSG #91623_my_test_channel + +parseMessageTargetFromMeta :: [String] -> String +parseMessageTargetFromMeta messageMetaArray = do + + if (length messageMetaArray) >= 3 + then + messageMetaArray !! 2 + else + "" + +-- :YOUR_NICK!~YOUR_NICK@YOUR_IP PRIVMSG #91623_my_test_channel :TEXT MESSAGE + + +parseMessageType :: [String] -> String +parseMessageType messageMetaArray = do + if(length messageMetaArray) >= 3 + then + messageMetaArray !! 1 + else + "" + + +targetDeterminer :: String -> String -> String +targetDeterminer nickName channelName = do + if (length channelName) > 1 then channelName else nickName parseNick :: String -> String @@ -25,7 +57,11 @@ parseNick dataString = do let startIndex = elemIndex ':' dataString let endIndex = elemIndex '!' dataString - slice (fromJust startIndex) (fromJust endIndex) dataString + if startIndex /= Nothing && endIndex /= Nothing + then + slice (fromJust startIndex) (fromJust endIndex) dataString + else "" + parseChannelName :: String -> String @@ -34,8 +70,10 @@ parseChannelName dataString = let startIndex = elemIndex '#' dataString let dropppedString = drop (fromJust startIndex) dataString let endIndex = elemIndex ' ' dropppedString - take (fromJust endIndex) dropppedString - + if startIndex /= Nothing && endIndex /= Nothing + then + take (fromJust endIndex) dropppedString + else "" parseMessageText :: String -> String @@ -48,4 +86,14 @@ parseMessageText dataString = basicRandomImplementation :: String -> IO String -basicRandomImplementation basicInput = getRandomFile "maidos" >>= \randomFileName -> uploadLocalFile randomFileName \ No newline at end of file +basicRandomImplementation basicInput = do + + let parameters = (words basicInput) + print ".random parameters: " + print parameters + if length parameters > 1 then + do + let directoryName = parameters !! 1 + getRandomFile directoryName >>= \randomFileName -> uploadLocalFile randomFileName + else + getRandomFile "maidos" >>= \randomFileName -> uploadLocalFile randomFileName diff --git a/src/IrcBot/RandomFileFinder.hs b/src/IrcBot/RandomFileFinder.hs index d9d140d..1e581d1 100644 --- a/src/IrcBot/RandomFileFinder.hs +++ b/src/IrcBot/RandomFileFinder.hs @@ -2,8 +2,8 @@ module IrcBot.RandomFileFinder where import System.Directory import System.Random - - +import Data.List +import Control.Monad.IO.Class getRandomFile :: String -> IO String @@ -11,8 +11,17 @@ getRandomFile :: String -> IO String getRandomFile directoryName = do let fullDirectoryName = "./.warehouse/" ++ directoryName - directoryItems <- getDirectoryContents fullDirectoryName - result <- return (length directoryItems) >>= \count -> randomRIO(4,count) :: IO Int - let fileName = directoryItems !! result - let newFileName = ".warehouse/" ++ directoryName ++ "/" ++ fileName - return newFileName + acceptedDirectoryNames <- (listDirectory "./.warehouse") >>= \x -> return x + print acceptedDirectoryNames + let directoryExist = elem directoryName acceptedDirectoryNames + + if directoryExist then do + directoryItems <- getDirectoryContents fullDirectoryName + let directoryItemCount = (length directoryItems) + print "directory item count is: " + if directoryItemCount > 2 then do -- . and .. basically makes 2 + result <- randomRIO(2,(directoryItemCount-1)) :: IO Int -- 2 represents an index which is exludes 0,1 elements from file list (. and ..) + let fileName = directoryItems !! result + return $ ".warehouse/" ++ directoryName ++ "/" ++ fileName + else return $ "" + else return $ "" diff --git a/src/IrcBot/RemoteUploader.hs b/src/IrcBot/RemoteUploader.hs index 4406cb1..7d09922 100644 --- a/src/IrcBot/RemoteUploader.hs +++ b/src/IrcBot/RemoteUploader.hs @@ -4,11 +4,12 @@ module IrcBot.RemoteUploader where import System.Process -uploadLocalFile:: String -> IO String +uploadLocalFile :: String -> IO String uploadLocalFile filePath = do - let command = "curl" - let params = "file=@"++ filePath - readProcess command ["-F",params,"0x0.st"] "" - + if filePath /= "" then do + let command = "curl" + let params = "file=@"++ filePath + readProcess command ["-F",params,"0x0.st"] "" + else return $ ";_;"