Fixed issue with some functions in the sockets module not reporting the correct error message. Added more disconnection cases in the irc module, also added a message limiting system.

This commit is contained in:
dom96 2011-10-30 18:25:33 +00:00
commit f5616bcb0b
2 changed files with 85 additions and 31 deletions

View file

@ -39,6 +39,8 @@ type
lastPong: float lastPong: float
lag: float lag: float
channelsToJoin: seq[string] channelsToJoin: seq[string]
msgLimit: bool
messageBuffer: seq[tuple[timeToSend: float, m: string]]
TIRCMType* = enum TIRCMType* = enum
MUnknown, MUnknown,
@ -54,7 +56,8 @@ type
MNick, MNick,
MNotice, MNotice,
MPing, MPing,
MPong MPong,
MError
TIRCEventType* = enum TIRCEventType* = enum
EvMsg, EvDisconnected EvMsg, EvDisconnected
@ -66,10 +69,21 @@ type
nick*, user*, host*, servername*: string nick*, user*, host*, servername*: string
numeric*: string numeric*: string
params*: seq[string] params*: seq[string]
origin*: string ## The channel/user that this msg originated from
raw*: string 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. ## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
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: try:
irc.sock.send(message & "\c\L") irc.sock.send(message & "\c\L")
except EOS: except EOS:
@ -147,6 +161,7 @@ proc parseMessage(msg: string): TIRCEvent =
of "QUIT": result.cmd = MQuit of "QUIT": result.cmd = MQuit
of "NICK": result.cmd = MNick of "NICK": result.cmd = MNick
of "NOTICE": result.cmd = MNotice of "NOTICE": result.cmd = MNotice
of "ERROR": result.cmd = MError
else: result.cmd = MUnknown else: result.cmd = MUnknown
# Don't skip space here. It is skipped in the following While loop. # Don't skip space here. It is skipped in the following While loop.
@ -173,16 +188,19 @@ proc connect*(irc: var TIRC) =
irc.sock = socket() irc.sock = socket()
irc.sock.connect(irc.address, irc.port) irc.sock.connect(irc.address, irc.port)
irc.connected = true
# Greet the server :) # Greet the server :)
if irc.serverPass != "": irc.send("PASS " & irc.serverPass) if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
irc.send("NICK " & irc.nick) irc.send("NICK " & irc.nick, true)
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname]) irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
proc irc*(address: string, port: TPort = 6667.TPort, proc irc*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot", nick = "NimrodBot",
user = "NimrodBot", user = "NimrodBot",
realname = "NimrodBot", serverPass = "", realname = "NimrodBot", serverPass = "",
joinChans: seq[string] = @[]): TIRC = joinChans: seq[string] = @[],
msgLimit: bool = true): TIRC =
## This function calls `connect`, so you don't need to. ## This function calls `connect`, so you don't need to.
result.address = address result.address = address
result.port = port result.port = port
@ -194,6 +212,8 @@ proc irc*(address: string, port: TPort = 6667.TPort,
result.lastPong = -1.0 result.lastPong = -1.0
result.lag = -1.0 result.lag = -1.0
result.channelsToJoin = joinChans result.channelsToJoin = joinChans
result.msgLimit = msgLimit
result.messageBuffer = @[]
result.connect() result.connect()
@ -214,6 +234,14 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
ev.typ = EvDisconnected ev.typ = EvDisconnected
else: else:
ev = parseMessage(line.string) 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: if ev.cmd == MPing:
irc.send("PONG " & ev.params[0]) irc.send("PONG " & ev.params[0])
if ev.cmd == MPong: if ev.cmd == MPong:
@ -227,7 +255,20 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
if epochTime() - irc.lastPing >= 20.0: if epochTime() - irc.lastPing >= 20.0:
irc.lastPing = epochTime() 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 = proc getLag*(irc: var TIRC): float =
## Returns the latency between this client and the IRC server in seconds. ## 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. ## If latency is unknown, returns -1.0.
return irc.lag return irc.lag
proc getLastPong*(irc: var TIRC): float = proc isConnected*(irc: var TIRC): bool =
## Returns the last time the server has responded to a PING message. ## Returns whether this IRC client is connected to an IRC server.
## return irc.connected
## 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
when isMainModule: 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: while True:
var event: TIRCEvent var event: TIRCEvent
if client.poll(event): if client.poll(event):
@ -255,8 +297,11 @@ when isMainModule:
of EvMsg: of EvMsg:
if event.cmd == MPrivMsg: if event.cmd == MPrivMsg:
var msg = event.params[event.params.high] var msg = event.params[event.params.high]
if msg == "|test": client.privmsg(event.params[0], "hello") 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( repr(event) )
echo("Lag: ", formatFloat(client.getLag())) #echo("Lag: ", formatFloat(client.getLag()))
echo("Last pong: ", formatFloat(client.getLastPong())) #"""

View file

@ -175,6 +175,15 @@ proc parseIp4*(s: string): int32 =
if s[i] != '\0': invalidIp4(s) if s[i] != '\0': invalidIp4(s)
result = int32(a shl 24 or b shl 16 or c shl 8 or d) 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 = "") = proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
## binds an address/port number to a socket. ## binds an address/port number to a socket.
## Use address string in dotted decimal form like "a.b.c.d" ## 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_family = toInt(AF_INET)
hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_socktype = toInt(SOCK_STREAM)
hints.ai_protocol = toInt(IPPROTO_TCP) 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: if bindSocket(cint(socket), aiList.ai_addr, aiList.ai_addrLen) < 0'i32:
OSError() OSError()
@ -355,7 +364,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
hints.ai_family = toInt(af) hints.ai_family = toInt(af)
hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_socktype = toInt(SOCK_STREAM)
hints.ai_protocol = toInt(IPPROTO_TCP) hints.ai_protocol = toInt(IPPROTO_TCP)
if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError() gaiNim(name, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var it = aiList var it = aiList
@ -391,7 +400,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
hints.ai_family = toInt(af) hints.ai_family = toInt(af)
hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_socktype = toInt(SOCK_STREAM)
hints.ai_protocol = toInt(IPPROTO_TCP) hints.ai_protocol = toInt(IPPROTO_TCP)
if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError() gaiNim(name, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var it = aiList var it = aiList