1 """ |
|
2 Irker protocol implementation. |
|
3 """ |
|
4 |
|
5 from twisted.internet import protocol, defer |
|
6 from twisted.protocols import basic |
|
7 from twisted.python import log |
|
8 |
|
9 import json, urlparse |
|
10 |
|
11 class Irk (basic.LineOnlyReceiver) : |
|
12 """ |
|
13 A connected Irk client. |
|
14 """ |
|
15 |
|
16 delimiter = '\n' |
|
17 |
|
18 def connectionMade (self) : |
|
19 self.transport.logPrefix = self.logPrefix |
|
20 |
|
21 log.msg("connected", self) |
|
22 |
|
23 def connectionLost (self, reason) : |
|
24 log.err("connection lost", reason) |
|
25 |
|
26 def error (self, *args) : |
|
27 log.err(*args) |
|
28 self.transport.loseConnection() |
|
29 |
|
30 def lineReceived (self, line) : |
|
31 """ |
|
32 JSON -> in |
|
33 """ |
|
34 |
|
35 try : |
|
36 irk = json.loads(line) |
|
37 |
|
38 except ValueError as ex : |
|
39 # invalid |
|
40 return self.error(ex, line) |
|
41 |
|
42 # dispatch |
|
43 self.factory.irkReceived(irk).addErrback(self.error, line) |
|
44 |
|
45 def __str__ (self) : |
|
46 if not self.transport : return 'Irk' |
|
47 |
|
48 host = self.transport.getHost() |
|
49 peer = self.transport.getPeer() |
|
50 |
|
51 return "{host.host}:{host.port}:{peer.host}".format(host=host, peer=peer) |
|
52 |
|
53 logPrefix = __str__ |
|
54 |
|
55 class IrkFactory (protocol.ServerFactory) : |
|
56 """ |
|
57 Manage connected Irk clients. |
|
58 """ |
|
59 |
|
60 MAXATTR = 16 |
|
61 |
|
62 protocol = Irk |
|
63 |
|
64 def __init__ (self, irc) : |
|
65 self.irc = irc |
|
66 |
|
67 @defer.inlineCallbacks |
|
68 def irkReceived (self, irk) : |
|
69 """ |
|
70 Deffered to handle lookup of target, and then sending message. |
|
71 |
|
72 Errbacks on failures. |
|
73 """ |
|
74 |
|
75 log.msg(str(irk)) |
|
76 |
|
77 if not 'to' in irk : |
|
78 raise ValueError("missing target: to") |
|
79 |
|
80 # MUST NOT be unicode |
|
81 # XXX: ValueError? |
|
82 url = urlparse.urlparse(str(irk.pop('to'))) |
|
83 |
|
84 # connect, register, join |
|
85 target = yield self.irc.target(url) |
|
86 |
|
87 # dispatch attrs |
|
88 for attr, value in irk.iteritems() : |
|
89 if len(attr) >= self.MAXATTR or not attr.islower() : |
|
90 raise ValueError("invalid attr: %s" % (attr, )) |
|
91 |
|
92 method = getattr(self, 'irk_' + attr, None) |
|
93 |
|
94 if not method : |
|
95 raise ValueError("unknown attr: %s" % (attr, )) |
|
96 |
|
97 if value : |
|
98 value = unicode(value) |
|
99 else : |
|
100 value = None |
|
101 |
|
102 method(target, value) |
|
103 |
|
104 # XXX: explicitly enable? |
|
105 def irk_privmsg (self, target, value) : |
|
106 """ |
|
107 Send PRIVMSG to target. |
|
108 """ |
|
109 |
|
110 if not value : |
|
111 # legacy |
|
112 return |
|
113 |
|
114 target.privmsg(value) |
|
115 |
|
116 def irk_notice (self, target, value) : |
|
117 """ |
|
118 Send NOTICE to target. |
|
119 """ |
|
120 |
|
121 if not value : |
|
122 raise ValueError("empty notice") |
|
123 |
|
124 target.notice(value) |
|
125 |
|
126 # TODO: refcounting vs join! |
|
127 def irk_part (self, target, value) : |
|
128 """ |
|
129 PART target. |
|
130 """ |
|
131 |
|
132 # value is optional |
|
133 target.part(value) |
|