@@ -1 +1,7 @@ | |||
# haskell-basic-irc-bot | |||
## doc | |||
example.json > .connection.json than you are ready to go |
@@ -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 | |||
@@ -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" | |||
] | |||
@@ -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 | |||
@@ -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 | |||
@@ -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) | |||
-- 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 | |||
return $ prepareJoinChannel (channelName serverResponse) | |||
randomImage :: IServerResponse -> IO String | |||
randomImage serverResponse = do | |||
x <- basicRandomImplementation (messageText serverResponse) | |||
return $ prepareSendMessage (channelName serverResponse) x | |||
autoconnect :: IServerResponse -> IO () | |||
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" | |||
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" | |||
quitCommand :: IServerResponse -> IO String | |||
quitCommand serverResponse = do | |||
return $ prepareQuit | |||
randomImage :: IServerResponse -> IO () | |||
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" | |||
leaveCommand :: IServerResponse -> IO String | |||
leaveCommand serverResponse = do | |||
return $ prepareDisconnectFromChannel (channelName serverResponse) | |||
quit :: IServerResponse -> IO () | |||
quit serverResponse = do | |||
if | hasStringExist ".quit" (readDataByteString serverResponse) -> quitFromServer (sock serverResponse) (channelName serverResponse) | |||
| otherwise -> print "NO CONDITION FOUND TO PARSE" | |||
data CommandTypes = SERVER | USER | NON_RETURN_ACTION deriving(Eq) | |||
leave :: IServerResponse -> IO () | |||
leave serverResponse = do | |||
if | hasStringExist ".leave" (readDataByteString serverResponse) -> disconnectFromChannel (sock serverResponse) (channelName serverResponse) | |||
| otherwise -> print "NO CONDITION FOUND TO PARSE" | |||
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 = [basicEcho, ping, autoconnect, randomImage, quit, leave] } | |||
myMethodList = MethodList {methods = [basicEchoDefinition,pingDefinition,bostDefinition,quitDefinition,leaveDefinition,autoconnectDefinition] } | |||
myAdminMethodList = MethodList { methods = []} |
@@ -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 | |||
@@ -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 :: Connection -> String -> IO () | |||
disconnectFromChannel sock targetChannel = | |||
do | |||
writeToSocket sock ("PART " ++ targetChannel) | |||
writeToSocket sock (prepareDisconnectFromChannel targetChannel) | |||
prepareDisconnectFromChannel :: String -> String | |||
prepareDisconnectFromChannel targetChannel = ("PART " ++ targetChannel) | |||
initBotName :: Connection -> String -> IO () | |||
initBotName sock botName = | |||
do | |||
let dataString = "USER "++ botName ++" "++ botName ++" "++ botName ++" :learning purpose bot" | |||
writeToSocket sock dataString | |||
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 = 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 :: Connection -> String -> IO () | |||
pong sock receivedStringData = do | |||
let replacedText = replace "PING" "PONG" receivedStringData | |||
writeToSocket sock replacedText | |||
writeToSocket sock (preparePong receivedStringData) | |||
preparePong :: String -> String | |||
preparePong receivedStringData = replace "PING" "PONG" receivedStringData |
@@ -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 |
@@ -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) |
@@ -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) |
@@ -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 | |||
} |
@@ -9,37 +9,25 @@ import IrcBot.BotNetwork | |||
import Network.Connection | |||
import qualified Control.Concurrent as T | |||
import qualified Data.ByteString.Char8 as C | |||
import IrcBot.Definitions.ServerAddress | |||
data IServerAddress = IServerAddress | |||
{ | |||
server :: String , | |||
port :: Int, | |||
ssl :: Bool, | |||
nickname :: String, | |||
channels :: [String] | |||
} deriving (Show) | |||
data IOptions = IOptions | |||
{ | |||
servers :: [Maybe IServerAddress] | |||
} deriving (Show) | |||
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) | |||
connectToIRCServer (serverAddress) |
@@ -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 | |||
@@ -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 "" | |||
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 | |||
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 |
@@ -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 $ "" |
@@ -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 $ ";_;" |