fix message parsing without prefix
[cloogle-irc.git] / IRCBot.icl
1 implementation module IRCBot
2
3 from Data.Func import $
4 import Data.Either
5 import Data.Error
6 import Data.Maybe
7 import IRC
8 import TCPIP
9
10 from Text import class Text(split,join), instance Text String
11
12 import StdList
13 import StdBool
14
15 TIMEOUT :== Just 1000
16
17 bot :: (String, Int) [IRCMessage] [IRCMessage] a (IRCMessage a *World -> (Maybe [IRCMessage], a, *World)) *World -> (MaybeErrorString a, *World)
18 bot (host, port) start end state bot w
19 //Lookup hostname
20 # (ip, w) = lookupIPAddress host w
21 | isNothing ip = (Error $ "DNS lookup for " +++ host +++ " failed", w)
22 //Connect
23 # (rpt,chan,w) = connectTCP_MT TIMEOUT (fromJust ip, port) w
24 | rpt == TR_Expired = (Error $ "Connection to " +++ host +++ " timed out", w)
25 | rpt == TR_NoSuccess = (Error $ "Could not connect to " +++ host, w)
26 // Send startup commands
27 # (merr, chan, w) = send (map toString start) (fromJust chan) w
28 | isError merr = (Error $ fromError merr, w)
29 //Start processing function
30 # (mer, chan, state, w) = process chan "" state bot w
31 | isError mer = (Error $ fromError mer, w)
32 // Send shutdown commands
33 # (merr, {rChannel,sChannel}, w) = send (map toString end) chan w
34 | isError merr = (Error $ fromError merr, w)
35 //Close channels
36 = (Ok state, closeChannel sChannel (closeRChannel rChannel w))
37
38 import StdDebug,StdMisc
39 process :: TCP_DuplexChannel String a (IRCMessage a *World -> (Maybe [IRCMessage], a, *World)) *World -> (MaybeErrorString (), TCP_DuplexChannel, a, *World)
40 process chan acc state bot w
41 //See if we have a message
42 = case split "\r\n" acc of
43 //We only have one message that is not complete
44 [m]
45 //Receive
46 # (merr_resp, chan, w) = recv chan w
47 | isError merr_resp = (Error (fromError merr_resp), chan, state, w)
48 # (Ok mresp) = merr_resp
49 | isNothing mresp = process chan acc state bot w
50 = process chan (m +++ fromJust mresp) state bot w
51 //We have a successfull split and therefore we process at least one message
52 [m:xs]
53 # acc = join "\r\n" xs
54 | not (trace_tn $ "Full message: '" +++ m +++ "'") = undef
55 = case parseIRCMessage $ m +++ "\r\n" of
56 (Left err) = (Error $ "IRC Parsing error: " +++ join "\n" err, chan, state, w)
57 (Right msg)
58 # (mircc, state, w) = bot msg state w
59 | isNothing mircc = (Ok (), chan, state, w) // Bot asks to quit
60 //Possible send the commands
61 # (merr, chan, w) = send (map toString $ fromJust mircc) chan w
62 | isError merr = (Error $ fromError merr, chan, state, w)
63 //Recurse
64 = process chan acc state bot w
65
66 send :: [String] TCP_DuplexChannel *World -> (MaybeErrorString (), TCP_DuplexChannel, *World)
67 send [] chan w = (Ok (), chan, w)
68 send [msg:msgs] {sChannel,rChannel} w
69 # (_, w) = sleep 250000 w
70 # (rpt,i,sChannel,w) = send_MT TIMEOUT (toByteSeq msg) sChannel w
71 | rpt <> TR_Success = (Error "Could not send message", {sChannel=sChannel,rChannel=rChannel}, w)
72 = send msgs {sChannel=sChannel,rChannel=rChannel} w
73 where
74 sleep :: !Int !*World -> (!Int, *World)
75 sleep i w = code {
76 ccall usleep "I:I:A"
77 }
78
79 recv :: TCP_DuplexChannel *World -> (MaybeErrorString (Maybe String), TCP_DuplexChannel, *World)
80 recv {sChannel,rChannel} w
81 # (rpt, resp, rChannel, w) = receive_MT TIMEOUT rChannel w
82 | rpt == TR_Expired = (Ok Nothing, {sChannel=sChannel,rChannel=rChannel}, w)
83 | rpt == TR_NoSuccess || isNothing resp = (Error "Timeout recv fail", {sChannel=sChannel,rChannel=rChannel}, w)
84 = (Ok $ Just $ toString $ fromJust resp, {sChannel=sChannel,rChannel=rChannel}, w)