]> jfr.im git - irc/quakenet/qwebirc.git/blame - qwebirc/ircclient.py
Change the way timeouts are processed entirely to make the entire system more resilient.
[irc/quakenet/qwebirc.git] / qwebirc / ircclient.py
CommitLineData
9e769c12
CP
1import twisted, sys
2from twisted.words.protocols import irc
3from twisted.internet import reactor, protocol
4from twisted.web import resource, server
5from twisted.protocols import basic
6
7import hmac, time, config
8from config import HMACTEMPORAL
9HMACKEY = hmac.HMAC(key=config.HMACKEY)
10
11def hmacfn(*args):
12 h = HMACKEY.copy()
13 h.update("%d %s" % (int(time.time() / HMACTEMPORAL), " ".join(args)))
14 return h.hexdigest()
15
16class QWebIRCClient(basic.LineReceiver):
17 delimiter = "\n"
85f01e3f
CP
18 def __init__(self, *args, **kwargs):
19 self.__nickname = "(unregistered)"
20
9e769c12
CP
21 def dataReceived(self, data):
22 basic.LineReceiver.dataReceived(self, data.replace("\r", ""))
23
24 def lineReceived(self, line):
25 line = irc.lowDequote(line)
26 try:
27 line = line.decode("utf-8")
28 except UnicodeDecodeError:
29 line = line.decode("iso-8859-1", "ignore")
30
31 try:
32 prefix, command, params = irc.parsemsg(line)
33 self.handleCommand(command, prefix, params)
34 except irc.IRCBadMessage:
35 self.badMessage(line, *sys.exc_info())
ace37679 36
85f01e3f
CP
37 if command == "001":
38 self.__nickname = params[0]
ace37679 39
85f01e3f
CP
40 if self.__perform is not None:
41 for x in self.__perform:
42 self.write(x)
43 self.__perform = None
44 elif command == "NICK":
45 nick = prefix.split("!", 1)[0]
46 if nick == self.__nickname:
47 self.__nickname = params[0]
48
9e769c12
CP
49 def badMessage(self, args):
50 self("badmessage", args)
51
52 def handleCommand(self, command, prefix, params):
53 self("c", command, prefix, params)
54
55 def __call__(self, *args):
56 self.factory.publisher.event(args)
57
58 def write(self, data):
59 self.transport.write("%s\r\n" % irc.lowQuote(data.encode("utf-8")))
60
61 def connectionMade(self):
62 basic.LineReceiver.connectionMade(self)
63
8dc46dfa 64 self.lastError = None
9e769c12
CP
65 f = self.factory.ircinit
66 nick, ident, ip, realname = f["nick"], f["ident"], f["ip"], f["realname"]
85f01e3f 67 self.__nickname = nick
ace37679 68 self.__perform = f.get("perform")
9e769c12
CP
69
70 hmac = hmacfn(ident, ip)
71 self.write("USER %s bleh bleh %s %s :%s" % (ident, ip, hmac, realname))
72 self.write("NICK %s" % nick)
73
74 self.factory.client = self
75 self("connect")
76
85f01e3f
CP
77 def __str__(self):
78 return "<QWebIRCClient: %s!%s@%s>" % (self.__nickname, self.factory.ircinit["ident"], self.factory.ircinit["ip"])
79
9e769c12 80 def connectionLost(self, reason):
8dc46dfa
CP
81 if self.lastError:
82 self.disconnect("Connection to IRC server lost: %s" % self.lastError)
83 else:
84 self.disconnect("Connection to IRC server lost.")
9e769c12
CP
85 self.factory.client = None
86 basic.LineReceiver.connectionLost(self, reason)
8dc46dfa
CP
87
88 def error(self, message):
89 self.lastError = message
90 self.write("QUIT :qwebirc exception: %s" % message)
91 self.transport.loseConnection()
92
93 def disconnect(self, reason):
94 self("disconnect", reason)
95 self.factory.publisher.disconnect()
9e769c12
CP
96
97class QWebIRCFactory(protocol.ClientFactory):
98 protocol = QWebIRCClient
99 def __init__(self, publisher, **kwargs):
100 self.client = None
101 self.publisher = publisher
102 self.ircinit = kwargs
103
104 def write(self, data):
105 self.client.write(data)
8dc46dfa
CP
106
107 def error(self, reason):
108 self.client.error(reason)
109
623b5428
CP
110 def clientConnectionFailed(self, connector, reason):
111 protocol.ClientFactory.clientConnectionFailed(self, connector, reason)
112 self.publisher.event(["disconnect", "Connection to IRC server failed."])
113 self.publisher.disconnect()
8dc46dfa 114
9e769c12
CP
115def createIRC(*args, **kwargs):
116 f = QWebIRCFactory(*args, **kwargs)
117 reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f)
118 return f
119
120if __name__ == "__main__":
121 e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo")
8dc46dfa 122 reactor.run()