summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/irc.nim101
-rwxr-xr-xlib/pure/sockets.nim15
2 files changed, 85 insertions, 31 deletions
diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim
index 7e1cd0eca..43687a74f 100644
--- a/lib/pure/irc.nim
+++ b/lib/pure/irc.nim
@@ -39,6 +39,8 @@ type
     lastPong: float
     lag: float
     channelsToJoin: seq[string]
+    msgLimit: bool
+    messageBuffer: seq[tuple[timeToSend: float, m: string]]
 
   TIRCMType* = enum
     MUnknown,
@@ -54,7 +56,8 @@ type
     MNick,
     MNotice,
     MPing,
-    MPong
+    MPong,
+    MError
   
   TIRCEventType* = enum
     EvMsg, EvDisconnected
@@ -66,16 +69,27 @@ type
       nick*, user*, host*, servername*: string
       numeric*: string
       params*: seq[string]
+      origin*: string ## The channel/user that this msg originated from
       raw*: string
   
-proc send*(irc: var TIRC, message: string) =
+proc send*(irc: var TIRC, message: string, sendImmediately = false) =
   ## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
-  try:
-    irc.sock.send(message & "\c\L")
-  except EOS:
-    # Assuming disconnection of every EOS could be bad,
-    # but I can't exactly check for EBrokenPipe.
-    irc.connected = false
+  var sendMsg = true
+  if irc.msgLimit and not sendImmediately:
+    var timeToSend = epochTime()
+    if irc.messageBuffer.len() >= 3:
+      timeToSend = (irc.messageBuffer[irc.messageBuffer.len()-1][0] + 2.0)
+
+    irc.messageBuffer.add((timeToSend, message))
+    sendMsg = false
+
+  if sendMsg:
+    try:
+      irc.sock.send(message & "\c\L")
+    except EOS:
+      # Assuming disconnection of every EOS could be bad,
+      # but I can't exactly check for EBrokenPipe.
+      irc.connected = false
 
 proc privmsg*(irc: var TIRC, target, message: string) =
   ## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
@@ -147,6 +161,7 @@ proc parseMessage(msg: string): TIRCEvent =
     of "QUIT": result.cmd = MQuit
     of "NICK": result.cmd = MNick
     of "NOTICE": result.cmd = MNotice
+    of "ERROR": result.cmd = MError
     else: result.cmd = MUnknown
   
   # Don't skip space here. It is skipped in the following While loop.
@@ -172,17 +187,20 @@ proc connect*(irc: var TIRC) =
   
   irc.sock = socket()
   irc.sock.connect(irc.address, irc.port)
+ 
+  irc.connected = true
   
   # Greet the server :)
-  if irc.serverPass != "": irc.send("PASS " & irc.serverPass)
-  irc.send("NICK " & irc.nick)
-  irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname])
+  if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
+  irc.send("NICK " & irc.nick, true)
+  irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
 
 proc irc*(address: string, port: TPort = 6667.TPort,
          nick = "NimrodBot",
          user = "NimrodBot",
          realname = "NimrodBot", serverPass = "",
-         joinChans: seq[string] = @[]): TIRC =
+         joinChans: seq[string] = @[],
+         msgLimit: bool = true): TIRC =
   ## This function calls `connect`, so you don't need to.
   result.address = address
   result.port = port
@@ -194,6 +212,8 @@ proc irc*(address: string, port: TPort = 6667.TPort,
   result.lastPong = -1.0
   result.lag = -1.0
   result.channelsToJoin = joinChans
+  result.msgLimit = msgLimit
+  result.messageBuffer = @[]
 
   result.connect()
   
@@ -214,6 +234,14 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
         ev.typ = EvDisconnected
       else:
         ev = parseMessage(line.string)
+        # Get the origin
+        ev.origin = ev.params[0]
+        if ev.origin == irc.nick: ev.origin = ev.nick
+
+        if ev.cmd == MError:
+          ev.typ = EvDisconnected
+          return
+
         if ev.cmd == MPing:
           irc.send("PONG " & ev.params[0])
         if ev.cmd == MPong:
@@ -227,7 +255,20 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
 
   if epochTime() - irc.lastPing >= 20.0:
     irc.lastPing = epochTime()
-    irc.send("PING :" & formatFloat(irc.lastPing))
+    irc.send("PING :" & formatFloat(irc.lastPing), true)
+
+  if epochTime() - irc.lastPong >= 120.0 and irc.lastPong != -1.0:
+    ev.typ = EvDisconnected # TODO: EvTimeout?
+    return true
+
+  
+  for i in 0..irc.messageBuffer.len-1:
+    if epochTime() >= irc.messageBuffer[0][0]:
+      irc.send(irc.messageBuffer[0].m, true)
+      irc.messageBuffer.delete(0)
+    else:
+      break # messageBuffer is guaranteed to be from the quickest to the
+            # later-est.
 
 proc getLag*(irc: var TIRC): float =
   ## Returns the latency between this client and the IRC server in seconds.
@@ -235,17 +276,18 @@ proc getLag*(irc: var TIRC): float =
   ## If latency is unknown, returns -1.0.
   return irc.lag
 
-proc getLastPong*(irc: var TIRC): float =
-  ## Returns the last time the server has responded to a PING message.
-  ##
-  ## This is useful if you want to detect whether your 
-  ## connection has timed out.
-  ## 
-  ## If a PONG has never been received, returns -1.0.
-  return irc.lastPong
- 
+proc isConnected*(irc: var TIRC): bool =
+  ## Returns whether this IRC client is connected to an IRC server.
+  return irc.connected
+
 when isMainModule:
-  var client = irc("irc.freenode.net", nick="TestBot", joinChans = @["#nimrod"])
+  #var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
+  #echo(repr(m))
+
+  #discard """
+  
+  var client = irc("amber.tenthbit.net", nick="TestBot1234",
+                   joinChans = @["#flood"])
   while True:
     var event: TIRCEvent
     if client.poll(event):
@@ -255,8 +297,11 @@ when isMainModule:
       of EvMsg:
         if event.cmd == MPrivMsg:
           var msg = event.params[event.params.high]
-          if msg == "|test": client.privmsg(event.params[0], "hello")
-          
-        echo( repr(event) )
-      echo("Lag: ", formatFloat(client.getLag()))
-      echo("Last pong: ", formatFloat(client.getLastPong()))
+          if msg == "|test": client.privmsg(event.origin, "hello")
+          if msg == "|excessFlood":
+            for i in 0..10:
+              client.privmsg(event.origin, "TEST" & $i)
+
+        #echo( repr(event) )
+      #echo("Lag: ", formatFloat(client.getLag()))
+  #"""
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index e1851301e..981afb5ca 100755
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -175,6 +175,15 @@ proc parseIp4*(s: string): int32 =
   if s[i] != '\0': invalidIp4(s)
   result = int32(a shl 24 or b shl 16 or c shl 8 or d)
 
+template gaiNim(a, p, h, l: expr): stmt =
+  block:
+    var gaiResult = getAddrInfo(a, $p, addr(h), l)
+    if gaiResult != 0'i32:
+      when defined(windows):
+        OSError()
+      else:
+        OSError($gai_strerror(gaiResult))
+
 proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
   ## binds an address/port number to a socket.
   ## Use address string in dotted decimal form like "a.b.c.d"
@@ -197,7 +206,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
     hints.ai_family = toInt(AF_INET)
     hints.ai_socktype = toInt(SOCK_STREAM)
     hints.ai_protocol = toInt(IPPROTO_TCP)
-    if getAddrInfo(address, $port, addr(hints), aiList) != 0'i32: OSError()
+    gaiNim(address, port, hints, aiList)
     if bindSocket(cint(socket), aiList.ai_addr, aiList.ai_addrLen) < 0'i32:
       OSError()
 
@@ -355,7 +364,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
   hints.ai_family = toInt(af)
   hints.ai_socktype = toInt(SOCK_STREAM)
   hints.ai_protocol = toInt(IPPROTO_TCP)
-  if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
+  gaiNim(name, port, hints, aiList)
   # try all possibilities:
   var success = false
   var it = aiList
@@ -391,7 +400,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
   hints.ai_family = toInt(af)
   hints.ai_socktype = toInt(SOCK_STREAM)
   hints.ai_protocol = toInt(IPPROTO_TCP)
-  if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
+  gaiNim(name, port, hints, aiList)
   # try all possibilities:
   var success = false
   var it = aiList
hut@lavabit.com> 2009-12-25 21:55:04 +0100 committer hut <hut@lavabit.com> 2009-12-25 21:55:04 +0100 updated pydoc pages' href='/akspecs/ranger/commit/doc/ranger.gui.widgets.statusbar.html?h=v1.7.0-emacs&id=f07bb12fc5c59430e995a64956b36331ce3629b9'>f07bb12f ^
4c13e1f2 ^
f07bb12f ^












f07bb12f ^















4c13e1f2 ^











f07bb12f ^
4e9450f9 ^
f07bb12f ^



























4c13e1f2 ^



f07bb12f ^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210