a7f72507138550e5b779ffb64aa9fe30b3cba992
[cloogle-irc.git] / IRC.icl
1 implementation module IRC
2
3 import StdList
4 import GenPrint
5 import StdOverloaded
6 import Data.Maybe
7 import Data.Either
8 import StdFunc
9 import StdString
10 import StdChar
11
12 import Text.Parsers.Simple.Core
13 import Text.Parsers.Simple.Chars
14 import Data.Tuple
15 import Control.Monad
16 import Control.Applicative
17 from Data.Functor import <$>
18
19 from Data.Func import $
20 from Text import class Text(concat), instance Text String
21 import qualified Text
22 from StdMisc import undef
23
24 jon :== 'Text'.join
25
26 derive gPrint IRCCommand, IRCReplies, IRCErrors, (,), Maybe, (), Either
27
28 //Start = runParser parseMessage $ fromString ":frobnicator!~frobnicat@92.110.128.124 QUIT\r\n"
29 //Start = runParser parseMessage $ fromString ":frobnicator!~frobnicat@92.110.128.124 AWAY test\r\n"
30 Start = runParser parseMessage $ fromString ":frobnicator!~frobnicat@92.110.128.124 AWAY test with spaces\r\n"
31
32 (<+) infixr 5 :: a b -> String | toString a & toString b
33 (<+) a b = toString a +++ toString b
34
35 parseIRCMessage :: (String -> Either [Error] IRCMessage)
36 parseIRCMessage = parse parseMessage o fromString
37
38 parseMessage :: Parser Char IRCMessage
39 parseMessage = optional (parseEither parseHost parseUser) <* spaceParser
40 >>= \mprefix->parseCommand
41 <* pToken '\r' <* pToken '\n'
42 >>= \cmd->pure {IRCMessage | irc_prefix=mprefix, irc_command=cmd}
43
44 spaceParser :: Parser Char [Char]
45 spaceParser = pMany $ pToken ' '
46
47 parseServer :: Parser Char String
48 parseServer = pFail
49
50 parseEither :: (Parser a b) (Parser a c) -> Parser a (Either b c)
51 parseEither p q = Left <$> p <|> Right <$> q
52
53 parseUser :: Parser Char IRCUser
54 parseUser = pToken ':' >>| parseNick
55 >>= \nick->optional (pToken '!' >>| parseUsr)
56 >>= \muser->optional (pToken '@' >>| parseHost)
57 >>= \mhost->pure {IRCUser | irc_nick=nick, irc_user=muser, irc_host=mhost}
58
59 parseUsr :: Parser Char String
60 parseUsr = toString <$> pSome (pNoneOf [' ', '@':illegal])
61
62 parseNick :: Parser Char String
63 parseNick = pAlpha >>= \c->pMany (pAlpha <|> pDigit <|> pSpecial)
64 >>= \cs->pure (toString [c:cs])
65
66 pSpecial :: Parser Char Char
67 pSpecial = pOneOf ['-', '[', ']', '\\', '\`', '^', '{', '}']
68
69 parseHost :: Parser Char String
70 parseHost = parseName
71 >>= \nm->pMany (pToken '.' >>| parseName)
72 >>= \nms->pure (concat [nm:nms])
73 where
74 parseName :: Parser Char String
75 parseName = toString <$> pSome (pAlpha <|> pDigit <|> pToken '.')
76
77 instance toString IRCMessage where
78 toString m = maybe "" (\s->either id ((<+) ":") s <+ " ") m.irc_prefix <+ m.irc_command
79
80 instance toString IRCUser where
81 toString m = m.irc_nick <+ maybe "" ((<+) "!") m.irc_user
82 <+ maybe "" ((<+) "@") m.irc_host
83
84 cons :: a [a] -> [a]
85 cons a as = [a:as]
86
87 pMiddle :: Parser Char String
88 pMiddle = fmap toString $
89 spaceParser >>| pToken ':' >>| pMany (pNoneOf illegal)
90
91 pTrailing :: Parser Char String
92 pTrailing = fmap toString $
93 spaceParser >>| liftM2 cons (pNotSatisfy ((==)':')) (pMany $ pNoneOf [' ':illegal])
94
95 pParam :: Parser Char String
96 pParam = pMiddle <|> pTrailing
97
98 pNoneOf :: [a] -> Parser a a | Eq a
99 pNoneOf l = pSatisfy (not o flip isMember l)
100
101 pNotSatisfy :: (a -> Bool) -> Parser a a | Eq a
102 pNotSatisfy f = pSatisfy (not o f)
103
104 pInt :: Parser Char Int
105 pInt = toInt o toString <$> (spaceParser >>| pSome pDigit)
106
107 illegal :: [Char]
108 illegal = ['\x00','\r','\n']
109
110 pCommand :: String -> Parser Char [Char]
111 pCommand s = pList (map pToken $ fromString s)
112
113 pCommand0 :: String IRCCommand -> Parser Char IRCCommand
114 pCommand0 s c = pCommand s >>| pure c
115
116 pCommand1 :: String (Parser Char a) (a -> IRCCommand) -> Parser Char IRCCommand
117 pCommand1 s p c = pCommand s >>| liftM c p
118
119 pCommand2 :: String (Parser Char a) (Parser Char b) (a b -> IRCCommand) -> Parser Char IRCCommand
120 pCommand2 s p q c = pCommand s >>| liftM2 c p q
121
122 pCommand3 :: String (Parser Char a) (Parser Char b) (Parser Char c) (a b c -> IRCCommand) -> Parser Char IRCCommand
123 pCommand3 s p q r c = pCommand s >>| liftM3 c p q r
124
125 parseCommand :: Parser Char IRCCommand
126 parseCommand =
127 pCommand1 "ADMIN" (optional pMiddle) ADMIN
128 <|> pCommand1 "AWAY" pParam AWAY
129 <|> pCommand2 "CONNECT" pParam (optional $ liftM2 tuple pInt (optional pParam)) CONNECT
130 <|> pCommand0 "DIE" DIE
131 <|> pCommand1 "ERROR" pParam ERROR
132 <|> pCommand1 "INFO" (optional pParam) INFO
133 <|> pCommand2 "INVITE" pMiddle pMiddle INVITE
134 <|> pCommand1 "ISON" (pSome pMiddle) ISON
135 <|> pCommand1 "JOIN" (pSepBy (liftM2 tuple pMiddle $ optional pMiddle) pComma) JOIN
136 <|> pCommand3 "KICK" pMiddle pMiddle (optional pParam) KICK
137 <|> pCommand2 "KILL" pMiddle pParam KILL
138 <|> pCommand1 "LINKS" (optional (liftM2 tuple (optional pMiddle) pMiddle)) LINKS
139 //<|> pCommand "QUIT" (optional pParam))
140
141 instance toString IRCCommand where
142 toString r = flip (+++) "\r\n" case r of
143 //ADMIN (Maybe String)
144 //AWAY String
145 //CONNECT String (Maybe (Int, Maybe String))
146 //DIE
147 //ERROR String
148 //INFO (Maybe String)
149 //INVITE String String
150 //ISON [String]
151 JOIN chs = "JOIN " +++ (if (isEmpty chs) "0"
152 (jon ", " [jon " " [ch:maybeToList mk]\\(ch, mk)<-chs]))
153 //KICK String String (Maybe String)
154 //KILL String String
155 //LINKS (Maybe (Maybe String, String))
156 //LIST [String]
157 //LUSERS (Maybe (String, Maybe String))
158 //MODE String
159 //MOTD (Maybe String)
160 //NAMES [String]
161 NICK n = jon " " ["NICK", n]
162 //NJOIN
163 //NOTICE String String
164 //OPER String String
165 //PART [String]
166 //PASS String
167 PING a mb = jon " " ["PING",a:maybeToList mb]
168 PONG a mb = jon " " ["PONG",a:maybeToList mb]
169 PRIVMSG dest msg = jon " " ["PRIVMSG", dest, ":"+++msg]
170 QUIT msg = jon " " ["QUIT":maybeToList msg]
171 //REHASH
172 //RESTART
173 //SERVER
174 //SERVICE String String String String
175 //SERVLIST (Maybe (String, Maybe String))
176 //SQUERY String String
177 //SQUIRT
178 //SQUIT String String
179 //STATS (Maybe (String, Maybe String))
180 //SUMMON String (Maybe (String, Maybe String))
181 //TIME (Maybe String)
182 //TOPIC String (Maybe String)
183 //TRACE (Maybe String)
184 USER login mode rn = jon " " ["USER", login, toString mode, "*", ":"+++rn]
185 //USERHOST [String]
186 //USERS (Maybe String)
187 //VERSION (Maybe String)
188 //WALLOPS String
189 //WHO (Maybe String)
190 //WHOIS (Maybe String) [String]
191 //WHOWAS (Maybe String) [String]
192 _ = printToString r
193
194
195 instance toString IRCReplies where toString r = printToString r
196 instance toString IRCErrors where toString r = printToString r
197
198 instance fromInt IRCReplies where
199 fromInt r = case r of
200 1 = RPL_WELCOME
201 2 = RPL_YOURHOST
202 3 = RPL_CREATED
203 4 = RPL_MYINFO
204 5 = RPL_BOUNCE
205 200 = RPL_TRACELINK
206 201 = RPL_TRACECONNECTING
207 202 = RPL_TRACEHANDSHAKE
208 203 = RPL_TRACEUNKNOWN
209 204 = RPL_TRACEOPERATOR
210 205 = RPL_TRACEUSER
211 206 = RPL_TRACESERVER
212 207 = RPL_TRACESERVICE
213 208 = RPL_TRACENEWTYPE
214 209 = RPL_TRACECLASS
215 210 = RPL_TRACERECONNECT
216 211 = RPL_STATSLINKINFO
217 212 = RPL_STATSCOMMANDS
218 219 = RPL_ENDOFSTATS
219 221 = RPL_UMODEIS
220 234 = RPL_SERVLIST
221 235 = RPL_SERVLISTEND
222 242 = RPL_STATSUPTIME
223 243 = RPL_STATSOLINE
224 251 = RPL_LUSERCLIENT
225 252 = RPL_LUSEROP
226 253 = RPL_LUSERUNKNOWN
227 254 = RPL_LUSERCHANNELS
228 255 = RPL_LUSERME
229 256 = RPL_ADMINME
230 257 = RPL_ADMINLOC1
231 258 = RPL_ADMINLOC2
232 259 = RPL_ADMINEMAIL
233 261 = RPL_TRACELOG
234 262 = RPL_TRACEEND
235 263 = RPL_TRYAGAIN
236 301 = RPL_AWAY
237 302 = RPL_USERHOST
238 303 = RPL_ISON
239 304 = RPL_UNAWAY
240 305 = RPL_NOWAWAY
241 311 = RPL_WHOISUSER
242 312 = RPL_WHOISSERVER
243 313 = RPL_WHOISOPERATOR
244 314 = RPL_WHOWASUSER
245 315 = RPL_ENDOFWHO
246 317 = RPL_WHOISIDLE
247 318 = RPL_ENDOFWHOIS
248 319 = RPL_WHOISCHANNELS
249 321 = RPL_LISTSTART
250 322 = RPL_LIST
251 323 = RPL_LISTEND
252 324 = RPL_CHANNELMODEIS
253 325 = RPL_UNIQOPIS
254 331 = RPL_NOTOPIC
255 332 = RPL_TOPIC
256 341 = RPL_INVITING
257 342 = RPL_SUMMONING
258 346 = RPL_INVITELIST
259 347 = RPL_ENDOFINVITELIST
260 348 = RPL_EXCEPTLIST
261 349 = RPL_ENDOFEXCEPTLIST
262 351 = RPL_VERSION
263 352 = RPL_WHOREPLY
264 353 = RPL_NAMREPLY
265 364 = RPL_LINKS
266 365 = RPL_ENDOFLINKS
267 366 = RPL_ENDOFNAMES
268 367 = RPL_BANLIST
269 368 = RPL_ENDOFBANLIST
270 369 = RPL_ENDOFWHOWAS
271 371 = RPL_INFO
272 372 = RPL_MOTD
273 374 = RPL_ENDOFINFO
274 375 = RPL_MOTDSTART
275 376 = RPL_ENDOFMOTD
276 381 = RPL_YOUREOPER
277 382 = RPL_REHASHING
278 383 = RPL_YOURESERVICE
279 391 = RPL_TIME
280 392 = RPL_USERSSTART
281 393 = RPL_USERS
282 394 = RPL_ENDOFUSERS
283 395 = RPL_NOUSERS
284 _ = undef
285
286 instance toInt IRCReplies where
287 toInt r = case r of
288 RPL_WELCOME = 1
289 RPL_YOURHOST = 2
290 RPL_CREATED = 3
291 RPL_MYINFO = 4
292 RPL_BOUNCE = 5
293 RPL_TRACELINK = 200
294 RPL_TRACECONNECTING = 201
295 RPL_TRACEHANDSHAKE = 202
296 RPL_TRACEUNKNOWN = 203
297 RPL_TRACEOPERATOR = 204
298 RPL_TRACEUSER = 205
299 RPL_TRACESERVER = 206
300 RPL_TRACESERVICE = 207
301 RPL_TRACENEWTYPE = 208
302 RPL_TRACECLASS = 209
303 RPL_TRACERECONNECT = 210
304 RPL_STATSLINKINFO = 211
305 RPL_STATSCOMMANDS = 212
306 RPL_ENDOFSTATS = 219
307 RPL_UMODEIS = 221
308 RPL_SERVLIST = 234
309 RPL_SERVLISTEND = 234
310 RPL_STATSUPTIME = 242
311 RPL_STATSOLINE = 243
312 RPL_LUSERCLIENT = 251
313 RPL_LUSEROP = 252
314 RPL_LUSERUNKNOWN = 253
315 RPL_LUSERCHANNELS = 254
316 RPL_LUSERME = 255
317 RPL_ADMINME = 256
318 RPL_ADMINLOC1 = 257
319 RPL_ADMINLOC2 = 258
320 RPL_ADMINEMAIL = 259
321 RPL_TRACELOG = 261
322 RPL_TRACEEND = 262
323 RPL_TRYAGAIN = 263
324 RPL_AWAY = 301
325 RPL_USERHOST = 302
326 RPL_ISON = 303
327 RPL_UNAWAY = 304
328 RPL_NOWAWAY = 305
329 RPL_WHOISUSER = 311
330 RPL_WHOISSERVER = 312
331 RPL_WHOISOPERATOR = 313
332 RPL_WHOWASUSER = 314
333 RPL_ENDOFWHO = 315
334 RPL_WHOISIDLE = 317
335 RPL_ENDOFWHOIS = 318
336 RPL_WHOISCHANNELS = 319
337 RPL_LISTSTART = 321
338 RPL_LIST = 322
339 RPL_LISTEND = 323
340 RPL_CHANNELMODEIS = 324
341 RPL_UNIQOPIS = 325
342 RPL_NOTOPIC = 331
343 RPL_TOPIC = 332
344 RPL_INVITING = 341
345 RPL_SUMMONING = 342
346 RPL_INVITELIST = 346
347 RPL_ENDOFINVITELIST = 347
348 RPL_EXCEPTLIST = 348
349 RPL_ENDOFEXCEPTLIST = 349
350 RPL_VERSION = 351
351 RPL_WHOREPLY = 352
352 RPL_NAMREPLY = 353
353 RPL_LINKS = 364
354 RPL_ENDOFLINKS = 365
355 RPL_ENDOFNAMES = 366
356 RPL_BANLIST = 367
357 RPL_ENDOFBANLIST = 367
358 RPL_ENDOFWHOWAS = 369
359 RPL_INFO = 371
360 RPL_MOTD = 372
361 RPL_ENDOFINFO = 374
362 RPL_MOTDSTART = 375
363 RPL_ENDOFMOTD = 376
364 RPL_YOUREOPER = 381
365 RPL_REHASHING = 382
366 RPL_YOURESERVICE = 383
367 RPL_TIME = 391
368 RPL_USERSSTART = 392
369 RPL_USERS = 393
370 RPL_ENDOFUSERS = 394
371 RPL_NOUSERS = 395
372
373 instance fromInt IRCErrors where
374 fromInt r = case r of
375 401 = ERR_NOSUCHNICK
376 402 = ERR_NOSUCHSERVER
377 403 = ERR_NOSUCHCHANNEL
378 404 = ERR_CANNOTSENDTOCHAN
379 405 = ERR_TOOMANYCHANNELS
380 406 = ERR_WASNOSUCHNICK
381 407 = ERR_TOOMANYTARGETS
382 408 = ERR_NOSUCHSERVICE
383 409 = ERR_NOORIGIN
384 411 = ERR_NORECIPIENT
385 412 = ERR_NOTEXTTOSEND
386 413 = ERR_NOTOPLEVEL
387 414 = ERR_WILDTOPLEVEL
388 415 = ERR_BADMASK
389 421 = ERR_UNKNOWNCOMMAND
390 422 = ERR_NOMOTD
391 423 = ERR_NOADMININFO
392 424 = ERR_FILEERROR
393 431 = ERR_NONICKNAMEGIVEN
394 432 = ERR_ERRONEUSNICKNAME
395 433 = ERR_NICKNAMEINUSE
396 436 = ERR_NICKCOLLISION
397 437 = ERR_UNAVAILRESOURCE
398 441 = ERR_USERNOTINCHANNEL
399 442 = ERR_NOTONCHANNEL
400 443 = ERR_USERONCHANNEL
401 444 = ERR_NOLOGIN
402 445 = ERR_SUMMONDISABLED
403 446 = ERR_USERSDISABLED
404 451 = ERR_NOTREGISTERED
405 461 = ERR_NEEDMOREPARAMS
406 462 = ERR_ALREADYREGISTRED
407 463 = ERR_NOPERMFORHOST
408 464 = ERR_PASSWDMISMATCH
409 465 = ERR_YOUREBANNEDCREEP
410 466 = ERR_YOUWILLBEBANNED
411 467 = ERR_KEYSET
412 471 = ERR_CHANNELISFULL
413 472 = ERR_UNKNOWNMODE
414 473 = ERR_INVITEONLYCHAN
415 474 = ERR_BANNEDFROMCHAN
416 475 = ERR_BADCHANNELKEY
417 476 = ERR_BADCHANMASK
418 477 = ERR_NOCHANMODES
419 478 = ERR_BANLISTFULL
420 481 = ERR_NOPRIVILEGES
421 482 = ERR_CHANOPRIVSNEEDED
422 483 = ERR_CANTKILLSERVER
423 484 = ERR_RESTRICTED
424 485 = ERR_UNIQOPPRIVSNEEDED
425 491 = ERR_NOOPERHOST
426 501 = ERR_UMODEUNKNOWNFLAG
427 502 = ERR_USERSDONTMATCH
428
429 instance toInt IRCErrors where
430 toInt r = case r of
431 ERR_NOSUCHNICK = 401
432 ERR_NOSUCHSERVER = 402
433 ERR_NOSUCHCHANNEL = 403
434 ERR_CANNOTSENDTOCHAN = 404
435 ERR_TOOMANYCHANNELS = 405
436 ERR_WASNOSUCHNICK = 406
437 ERR_TOOMANYTARGETS = 407
438 ERR_NOSUCHSERVICE = 408
439 ERR_NOORIGIN = 409
440 ERR_NORECIPIENT = 411
441 ERR_NOTEXTTOSEND = 412
442 ERR_NOTOPLEVEL = 413
443 ERR_WILDTOPLEVEL = 414
444 ERR_BADMASK = 415
445 ERR_UNKNOWNCOMMAND = 421
446 ERR_NOMOTD = 422
447 ERR_NOADMININFO = 423
448 ERR_FILEERROR = 424
449 ERR_NONICKNAMEGIVEN = 431
450 ERR_ERRONEUSNICKNAME = 432
451 ERR_NICKNAMEINUSE = 433
452 ERR_NICKCOLLISION = 436
453 ERR_UNAVAILRESOURCE = 437
454 ERR_USERNOTINCHANNEL = 441
455 ERR_NOTONCHANNEL = 442
456 ERR_USERONCHANNEL = 443
457 ERR_NOLOGIN = 444
458 ERR_SUMMONDISABLED = 445
459 ERR_USERSDISABLED = 446
460 ERR_NOTREGISTERED = 451
461 ERR_NEEDMOREPARAMS = 461
462 ERR_ALREADYREGISTRED = 462
463 ERR_NOPERMFORHOST = 463
464 ERR_PASSWDMISMATCH = 464
465 ERR_YOUREBANNEDCREEP = 465
466 ERR_YOUWILLBEBANNED = 466
467 ERR_KEYSET = 467
468 ERR_CHANNELISFULL = 471
469 ERR_UNKNOWNMODE = 472
470 ERR_INVITEONLYCHAN = 473
471 ERR_BANNEDFROMCHAN = 474
472 ERR_BADCHANNELKEY = 475
473 ERR_BADCHANMASK = 476
474 ERR_NOCHANMODES = 477
475 ERR_BANLISTFULL = 478
476 ERR_NOPRIVILEGES = 481
477 ERR_CHANOPRIVSNEEDED = 482
478 ERR_CANTKILLSERVER = 483
479 ERR_RESTRICTED = 484
480 ERR_UNIQOPPRIVSNEEDED = 485
481 ERR_NOOPERHOST = 491
482 ERR_UMODEUNKNOWNFLAG = 501
483 ERR_USERSDONTMATCH = 502