X-Git-Url: https://git.martlubbers.net/?p=cloogle-irc.git;a=blobdiff_plain;f=cloogle.icl;h=b1303000dc9ef822275c07be4437591d44dd38b9;hp=f1249255ed5c0f518e5a1f7124893a1a186f388a;hb=3134ab7e61bcbc84560b4a5d613a92b1a48362c2;hpb=6fb569170aca43bf17a5e223544c0a5ef0b42fb6 diff --git a/cloogle.icl b/cloogle.icl index f124925..b130300 100644 --- a/cloogle.icl +++ b/cloogle.icl @@ -1,5 +1,6 @@ module cloogle +import Cloogle import GenPrint import IRC import StdEnv @@ -7,14 +8,29 @@ import StdEnv import Data.Functor import Data.Maybe from Data.Func import $ -from Text import class Text(..), instance Text String +from Text import class Text(..), instance Text String, instance + String + +import Text.JSON + +import Text.URI + +import Control.Applicative +import qualified Control.Monad as CM +import qualified Data.Map as DM +from Control.Monad import class Monad, instance Monad Maybe +from Text.Encodings.UrlEncoding import urlEncode +import Internet.HTTP +import Data.Error +import Data.List +import Data.Functor +import Data.Tuple import TCPIP commands :: [String] commands = map toString - [NICK "clooglebot" - ,USER "cloogle" 0 "Cloogle bot" + [NICK "clooglebot" Nothing + ,USER "cloogle" "0" "Cloogle bot" ,JOIN [("#cloogle", Nothing)] ] @@ -23,66 +39,177 @@ SERVER :== "irc.freenode.net" KEY :== "PRIVMSG #cloogle :!" -send :: [String] TCP_DuplexChannel *World -> (TCP_DuplexChannel, *World) -send [] chan w = (chan, w) -send [msg:msgs] {sChannel,rChannel} w -# (rpt,i,sChannel,w) = send_MT TIMEOUT (toByteSeq msg) sChannel w -| rpt <> TR_Success = abort "Could not send request\n" -= send msgs {sChannel=sChannel,rChannel=rChannel} w - -recv :: TCP_DuplexChannel *World -> (Maybe String, TCP_DuplexChannel, *World) -recv {sChannel,rChannel} w -# (rpt, resp, rChannel, w) = receive_MT TIMEOUT rChannel w -| rpt == TR_Expired = (Nothing, {sChannel=sChannel,rChannel=rChannel}, w) -| rpt == TR_NoSuccess || isNothing resp = abort "Halp?\n" -= (toString <$> resp, {sChannel=sChannel,rChannel=rChannel}, w) - -msg :: (String -> IRCCommands) -msg = PRIVMSG "#cloogle" - -process :: *File TCP_DuplexChannel *World -> (*File, TCP_DuplexChannel, *World) -process io chan w -# (mr, chan, w) = recv chan w -| isNothing mr = process io chan w -# resp = fromJust mr -#! io = io <<< ("Received: " +++ resp +++ "\n") -# ind = indexOf KEY resp -| ind > 0 - # cmd = split " " $ rtrim $ subString (ind + size KEY) (size resp) resp - #! io = io <<< ("Received command: " +++ printToString cmd +++ "\n") - # toSend = case cmd of - ["stop":_] = Nothing - ["ping":xs] = Just [msg $ "pong " +++ join " " xs] - ["help"] = Just - [msg "type !help cmd for command specific help" - ,msg "available commands: help, ping"] - ["help":c:_] = case c of - "help" = Just [msg "help [CMD] - I will print general help or the help of CMD"] - "ping" = Just [msg "ping [TXT] - I will reply with pong and the optionar TXT"] - _ = Just [msg "Unknown command"] - [c:_] = Just [msg $ join " " ["unknown command: " , c, ", type !help to get help"]] - | isNothing toSend = (io, chan, w) - # (chan, w) = send (map toString $ fromJust toSend) chan w - = process io chan w -| indexOf "PING :" resp > 0 - # cmd = rtrim $ subString (indexOf "PING :" resp + size "PING :") (size resp) resp - #! io <<< (toString $ PONG cmd Nothing) <<< "\n" - # (chan, w) = send [toString $ PONG cmd Nothing] chan w - = process io chan w -= process io chan w - -Start :: *World -> *World -Start w -# (io, w) = stdio w -# (ip, w) = lookupIPAddress SERVER w -| isNothing ip = abort $ "DNS lookup for " +++ SERVER +++ " failed\n" +doRequest :: HTTPRequest *World -> *(MaybeErrorString HTTPResponse, *World) +doRequest req w +# (ip,w) = lookupIPAddress server_name w +| isNothing ip + = (Error $ "DNS lookup for " + server_name + " failed.", w) # (Just ip) = ip -# (rpt,chan,w) = connectTCP_MT TIMEOUT (ip, 6667) w -| rpt == TR_Expired = abort $ "Connection to " +++ SERVER +++ " timed out\n" -| rpt == TR_NoSuccess = abort $ "Could not connect to " +++ SERVER +++ "\n" -# chan = fromJust chan -# (chan, w) = send commands chan w -# (io, chan, w) = process io chan w -# ({sChannel,rChannel}, w) = send [toString $ QUIT Nothing] chan w -# (_, w) = fclose io w -= closeChannel sChannel (closeRChannel rChannel w) +# (rpt,chan,w) = connectTCP_MT TIMEOUT (ip, req.server_port) w +| rpt == TR_Expired + = (Error $ "Connection to " + toString ip + " timed out.", w) +| rpt == TR_NoSuccess + = (Error $ "Could not connect to " + server_name + ".", w) +# (Just {sChannel,rChannel}) = chan +# (rpt,i,sChannel,w) = send_MT TIMEOUT (toByteSeq req) sChannel w +| rpt <> TR_Success + = (Error $ "Could not send request to " + server_name + ".", w) +# (rpt,resp,rChannel,w) = receive_MT TIMEOUT rChannel w +| rpt <> TR_Success + = (Error $ "Did not receive a reply from " + server_name + ".", w) +# resp = 'CM'.join $ parseResponse <$> toString <$> resp +| isNothing resp + # w = closeChannel sChannel (closeRChannel rChannel w) + = (Error $ "Server did not respond with HTTP.", w) +# (resp,rChannel,w) = receiveRest (fromJust resp) rChannel w +# w = closeChannel sChannel (closeRChannel rChannel w) += (resp,w) +where + server_name = req.server_name + receiveRest resp chan w + # cl = lookup "Content-Length" resp.HTTPResponse.rsp_headers + | isNothing cl + = (Ok resp, chan, w) + | size resp.rsp_data >= toInt (fromJust cl) + = (Ok resp, chan, w) + # (rpt,newresp,chan,w) = receive_MT TIMEOUT chan w + | rpt <> TR_Success + = (Error $ server_name + " hung up during transmission.", chan, w) + = receiveRest {resp & rsp_data=resp.rsp_data + toString (fromJust newresp)} chan w + +import StdMisc +import StdDebug + +doRequestL :: HTTPRequest Int *World -> *(MaybeErrorString HTTPResponse, *World) +doRequestL req 0 w = (Error "Maximal redirect numbe exceeded", w) +doRequestL req maxRedirects w +| not (trace_tn $ toString req) = undef +# (er, w) = doRequest req w +| isError er = (er, w) +# resp = fromOk er +| isMember resp.HTTPResponse.rsp_code [301, 302, 303, 307, 308] + = case lookup "Location" resp.HTTPResponse.rsp_headers of + Nothing = (Error $ "Redirect given but no Location header", w) + Just loc = case parseURI loc of + Nothing = (Error $ "Redirect URI couldn't be parsed", w) + Just uri = doRequestL {req + & server_name = maybe loc id uri.uriRegName + , server_port = maybe 80 id uri.uriPort + , req_path = uri.uriPath + , req_query = maybe "" ((+++) "?") uri.uriQuery + } (maxRedirects-1) w += (er, w) + +shorten :: String *World -> (String, *World) +shorten s w +# s = if (startsWith "http://" s) s (if (startsWith "https://" s) s ("http://" + s)) +# data = "type=regular&url="+urlEncode s+"&token=a" +# (mer, w) = doRequest + { newHTTPRequest + & req_method = HTTP_POST + , req_path = "/" + , server_name = "cloo.gl" + , server_port = 80 + , req_headers = 'DM'.fromList + [("Content-Type", "application/x-www-form-urlencoded") + ,("Content-Length", toString $ size data) + ,("Accept", "*/*")] + , req_data = data} w +| isError mer = ("request failed: " + fromError mer, w) +# resp = fromOk mer += (resp.rsp_data, w) + +cloogle :: String *World -> (String, *World) +cloogle data w +# (mer, w) = doRequestL + { newHTTPRequest + & req_path = "/api.php" + , req_query = "?str=" + urlEncode data + , req_headers = 'DM'.fromList [("User-Agent", "cloogle-irc")] + , server_name = "cloogle.org" + , server_port = 80} 10 w +| isError mer = ("request failed: " + fromError mer, w) +# resp = fromOk mer += case fromJSON $ fromString resp.HTTPResponse.rsp_data of + Nothing = ("couldn't parse json", w) + Just clr = ("Results for " + data + " -- https://cloogle.org/#" + replaceSubString "+" "%20" (urlEncode data) + "\n" + + processResults clr, w) + where + processResults :: Response -> String + processResults resp + | resp.return > 127 = "Failed: return code: " + toString resp.return + ", " + resp.msg + = join "\n" $ map processResult $ take 3 resp.data + + processResult :: Result -> String + processResult (FunctionResult (br, {func})) + = "Function in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ func + processResult (TypeResult (br, {type})) + = "Type in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ limitResults type + processResult (ClassResult (br, {class_name,class_funs})) + = "Class in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ class_name +++ " with " + +++ toString (length class_funs) +++ " class functions" + processResult (MacroResult (br, {macro_name})) + = "Macro in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ macro_name + processResult (ModuleResult (br, _)) + = "Module in " +++ br.library +++ ": " +++ br.modul + + limitResults :: String -> String + limitResults s + # lines = split "\n" s + | length lines > 4 = limitResults (join "\n" (take 3 lines) + "\n...") + = join "\n" (map maxWidth lines) + + maxWidth :: String -> String + maxWidth s + | size s > 80 = subString 0 77 s + "..." + = s + +/* + ["stop":_] = (w, Nothing) + ["ping":xs] = (w, Just [msg $ "pong " +++ join " " xs]) + ["query":xs] + # (s, w) = cloogle (join " " xs) w + = (w, Just $ map msg $ split "\n" s) + ["short"] = (w, Just [msg $ "short requires an url argument"]) + ["short":xs] + # (s, w) = shorten (join " " xs) w + = (w, Just [msg s]) + ["help"] = (w, Just + [msg "type !help cmd for command specific help" + ,msg "available commands: help, ping, query, short"]) + ["help":c:_] = (w, case c of + "help" = Just [msg "help [CMD] - I will print general help or the help of CMD"] + "ping" = Just [msg "ping [TXT] - I will reply with pong and the optionar TXT"] + "query" = Just [msg "query QUERY - I will send QUERY to cloogle and post the results"] + "short" = Just [msg "short URL - I will give the url to https://cloo.gl shortening service and post back the result"] + _ = Just [msg "Unknown command"]) + [c:_] = (w, Just [msg $ join " " ["unknown command: " , c, ", type !help to get help"]]) +*/ + +Start :: *World -> (MaybeErrorString (), *World) +Start w = bot ("irc.freenode.net", 6667) startup shutdown () process w + where + toPrefix c = {irc_prefix=Nothing,irc_command=c} + startup = map toPrefix + [NICK "clooglebot" Nothing + ,USER "cloogle" "0" "Cloogle bot" + ,JOIN [("#cloogle", Nothing)]] + shutdown = map toPrefix [QUIT (Just "Bye")] + + process :: IRCMessage () *World -> (Maybe [IRCMessage], (), *World) + process im s w = case process` im.irc_command w of + (Nothing, w) = (Nothing, (), w) + (Just cs, w) = (Just $ map toPrefix cs, (), w) + + process` :: IRCCommand *World -> (Maybe [IRCCommand], *World) + process` (PRIVMSG t m) w = (Just $ if (startsWith "!" m) + (map (PRIVMSG t) $ realProcess $ split " " $ subString 1 (size m) m) + [], w) + process` (PING t mt) w = (Just [PONG t mt], w) + process` _ w = (Just [], w) + + realProcess :: [String] -> [String] + realProcess ["help":xs] = + ["type !help cmd for command specific help" + ,"available commands: help"] + realProcess [c:_] = [join " " ["unknown cmd: ", c, ", type !help to get help"]]