Merge lp:~stefanor/ibid/summon-330869 into lp:~ibid-core/ibid/old-trunk-1.6
- summon-330869
- Merge into old-trunk-1.6
Proposed by
Stefano Rivera
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Michael Gorven | ||||||||
Approved revision: | 796 | ||||||||
Merged at revision: | 817 | ||||||||
Proposed branch: | lp:~stefanor/ibid/summon-330869 | ||||||||
Merge into: | lp:~ibid-core/ibid/old-trunk-1.6 | ||||||||
Diff against target: |
894 lines (+349/-106) 14 files modified
ibid/__init__.py (+2/-0) ibid/plugins/__init__.py (+1/-1) ibid/plugins/core.py (+32/-4) ibid/plugins/games.py (+4/-4) ibid/plugins/identity.py (+77/-3) ibid/plugins/irc.py (+3/-3) ibid/plugins/log.py (+48/-36) ibid/plugins/memo.py (+1/-1) ibid/plugins/seen.py (+7/-6) ibid/source/campfire.py (+3/-3) ibid/source/dc.py (+15/-1) ibid/source/irc.py (+82/-15) ibid/source/jabber.py (+47/-26) ibid/source/silc.py (+27/-3) |
||||||||
To merge this branch: | bzr merge lp:~stefanor/ibid/summon-330869 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Gorven | Approve | ||
Jonathan Hitchcock | Approve | ||
Review via email: mp+16616@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Stefano Rivera (stefanor) wrote : | # |
lp:~stefanor/ibid/summon-330869
updated
- 794. By Stefano Rivera
-
We now have ibid.compat
- 795. By Stefano Rivera
-
Werewolf missed the event_types unicoding
Revision history for this message
Jonathan Hitchcock (vhata) wrote : | # |
Seeeems good.
review:
Approve
lp:~stefanor/ibid/summon-330869
updated
- 796. By Stefano Rivera
-
Merge from trunk
Revision history for this message
Michael Gorven (mgorven) wrote : | # |
Seems fine.
review approve
status approved
review:
Approve
lp:~stefanor/ibid/summon-330869
updated
- 797. By Stefano Rivera
-
Merge from trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ibid/__init__.py' |
2 | --- ibid/__init__.py 2009-10-16 16:31:34 +0000 |
3 | +++ ibid/__init__.py 2009-12-30 22:21:13 +0000 |
4 | @@ -10,6 +10,7 @@ |
5 | import twisted.python.log |
6 | |
7 | import ibid.core |
8 | +from ibid.compat import defaultdict |
9 | from ibid.config import FileConfig |
10 | |
11 | class InsensitiveDict(dict): |
12 | @@ -32,6 +33,7 @@ |
13 | service = None |
14 | options = {} |
15 | rpc = {} |
16 | +channels = defaultdict(lambda: defaultdict(set)) |
17 | |
18 | def twisted_log(eventDict): |
19 | log = logging.getLogger('twisted') |
20 | |
21 | === modified file 'ibid/plugins/__init__.py' |
22 | --- ibid/plugins/__init__.py 2009-12-10 09:56:44 +0000 |
23 | +++ ibid/plugins/__init__.py 2009-12-30 22:21:13 +0000 |
24 | @@ -26,7 +26,7 @@ |
25 | explicitly required in the configuration file |
26 | """ |
27 | |
28 | - event_types = ('message',) |
29 | + event_types = (u'message',) |
30 | addressed = True |
31 | processed = False |
32 | priority = 0 |
33 | |
34 | === modified file 'ibid/plugins/core.py' |
35 | --- ibid/plugins/core.py 2009-12-30 20:53:06 +0000 |
36 | +++ ibid/plugins/core.py 2009-12-30 22:21:13 +0000 |
37 | @@ -7,6 +7,7 @@ |
38 | from ibid.compat import any |
39 | from ibid.config import IntOption, ListOption, DictOption |
40 | from ibid.plugins import Processor, handler |
41 | +from ibid.plugins.identity import identify |
42 | |
43 | class Addressed(Processor): |
44 | |
45 | @@ -43,7 +44,7 @@ |
46 | |
47 | priority = -1600 |
48 | addressed = False |
49 | - event_types = ('message', 'action', 'notice') |
50 | + event_types = (u'message', u'action', u'notice') |
51 | |
52 | pattern = re.compile(r'^\s*(.*?)\s*[?!.]*\s*$', re.DOTALL) |
53 | |
54 | @@ -58,7 +59,7 @@ |
55 | |
56 | priority = -1500 |
57 | addressed = False |
58 | - event_types = ('message', 'action', 'notice') |
59 | + event_types = (u'message', u'action', u'notice') |
60 | |
61 | nicks = ListOption('ignore', 'List of nicks to ignore', []) |
62 | |
63 | @@ -85,7 +86,6 @@ |
64 | priority = 1600 |
65 | processed = True |
66 | addressed = False |
67 | - |
68 | event_types = ('message', 'action', 'notice', 'state') |
69 | |
70 | acknowledgements = ListOption('acknowledgements', 'Responses for positive acknowledgements', |
71 | @@ -148,7 +148,7 @@ |
72 | class RateLimit(Processor): |
73 | |
74 | priority = -1000 |
75 | - event_types = ('message', 'action', 'notice') |
76 | + event_types = (u'message', u'action', u'notice') |
77 | |
78 | limit_time = IntOption('limit_time', 'Time period over which to measure messages', 10) |
79 | limit_messages = IntOption('limit_messages', 'Number of messages to allow during the time period', 5) |
80 | @@ -210,4 +210,32 @@ |
81 | elif isinstance(object, str): |
82 | self.log.warning(u'Found a non-unicode string: %s' % object) |
83 | |
84 | +class ChannelTracker(Processor): |
85 | + priority = -1550 |
86 | + addressed = False |
87 | + event_types = (u'state', u'source') |
88 | + |
89 | + @handler |
90 | + def track(self, event): |
91 | + if event.type == u'source': |
92 | + if event.status == u'disconnected': |
93 | + ibid.channels.pop(event.source, None) |
94 | + elif event.status == u'left': |
95 | + ibid.channels[event.source].pop(event.channel, None) |
96 | + elif event.public: |
97 | + if event.state == u'online' and hasattr(event, 'othername'): |
98 | + oldid = identify(event.session, event.source, event.othername) |
99 | + for channel in ibid.channels[event.source].values(): |
100 | + if oldid in channel: |
101 | + channel.remove(oldid) |
102 | + channel.add(event.identity) |
103 | + elif event.state == u'online': |
104 | + ibid.channels[event.source][event.channel].add(event.identity) |
105 | + elif event.state == u'offline' and not hasattr(event, 'othername'): |
106 | + if event.channel: |
107 | + ibid.channels[event.source][event.channel].remove(event.identity) |
108 | + else: |
109 | + for channel in ibid.channels[event.source].values(): |
110 | + channel.discard(event.identity) |
111 | + |
112 | # vi: set et sta sw=4 ts=4: |
113 | |
114 | === modified file 'ibid/plugins/games.py' |
115 | --- ibid/plugins/games.py 2009-12-30 14:08:03 +0000 |
116 | +++ ibid/plugins/games.py 2009-12-30 22:21:13 +0000 |
117 | @@ -218,7 +218,7 @@ |
118 | feature = 'duel' |
119 | |
120 | # Parameters for Processor: |
121 | - event_types = ('message', 'action') |
122 | + event_types = (u'message', u'action') |
123 | |
124 | addressed = BoolOption('addressed', 'Must the bot be addressed?', True) |
125 | |
126 | @@ -400,7 +400,7 @@ |
127 | class DuelFlee(Processor): |
128 | feature = 'duel' |
129 | addressed = False |
130 | - event_types = ('state',) |
131 | + event_types = (u'state',) |
132 | |
133 | @handler |
134 | def dueller_fled(self, event): |
135 | @@ -470,7 +470,7 @@ |
136 | seer_delay = IntOption('seer_delay', |
137 | 'Number of players between extra wolf and extra seer', 4) |
138 | |
139 | - event_types = ('message', 'action') |
140 | + event_types = (u'message', u'action') |
141 | |
142 | @match(r'^(?:start|play|begin)s?\b.*werewolf$') |
143 | def prestart(self, event): |
144 | @@ -843,7 +843,7 @@ |
145 | |
146 | class WerewolfState(Processor): |
147 | feature = 'werewolf' |
148 | - event_types = ('state',) |
149 | + event_types = (u'state',) |
150 | |
151 | @handler |
152 | def state_change(self, event): |
153 | |
154 | === modified file 'ibid/plugins/identity.py' |
155 | --- ibid/plugins/identity.py 2009-12-22 14:49:33 +0000 |
156 | +++ ibid/plugins/identity.py 2009-12-30 22:21:13 +0000 |
157 | @@ -3,9 +3,10 @@ |
158 | import logging |
159 | |
160 | import ibid |
161 | -from ibid.db import eagerload, IntegrityError |
162 | +from ibid.config import Option |
163 | +from ibid.db import eagerload, IntegrityError, and_, or_ |
164 | from ibid.db.models import Account, Identity, Attribute |
165 | -from ibid.plugins import Processor, match, auth_responses |
166 | +from ibid.plugins import Processor, match, handler, auth_responses, authorise |
167 | from ibid.utils import human_join |
168 | |
169 | help = {} |
170 | @@ -368,11 +369,79 @@ |
171 | 'identities': human_join(u'%s on %s' % (identity.identity, identity.source) for identity in account.identities), |
172 | }) |
173 | |
174 | +help['summon'] = u"Get the attention of a person via different source" |
175 | +class Summon(Processor): |
176 | + u"summon <person> [via <source>]" |
177 | + feature = 'summon' |
178 | + permission = u'summon' |
179 | + |
180 | + default_source = Option('default_source', |
181 | + u'Default source to summon people via', u'jabber') |
182 | + |
183 | + @authorise(fallthrough=False) |
184 | + @match(r'^summon\s+(\S+)(?:\s+(?:via|on|using)\s+(\S+))?$') |
185 | + def summon(self, event, who, source): |
186 | + if not source: |
187 | + source = self.default_source |
188 | + |
189 | + if source.lower() not in ibid.sources: |
190 | + event.addresponse(u"I'm afraid that I'm not connected to %s", |
191 | + source) |
192 | + return |
193 | + |
194 | + account = event.session.query(Account) \ |
195 | + .options(eagerload('identities')) \ |
196 | + .join(Identity) \ |
197 | + .filter( |
198 | + or_( |
199 | + and_( |
200 | + Identity.identity == who, |
201 | + Identity.source == event.source, |
202 | + ), |
203 | + Account.username == who, |
204 | + )) \ |
205 | + .first() |
206 | + |
207 | + if account: |
208 | + for other_identity in [id for id |
209 | + in account.identities |
210 | + if id.source.lower() == source.lower()]: |
211 | + if any(True for channel |
212 | + in ibid.channels[other_identity.source].itervalues() |
213 | + if other_identity.id in channel): |
214 | + event.addresponse(u'Your presence has been requested by ' |
215 | + u'%(who)s in %(channel)s on %(source)s.', |
216 | + { |
217 | + 'who': event.sender['nick'], |
218 | + 'channel': (not event.public) |
219 | + and u'private' or event.channel, |
220 | + 'source': event.source, |
221 | + }, target=other_identity.identity, |
222 | + source=other_identity.source, address=False) |
223 | + event.addresponse(True) |
224 | + else: |
225 | + event.addresponse( |
226 | + u"Sorry %s doesn't appear to be available right now.", |
227 | + who) |
228 | + return |
229 | + |
230 | + event.addresponse( |
231 | + u"Sorry, I don't know how to find %(who)s on %(source)s. " |
232 | + u'%(who)s must first link an identity on %(source)s.', { |
233 | + 'who': who, |
234 | + 'source': source, |
235 | + }) |
236 | + return |
237 | + |
238 | class Identify(Processor): |
239 | |
240 | priority = -1600 |
241 | + addressed = False |
242 | + processed = True |
243 | + event_types = (u'message', u'state', u'action', u'notice') |
244 | |
245 | - def process(self, event): |
246 | + @handler |
247 | + def handle(self, event): |
248 | if event.sender: |
249 | if (event.source, event.sender['connection']) in identify_cache: |
250 | (event.identity, event.account) = identify_cache[(event.source, event.sender['connection'])] |
251 | @@ -414,4 +483,9 @@ |
252 | else: |
253 | return (event.identity,) |
254 | |
255 | +def identify(session, source, id): |
256 | + identity = session.query(Identity) \ |
257 | + .filter_by(source=source, identity=id).first() |
258 | + return identity and identity.id |
259 | + |
260 | # vi: set et sta sw=4 ts=4: |
261 | |
262 | === modified file 'ibid/plugins/irc.py' |
263 | --- ibid/plugins/irc.py 2009-10-20 15:45:46 +0000 |
264 | +++ ibid/plugins/irc.py 2009-12-30 22:21:13 +0000 |
265 | @@ -43,8 +43,8 @@ |
266 | source.join(channel) |
267 | event.addresponse(u'Joining %s', channel) |
268 | else: |
269 | - source.part(channel) |
270 | - event.addresponse(u'Parting %s', channel) |
271 | + source.leave(channel) |
272 | + event.addresponse(u'Leaving %s', channel) |
273 | |
274 | @match(r'^change\s+nick\s+to\s+(\S+)(?:\s+on\s+(\S+))?$') |
275 | @authorise() |
276 | @@ -66,7 +66,7 @@ |
277 | event.addresponse(u'Changing nick to %s', nick) |
278 | |
279 | class NickServ(Processor): |
280 | - event_types = ('notice',) |
281 | + event_types = (u'notice',) |
282 | |
283 | def is_nickserv(self, event): |
284 | source_cfg = ibid.config['sources'][event.source] |
285 | |
286 | === modified file 'ibid/plugins/log.py' |
287 | --- ibid/plugins/log.py 2009-12-14 13:40:46 +0000 |
288 | +++ ibid/plugins/log.py 2009-12-30 22:21:13 +0000 |
289 | @@ -7,7 +7,7 @@ |
290 | from dateutil.tz import tzlocal, tzutc |
291 | |
292 | import ibid |
293 | -from ibid.plugins import Processor |
294 | +from ibid.plugins import Processor, handler |
295 | from ibid.config import Option, BoolOption |
296 | from ibid.event import Event |
297 | |
298 | @@ -15,6 +15,7 @@ |
299 | |
300 | addressed = False |
301 | processed = True |
302 | + event_types = (u'message', u'state', u'action', u'notice') |
303 | priority = 1900 |
304 | |
305 | log = Option('log', 'Log file to log messages to. Can contain substitutions: source, channel, year, month, day', |
306 | @@ -31,6 +32,8 @@ |
307 | u'%(timestamp)s -%(sender_nick)s- %(message)s') |
308 | presence_format = Option('presence_format', 'Format string for presence events', |
309 | u'%(timestamp)s %(sender_nick)s (%(sender_connection)s) is now %(state)s') |
310 | + rename_format = Option('rename_format', 'Format string for rename events', |
311 | + u'%(timestamp)s %(sender_nick)s (%(sender_connection)s) has renamed to %(new_nick)s') |
312 | |
313 | public_mode = Option('public_mode', |
314 | u'File Permissions mode for public channels, in octal', '644') |
315 | @@ -72,42 +75,51 @@ |
316 | return self.logs[filename] |
317 | |
318 | def log_event(self, event): |
319 | - if event.type in ('message', 'state', 'action', 'notice'): |
320 | - when = event.time |
321 | - if not self.date_utc: |
322 | - when = when.replace(tzinfo=tzutc()).astimezone(tzlocal()) |
323 | - |
324 | - format = { |
325 | - 'message': self.message_format, |
326 | - 'state': self.presence_format, |
327 | - 'action': self.action_format, |
328 | - 'notice': self.notice_format, |
329 | - }[event.type] |
330 | - |
331 | - fields = { |
332 | - 'source': event.source, |
333 | - 'channel': event.channel, |
334 | - 'sender_connection': event.sender['connection'], |
335 | - 'sender_id': event.sender['id'], |
336 | - 'sender_nick': event.sender['nick'], |
337 | - 'timestamp': unicode( |
338 | - when.strftime(self.timestamp_format.encode('utf8')), |
339 | - 'utf8'), |
340 | - } |
341 | - |
342 | - if event.type == 'state': |
343 | + when = event.time |
344 | + if not self.date_utc: |
345 | + when = when.replace(tzinfo=tzutc()).astimezone(tzlocal()) |
346 | + |
347 | + format = { |
348 | + 'message': self.message_format, |
349 | + 'state': self.presence_format, |
350 | + 'action': self.action_format, |
351 | + 'notice': self.notice_format, |
352 | + }[event.type] |
353 | + |
354 | + # We get two events on a rename, ignore one of them |
355 | + if event.type == 'state' and hasattr(event, 'othername'): |
356 | + if event.state == 'online': |
357 | + return |
358 | + format = self.rename_format |
359 | + |
360 | + fields = { |
361 | + 'source': event.source, |
362 | + 'channel': event.channel, |
363 | + 'sender_connection': event.sender['connection'], |
364 | + 'sender_id': event.sender['id'], |
365 | + 'sender_nick': event.sender['nick'], |
366 | + 'timestamp': unicode( |
367 | + when.strftime(self.timestamp_format.encode('utf8')), |
368 | + 'utf8') |
369 | + } |
370 | + |
371 | + if event.type == 'state': |
372 | + if hasattr(event, 'othername'): |
373 | + fields['new_nick'] = event.othername |
374 | + else: |
375 | fields['state'] = event.state |
376 | - elif isinstance(event.message, dict): |
377 | - fields['message'] = event.message['raw'] |
378 | - else: |
379 | - fields['message'] = event.message |
380 | - |
381 | - file = self.get_logfile(event) |
382 | - |
383 | - file.write((format % fields).encode('utf-8') + '\n') |
384 | - file.flush() |
385 | - |
386 | - def process(self, event): |
387 | + elif isinstance(event.message, dict): |
388 | + fields['message'] = event.message['raw'] |
389 | + else: |
390 | + fields['message'] = event.message |
391 | + |
392 | + file = self.get_logfile(event) |
393 | + |
394 | + file.write((format % fields).encode('utf-8') + '\n') |
395 | + file.flush() |
396 | + |
397 | + @handler |
398 | + def log_handler(self, event): |
399 | self.log_event(event) |
400 | |
401 | for response in event.responses: |
402 | |
403 | === modified file 'ibid/plugins/memo.py' |
404 | --- ibid/plugins/memo.py 2009-12-20 21:15:42 +0000 |
405 | +++ ibid/plugins/memo.py 2009-12-30 22:21:13 +0000 |
406 | @@ -285,7 +285,7 @@ |
407 | class Notify(Processor): |
408 | feature = 'memo' |
409 | |
410 | - event_types = ('state',) |
411 | + event_types = (u'state',) |
412 | addressed = False |
413 | processed = True |
414 | |
415 | |
416 | === modified file 'ibid/plugins/seen.py' |
417 | --- ibid/plugins/seen.py 2009-12-20 21:15:42 +0000 |
418 | +++ ibid/plugins/seen.py 2009-12-30 22:21:13 +0000 |
419 | @@ -5,7 +5,7 @@ |
420 | Table, Column, ForeignKey, UniqueConstraint, \ |
421 | relation, IntegrityError, Base, VersionedSchema |
422 | from ibid.db.models import Identity, Account |
423 | -from ibid.plugins import Processor, match |
424 | +from ibid.plugins import Processor, match, handler |
425 | from ibid.utils import ago, format_date |
426 | |
427 | log = logging.getLogger('plugins.seen') |
428 | @@ -60,11 +60,12 @@ |
429 | feature = 'seen' |
430 | |
431 | priority = 1500 |
432 | - |
433 | - def process(self, event): |
434 | - if event.type != 'message' and event.type != 'state': |
435 | - return |
436 | - |
437 | + event_types = (u'message', u'state') |
438 | + addressed = False |
439 | + processed = True |
440 | + |
441 | + @handler |
442 | + def see(self, event): |
443 | sighting = event.session.query(Sighting) \ |
444 | .filter_by(identity_id=event.identity, type=event.type).first() |
445 | if not sighting: |
446 | |
447 | === modified file 'ibid/source/campfire.py' |
448 | --- ibid/source/campfire.py 2009-12-30 16:29:10 +0000 |
449 | +++ ibid/source/campfire.py 2009-12-30 22:21:13 +0000 |
450 | @@ -84,7 +84,7 @@ |
451 | def join(self, room_name): |
452 | return self.join_room(self._locate_room(room_name)) |
453 | |
454 | - def part(self, room_name): |
455 | + def leave(self, room_name): |
456 | return self.leave_room(self._locate_room(room_name)) |
457 | |
458 | class SourceFactory(IbidSourceFactory): |
459 | @@ -122,7 +122,7 @@ |
460 | def join(self, room_name): |
461 | return self.client.join(room_name) |
462 | |
463 | - def part(self, room_name): |
464 | - return self.client.part(room_name) |
465 | + def leave(self, room_name): |
466 | + return self.client.leave(room_name) |
467 | |
468 | # vi: set et sta sw=4 ts=4: |
469 | |
470 | === modified file 'ibid/source/dc.py' |
471 | --- ibid/source/dc.py 2009-12-30 16:29:10 +0000 |
472 | +++ ibid/source/dc.py 2009-12-30 22:21:13 +0000 |
473 | @@ -42,6 +42,11 @@ |
474 | |
475 | def connectionLost(self, reason): |
476 | self.factory.log.info(u"Disconnected (%s)", reason) |
477 | + |
478 | + event = Event(self.factory.name, u'source') |
479 | + event.status = u'disconnected' |
480 | + ibid.dispatcher.dispatch(event) |
481 | + |
482 | dcwords.DCClient.connectionLost(self, reason) |
483 | |
484 | def signedOn(self): |
485 | @@ -51,6 +56,16 @@ |
486 | names.append(self.my_nickname) |
487 | ibid.config.plugins['core']['names'] = names |
488 | ibid.reloader.reload_config() |
489 | + |
490 | + event = Event(self.factory.name, u'source') |
491 | + event.status = u'connected' |
492 | + ibid.dispatcher.dispatch(event) |
493 | + |
494 | + event = Event(self.factory.name, u'source') |
495 | + event.channel = u'$public' |
496 | + event.status = u'joined' |
497 | + ibid.dispatcher.dispatch(event) |
498 | + |
499 | self.factory.log.info(u"Signed on") |
500 | |
501 | def _create_event(self, type, user): |
502 | @@ -60,7 +75,6 @@ |
503 | event.sender['nick'] = user |
504 | event.channel = u'$public' |
505 | event.public = True |
506 | - event.source = self.factory.name |
507 | return event |
508 | |
509 | def _state_event(self, user, action): |
510 | |
511 | === modified file 'ibid/source/irc.py' |
512 | --- ibid/source/irc.py 2009-12-30 22:01:38 +0000 |
513 | +++ ibid/source/irc.py 2009-12-30 22:21:13 +0000 |
514 | @@ -22,15 +22,23 @@ |
515 | |
516 | def connectionMade(self): |
517 | self.nickname = self.factory.nick.encode('utf-8') |
518 | + |
519 | irc.IRCClient.connectionMade(self) |
520 | + |
521 | self.factory.resetDelay() |
522 | self.factory.proto = self |
523 | self.auth_callbacks = {} |
524 | + self.mode_prefixes = '@+' |
525 | self._ping_deferred = reactor.callLater(self.factory.ping_interval, self._idle_ping) |
526 | self.factory.log.info(u"Connected") |
527 | |
528 | def connectionLost(self, reason): |
529 | self.factory.log.info(u"Disconnected (%s)", reason) |
530 | + |
531 | + event = Event(self.factory.name, u'source') |
532 | + event.status = u'disconnected' |
533 | + ibid.dispatcher.dispatch(event) |
534 | + |
535 | irc.IRCClient.connectionLost(self, reason) |
536 | |
537 | def _idle_ping(self): |
538 | @@ -73,27 +81,30 @@ |
539 | self.join(channel.encode('utf-8')) |
540 | self.factory.log.info(u"Signed on") |
541 | |
542 | + event = Event(self.factory.name, u'source') |
543 | + event.status = u'connected' |
544 | + ibid.dispatcher.dispatch(event) |
545 | + |
546 | def _create_event(self, type, user, channel): |
547 | nick = user.split('!', 1)[0] |
548 | event = Event(self.factory.name, type) |
549 | - event.sender['connection'] = unicode(user, 'utf-8', 'replace') |
550 | - event.sender['id'] = unicode(nick, 'utf-8', 'replace') |
551 | + event.sender['connection'] = user |
552 | + event.sender['id'] = nick |
553 | event.sender['nick'] = event.sender['id'] |
554 | - event.channel = unicode(channel, 'utf-8', 'replace') |
555 | + event.channel = channel |
556 | event.public = True |
557 | - event.source = self.factory.name |
558 | return event |
559 | |
560 | - def _state_event(self, user, channel, action, kicker=None, message=None): |
561 | + def _state_event(self, user, channel, action, kicker=None, message=None, othername=None): |
562 | event = self._create_event(u'state', user, channel) |
563 | event.state = action |
564 | if message: |
565 | - event.message = unicode(message, 'utf-8', 'replace') |
566 | + event.message = message |
567 | if kicker: |
568 | - event.kicker = unicode(kicker, 'utf-8', 'replace') |
569 | + event.kicker = kicker |
570 | self.factory.log.debug(u"%s has been kicked from %s by %s (%s)", event.sender['id'], event.channel, event.kicker, event.message) |
571 | - else: |
572 | - self.factory.log.debug(u"%s has %s %s", user, action, channel) |
573 | + elif othername: |
574 | + event.othername = othername |
575 | ibid.dispatcher.dispatch(event).addCallback(self.respond) |
576 | |
577 | def privmsg(self, user, channel, msg): |
578 | @@ -106,6 +117,9 @@ |
579 | self._message_event(u'action', user, channel, msg) |
580 | |
581 | def _message_event(self, msgtype, user, channel, msg): |
582 | + user = unicode(user, 'utf-8', 'replace') |
583 | + channel = unicode(channel, 'utf-8', 'replace') |
584 | + |
585 | event = self._create_event(msgtype, user, channel) |
586 | event.message = unicode(msg, 'utf-8', 'replace') |
587 | self.factory.log.debug(u"Received %s from %s in %s: %s", msgtype, event.sender['id'], event.channel, event.message) |
588 | @@ -120,16 +134,32 @@ |
589 | ibid.dispatcher.dispatch(event).addCallback(self.respond) |
590 | |
591 | def userJoined(self, user, channel): |
592 | + user = unicode(user, 'utf-8', 'replace') |
593 | + channel = unicode(channel, 'utf-8', 'replace') |
594 | self._state_event(user, channel, u'online') |
595 | |
596 | def userLeft(self, user, channel): |
597 | + user = unicode(user, 'utf-8', 'replace') |
598 | + channel = unicode(channel, 'utf-8', 'replace') |
599 | self._state_event(user, channel, u'offline') |
600 | |
601 | + def userRenamed(self, oldname, newname): |
602 | + oldname = unicode(oldname, 'utf-8', 'replace') |
603 | + newname = unicode(newname, 'utf-8', 'replace') |
604 | + self._state_event(oldname, None, u'offline', othername=newname) |
605 | + self._state_event(newname, None, u'online', othername=oldname) |
606 | + |
607 | def userQuit(self, user, channel): |
608 | # Channel contains the quit message |
609 | - self._state_event(user, '', u'offline', message=channel) |
610 | + user = unicode(user, 'utf-8', 'replace') |
611 | + channel = unicode(channel, 'utf-8', 'replace') |
612 | + self._state_event(user, None, u'offline', message=channel) |
613 | |
614 | def userKicked(self, kickee, channel, kicker, message): |
615 | + kickee = unicode(kickee, 'utf-8', 'replace') |
616 | + channel = unicode(channel, 'utf-8', 'replace') |
617 | + kicker = unicode(kicker, 'utf-8', 'replace') |
618 | + message = unicode(message, 'utf-8', 'replace') |
619 | self._state_event(kickee, channel, u'kicked', kicker, message) |
620 | |
621 | def respond(self, event): |
622 | @@ -149,6 +179,7 @@ |
623 | self.factory.log.debug(u"Set topic in %s to %s", target, message) |
624 | elif response.get('action', False): |
625 | # We can't use self.me() because it prepends a # onto channel names |
626 | + # See http://twistedmatrix.com/trac/ticket/3910 |
627 | self.ctcpMakeQuery(raw_target, [('ACTION', raw_message)]) |
628 | self.factory.log.debug(u"Sent action to %s: %s", target, message) |
629 | elif response.get('notice', False): |
630 | @@ -162,9 +193,21 @@ |
631 | self.factory.log.info(u"Joining %s", channel) |
632 | irc.IRCClient.join(self, channel.encode('utf-8')) |
633 | |
634 | - def part(self, channel): |
635 | + def joined(self, channel): |
636 | + event = Event(self.factory.name, u'source') |
637 | + event.channel = channel |
638 | + event.status = u'joined' |
639 | + ibid.dispatcher.dispatch(event) |
640 | + |
641 | + def leave(self, channel): |
642 | self.factory.log.info(u"Leaving %s", channel) |
643 | - irc.IRCClient.part(self, channel.encode('utf-8')) |
644 | + irc.IRCClient.leave(self, channel.encode('utf-8')) |
645 | + |
646 | + def left(self, channel): |
647 | + event = Event(self.factory.name, u'source') |
648 | + event.channel = channel |
649 | + event.status = u'left' |
650 | + ibid.dispatcher.dispatch(event) |
651 | |
652 | def authenticate(self, nick, callback): |
653 | self.sendLine('WHOIS %s' % nick.encode('utf-8')) |
654 | @@ -186,6 +229,28 @@ |
655 | elif command == "RPL_ENDOFWHOIS": |
656 | self.do_auth_callback(params[1], False) |
657 | |
658 | + def irc_RPL_BOUNCE(self, prefix, params): |
659 | + # Broken in IrcClient :/ |
660 | + # See http://twistedmatrix.com/trac/ticket/3285 |
661 | + if params[-1] in ('are available on this server', 'are supported by this server'): |
662 | + self.isupport(params[1:-1]) |
663 | + else: |
664 | + self.bounce(params[1]) |
665 | + |
666 | + def isupport(self, options): |
667 | + "Server supports message" |
668 | + for option in options: |
669 | + if option.startswith('PREFIX='): |
670 | + self.mode_prefixes = option.split(')', 1)[1] |
671 | + |
672 | + def irc_RPL_NAMREPLY(self, prefix, params): |
673 | + channel = params[2] |
674 | + for user in params[3].split(): |
675 | + if user[0] in self.mode_prefixes: |
676 | + user = user[1:] |
677 | + if user != self.nickname: |
678 | + self.userJoined(user, channel) |
679 | + |
680 | def ctcpQuery_VERSION(self, user, channel, data): |
681 | nick = user.split("!")[0] |
682 | self.ctcpMakeReply(nick, [('VERSION', 'Ibid %s' % (ibid_version() or '',))]) |
683 | @@ -244,8 +309,8 @@ |
684 | def join(self, channel): |
685 | return self.proto.join(channel) |
686 | |
687 | - def part(self, channel): |
688 | - return self.proto.part(channel) |
689 | + def leave(self, channel): |
690 | + return self.proto.leave(channel) |
691 | |
692 | def change_nick(self, nick): |
693 | return self.proto.setNick(nick.encode('utf-8')) |
694 | @@ -254,7 +319,9 @@ |
695 | return self.proto.send(response) |
696 | |
697 | def logging_name(self, identity): |
698 | - return identity.split('!')[0] |
699 | + if identity is None: |
700 | + return u'' |
701 | + return identity.split(u'!')[0] |
702 | |
703 | def url(self): |
704 | return u'irc://%s@%s:%s' % (self.nick, self.server, self.port) |
705 | |
706 | === modified file 'ibid/source/jabber.py' |
707 | --- ibid/source/jabber.py 2009-12-30 16:29:10 +0000 |
708 | +++ ibid/source/jabber.py 2009-12-30 22:21:13 +0000 |
709 | @@ -1,6 +1,6 @@ |
710 | import logging |
711 | |
712 | -from wokkel import client, xmppim |
713 | +from wokkel import client, xmppim, subprotocols |
714 | from twisted.internet import reactor, ssl |
715 | from twisted.words.protocols.jabber.jid import JID |
716 | from twisted.words.xish import domish |
717 | @@ -39,27 +39,48 @@ |
718 | for room in self.parent.rooms: |
719 | self.join(room) |
720 | |
721 | + event = Event(self.parent.name, u'source') |
722 | + event.status = u'connected' |
723 | + ibid.dispatcher.dispatch(event) |
724 | + |
725 | + def connectionLost(self, reason): |
726 | + self.parent.log.info(u"Disconnected (%s)", reason) |
727 | + |
728 | + event = Event(self.parent.name, u'source') |
729 | + event.status = u'disconnected' |
730 | + ibid.dispatcher.dispatch(event) |
731 | + |
732 | + subprotocols.XMPPHandler.connectionLost(self, reason) |
733 | + |
734 | + def _state_event(self, entity, state): |
735 | + event = Event(self.name, u'state') |
736 | + event.state = state |
737 | + if entity.userhost().lower() in self.rooms: |
738 | + nick = entity.full().split('/')[1] |
739 | + event.channel = entity.userhost() |
740 | + if nick == self.parent.nick: |
741 | + event.type = u'connection' |
742 | + event.status = state == u'online' and u'joined' or u'left' |
743 | + else: |
744 | + event.sender['connection'] = entity.full() |
745 | + event.sender['id'] = event.sender['connection'] |
746 | + event.sender['nick'] = nick |
747 | + event.public = True |
748 | + else: |
749 | + event.sender['connection'] = entity.full() |
750 | + event.sender['id'] = event.sender['connection'].split('/')[0] |
751 | + event.sender['nick'] = event.sender['connection'].split('@')[0] |
752 | + event.channel = entity.full() |
753 | + event.public = False |
754 | + ibid.dispatcher.dispatch(event).addCallback(self.respond) |
755 | + |
756 | def availableReceived(self, entity, show=None, statuses=None, priority=0): |
757 | - event = Event(self.name, u'state') |
758 | - event.sender['connection'] = entity.full() |
759 | - event.sender['id'] = event.sender['connection'].split('/')[0] |
760 | - event.sender['nick'] = event.sender['connection'].split('@')[0] |
761 | - event.state = show or u'online' |
762 | - event.public = False |
763 | - event.channel = entity.full() |
764 | - self.parent.log.debug(u"Received available presence from %s (%s)", event.sender['connection'], event.state) |
765 | - ibid.dispatcher.dispatch(event).addCallback(self.respond) |
766 | + self.parent.log.debug(u"Received available presence from %s (%s)", entity.full(), show) |
767 | + self._state_event(entity, u'online') |
768 | |
769 | def unavailableReceived(self, entity, statuses): |
770 | - event = Event(self.name, u'state') |
771 | - event.sender['connection'] = entity.full() |
772 | - event.sender['id'] = event.sender['connection'].split('/')[0] |
773 | - event.sender['nick'] = event.sender['connection'].split('@')[0] |
774 | - event.state = u'offline' |
775 | - event.public = False |
776 | - event.channel = entity.full() |
777 | - self.parent.log.debug(u"Received unavailable presence from %s", event.sender['connection']) |
778 | - ibid.dispatcher.dispatch(event).addCallback(self.respond) |
779 | + self.parent.log.debug(u"Received unavailable presence from %s", entity.full()) |
780 | + self._state_event(entity, u'offline') |
781 | |
782 | def subscribeReceived(self, entity): |
783 | response = xmppim.Presence(to=entity, type='subscribed') |
784 | @@ -121,18 +142,18 @@ |
785 | self.parent.log.debug(u"Sent %s message to %s: %s", message['type'], message['to'], message.body) |
786 | |
787 | def join(self, room): |
788 | + self.parent.log.info(u"Joining %s", room) |
789 | jid = JID('%s/%s' % (room, self.parent.nick)) |
790 | presence = xmppim.AvailablePresence(to=jid) |
791 | self.xmlstream.send(presence) |
792 | - self.rooms.append(room) |
793 | - self.parent.log.info(u"Joining %s", room) |
794 | + self.rooms.append(room.lower()) |
795 | |
796 | - def part(self, room): |
797 | + def leave(self, room): |
798 | + self.parent.log.info(u"Leaving %s", room) |
799 | jid = JID('%s/%s' % (room, self.parent.nick)) |
800 | presence = xmppim.UnavailablePresence(to=jid) |
801 | self.xmlstream.send(presence) |
802 | - self.rooms.remove(room) |
803 | - self.parent.log.info(u"Leaving %s", room) |
804 | + self.rooms.remove(room.lower()) |
805 | |
806 | class SourceFactory(client.DeferredClientFactory, IbidSourceFactory): |
807 | |
808 | @@ -182,8 +203,8 @@ |
809 | def join(self, room): |
810 | return self.proto.join(room) |
811 | |
812 | - def part(self, room): |
813 | - return self.proto.part(room) |
814 | + def leave(self, room): |
815 | + return self.proto.leave(room) |
816 | |
817 | def url(self): |
818 | return u'xmpp://%s' % (self.jid_str,) |
819 | |
820 | === modified file 'ibid/source/silc.py' |
821 | --- ibid/source/silc.py 2009-12-30 18:34:43 +0000 |
822 | +++ ibid/source/silc.py 2009-12-30 22:21:13 +0000 |
823 | @@ -20,7 +20,7 @@ |
824 | self.users = {} |
825 | |
826 | self.factory.join = self.join |
827 | - self.factory.part = self.part |
828 | + self.factory.leave = self.leave |
829 | self.factory.send = self.send |
830 | |
831 | def _create_event(self, type, user, channel): |
832 | @@ -34,7 +34,6 @@ |
833 | else: |
834 | event.channel = event.sender['connection'] |
835 | event.public = True |
836 | - event.source = self.factory.name |
837 | |
838 | self.users[event.sender['connection']] = user |
839 | self.users[event.sender['id']] = user |
840 | @@ -115,7 +114,7 @@ |
841 | self.command_call('JOIN %s' % channel) |
842 | return True |
843 | |
844 | - def part(self, channel): |
845 | + def leave(self, channel): |
846 | if channel not in self.channels: |
847 | return False |
848 | |
849 | @@ -147,6 +146,19 @@ |
850 | del self.users[self._to_hex(user.user_id)] |
851 | del self.users[self._to_hex(user.fingerprint)] |
852 | |
853 | + def notify_nick_change(self, user, old_nick, new_nick): |
854 | + event = self._create_event(u'state', user, None) |
855 | + event.state = u'offline' |
856 | + event.sender['nick'] = unicode(old_nick, 'utf-8', 'replace') |
857 | + event.othername = unicode(new_nick, 'utf-8', 'replace') |
858 | + ibid.dispatcher.dispatch(event).addCallback(self.respond) |
859 | + |
860 | + event = self._create_event(u'state', user, None) |
861 | + event.state = u'online' |
862 | + event.sender['nick'] = unicode(new_nick, 'utf-8', 'replace') |
863 | + event.othername = unicode(old_nick, 'utf-8', 'replace') |
864 | + ibid.dispatcher.dispatch(event).addCallback(self.respond) |
865 | + |
866 | def notify_kicked(self, user, message, kicker, channel): |
867 | self._state_event(user, channel, u'kicked', kicker, message) |
868 | |
869 | @@ -162,13 +174,25 @@ |
870 | for channel in self.factory.channels: |
871 | self.join(channel) |
872 | |
873 | + event = Event(self.factory.name, u'source') |
874 | + event.status = u'connected' |
875 | + ibid.dispatcher.dispatch(event) |
876 | + |
877 | def command_reply_join(self, channel, name, topic, hmac, x, y, users): |
878 | self.channels[name] = channel |
879 | + for user in users: |
880 | + self._state_event(user, channel, u'online') |
881 | |
882 | def disconnect(self): |
883 | self.command_call('QUIT') |
884 | |
885 | def disconnected(self, message): |
886 | + self.factory.log.info(u"Disconnected (%s)", reason) |
887 | + |
888 | + event = Event(self.factory.name, u'source') |
889 | + event.status = u'disconnected' |
890 | + ibid.dispatcher.dispatch(event) |
891 | + |
892 | self.factory.s.stopService() |
893 | self.channels.clear() |
894 | self.users.clear() |
OK I can't reproduce the problem that was holding back this proposal.