Merge lp:~stefanor/ibid/meetings into lp:~ibid-core/ibid/old-trunk-pack-0.92
- meetings
- Merge into old-trunk-pack-0.92
Proposed by
Stefano Rivera
Status: | Merged |
---|---|
Approved by: | Michael Gorven |
Approved revision: | 776 |
Merged at revision: | 778 |
Proposed branch: | lp:~stefanor/ibid/meetings |
Merge into: | lp:~ibid-core/ibid/old-trunk-pack-0.92 |
Diff against target: |
572 lines (+530/-2) 4 files modified
ibid/plugins/meetings.py (+427/-0) ibid/templates/meetings/minutes.html (+59/-0) ibid/templates/meetings/minutes.txt (+34/-0) scripts/ibid-plugin (+10/-2) |
To merge this branch: | bzr merge lp:~stefanor/ibid/meetings |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Gorven | Approve | ||
Jonathan Hitchcock | Approve | ||
Review via email: mp+13953@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Michael Gorven (mgorven) wrote : | # |
+ permission = u'chairmeeting'
+ permission = u'meetingchair'
Why are those different?
+ time = IntOption(
Might be useful to specify the duration in the start poll command.
lp:~stefanor/ibid/meetings
updated
- 775. By Stefano Rivera
-
Consistent permission names
Revision history for this message
Stefano Rivera (stefanor) wrote : | # |
> Why are those different?
Fixed in r775
> Might be useful to specify the duration in the start poll command.
Fixed in r776
lp:~stefanor/ibid/meetings
updated
- 776. By Stefano Rivera
-
Specify poll end when starting poll
Revision history for this message
Michael Gorven (mgorven) wrote : | # |
review approve
status approved
review:
Approve
lp:~stefanor/ibid/meetings
updated
- 777. By Stefano Rivera
-
1-index polls
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'ibid/plugins/meetings.py' |
2 | --- ibid/plugins/meetings.py 1970-01-01 00:00:00 +0000 |
3 | +++ ibid/plugins/meetings.py 2009-12-02 11:45:24 +0000 |
4 | @@ -0,0 +1,427 @@ |
5 | +from datetime import datetime, timedelta |
6 | +import logging |
7 | +from os import makedirs |
8 | +from os.path import dirname, expanduser, join |
9 | +import re |
10 | +from urllib import quote |
11 | +from xmlrpclib import ServerProxy |
12 | + |
13 | +from dateutil.parser import parse |
14 | +from dateutil.tz import tzlocal, tzutc |
15 | +from jinja import Environment, PackageLoader |
16 | + |
17 | +import ibid |
18 | +from ibid.compat import json |
19 | +from ibid.config import BoolOption, IntOption, Option |
20 | +from ibid.plugins import Processor, match, authorise |
21 | +from ibid.utils import format_date, plural |
22 | + |
23 | +help = {} |
24 | +log = logging.getLogger('plugins.meetings') |
25 | + |
26 | +templates = Environment(loader=PackageLoader('ibid', 'templates')) |
27 | +meetings = {} |
28 | + |
29 | +help['meeting'] = u'Take minutes of an IRC Meeting' |
30 | +class Meeting(Processor): |
31 | + u""" |
32 | + (start | end) meeting [about <title>] |
33 | + I am <True Name> |
34 | + topic <topic> |
35 | + (agreed | idea | accepted | rejected) <statement> |
36 | + minutes so far |
37 | + meeting title is <title> |
38 | + """ |
39 | + feature = 'meeting' |
40 | + permission = u'chairmeeting' |
41 | + |
42 | + formats = Option('formats', u'Formats to log to. ' |
43 | + u'Requires templates of the name meeting/minutes.format', |
44 | + ('json', 'txt', 'html')) |
45 | + logfile = Option('logfile', u'File name for meeting logs. ' |
46 | + u'Can contain substitutions: source, channel, date, format', |
47 | + 'logs/meetings/%(source)s-%(channel)s-%(date)s.%(format)s') |
48 | + logurl = Option('logurl', u'Public URL for meeting logs. ' |
49 | + u'Can contain substitutions: source, channel, date, format ' |
50 | + u'If unset, will use a pastebin.', |
51 | + None) |
52 | + date_format = Option('date_format', 'Format to substitute %(date)s with', |
53 | + '%Y-%m-%d-%H-%M-%S') |
54 | + |
55 | + @authorise |
56 | + @match(r'^start\s+meeting(?:\s+about\s+(.+))?$') |
57 | + def start_meeting(self, event, title): |
58 | + if not event.public: |
59 | + event.addresponse(u'Sorry, must be done in public') |
60 | + return |
61 | + if (event.source, event.channel) in meetings: |
62 | + event.addresponse(u'Sorry, meeting in progress.') |
63 | + return |
64 | + meeting = { |
65 | + 'starttime': event.time, |
66 | + 'convenor': event.sender['nick'], |
67 | + 'source': event.source, |
68 | + 'channel': ibid.sources[event.source] |
69 | + .logging_name(event.channel), |
70 | + 'title': title, |
71 | + 'attendees': {}, |
72 | + 'minutes': [{ |
73 | + 'time': event.time, |
74 | + 'type': 'started', |
75 | + 'subject': None, |
76 | + 'nick': event.sender['nick'], |
77 | + }], |
78 | + 'log': [], |
79 | + } |
80 | + meetings[(event.source, event.channel)] = meeting |
81 | + |
82 | + event.addresponse(u'gets out his memo-pad and cracks his knuckles', |
83 | + action=True) |
84 | + |
85 | + @match(r'^i\s+am\s+(.+)$') |
86 | + def ident(self, event, name): |
87 | + if not event.public or (event.source, event.channel) not in meetings: |
88 | + return |
89 | + |
90 | + meeting = meetings[(event.source, event.channel)] |
91 | + meeting['attendees'][event.sender['nick']] = name |
92 | + |
93 | + event.addresponse(True) |
94 | + |
95 | + @authorise |
96 | + @match(r'^(topic|idea|agreed|accepted|rejected)\s+(.+)$') |
97 | + def identify(self, event, action, subject): |
98 | + if not event.public or (event.source, event.channel) not in meetings: |
99 | + return |
100 | + |
101 | + action = action.lower() |
102 | + |
103 | + meeting = meetings[(event.source, event.channel)] |
104 | + meeting['minutes'].append({ |
105 | + 'time': event.time, |
106 | + 'type': action, |
107 | + 'subject': subject, |
108 | + 'nick': event.sender['nick'], |
109 | + }) |
110 | + |
111 | + if action == 'topic': |
112 | + message = u'Current Topic: %s' |
113 | + elif action == 'idea': |
114 | + message = u'Idea recorded: %s' |
115 | + elif action == 'agreed': |
116 | + message = u'Agreed: %s' |
117 | + elif action == 'accepted': |
118 | + message = u'Accepted: %s' |
119 | + elif action == 'rejected': |
120 | + message = u'Rejected: %s' |
121 | + event.addresponse(message, subject, address=False) |
122 | + |
123 | + @authorise |
124 | + @match(r'^meeting\s+title\s+is\s+(.+)$') |
125 | + def set_title(self, event, title): |
126 | + if not event.public: |
127 | + event.addresponse(u'Sorry, must be done in public') |
128 | + return |
129 | + if (event.source, event.channel) not in meetings: |
130 | + event.addresponse(u'Sorry, no meeting in progress.') |
131 | + return |
132 | + meeting = meetings[(event.source, event.channel)] |
133 | + meeting['title'] = title |
134 | + event.addresponse(True) |
135 | + |
136 | + @match(r'^minutes(?:\s+(?:so\s+far|please))?$') |
137 | + def write_minutes(self, event): |
138 | + if not event.public: |
139 | + event.addresponse(u'Sorry, must be done in public') |
140 | + return |
141 | + if (event.source, event.channel) not in meetings: |
142 | + event.addresponse(u'Sorry, no meeting in progress.') |
143 | + return |
144 | + meeting = meetings[(event.source, event.channel)] |
145 | + meeting['attendees'].update((e['nick'], None) for e in meeting['log'] |
146 | + if e['nick'] not in meeting['attendees'] |
147 | + and e['nick'] != ibid.config['botname']) |
148 | + |
149 | + render_to = set() |
150 | + if self.logurl is None: |
151 | + render_to.add('txt') |
152 | + render_to.update(self.formats) |
153 | + minutes = {} |
154 | + for format in render_to: |
155 | + if format == 'json': |
156 | + class DTJSONEncoder(json.JSONEncoder): |
157 | + def default(self, o): |
158 | + if isinstance(o, datetime): |
159 | + return o.strftime('%Y-%m-%dT%H:%M:%SZ') |
160 | + return json.JSONEncoder.default(self, o) |
161 | + minutes[format] = json.dumps(meeting, cls=DTJSONEncoder, |
162 | + indent=2) |
163 | + else: |
164 | + template = templates.get_template('meetings/minutes.' + format) |
165 | + minutes[format] = template.render(meeting=meeting) \ |
166 | + .encode('utf-8') |
167 | + |
168 | + filename = self.logfile % { |
169 | + 'source': event.source.replace('/', '-'), |
170 | + 'channel': meeting['channel'].replace('/', '-'), |
171 | + 'date': meeting['starttime'].strftime(self.date_format), |
172 | + 'format': format, |
173 | + } |
174 | + filename = join(ibid.options['base'], expanduser(filename)) |
175 | + try: |
176 | + makedirs(dirname(filename)) |
177 | + except OSError, e: |
178 | + if e.errno != 17: |
179 | + raise e |
180 | + f = open(filename, 'w+') |
181 | + f.write(minutes[format]) |
182 | + f.close() |
183 | + |
184 | + if self.logurl is None: |
185 | + pastebin = ServerProxy('http://paste.pocoo.org/xmlrpc/', |
186 | + allow_none=True) |
187 | + id = pastebin.pastes.newPaste(None, minutes['txt'], None, '', |
188 | + 'text/plain', False) |
189 | + |
190 | + url = u'http://paste.pocoo.org/show/' + id |
191 | + elif u'%(format)s' not in self.logurl: |
192 | + # Content Negotiation |
193 | + url = self.logurl % { |
194 | + 'source': quote(event.source.replace('/', '-')), |
195 | + 'channel': quote(meeting['channel'].replace('/', '-')), |
196 | + 'date': quote(meeting['starttime'].strftime(self.date_format)), |
197 | + } |
198 | + else: |
199 | + url = u' :: '.join(u'%s: %s' % (format, self.logurl % { |
200 | + 'source': quote(event.source.replace('/', '-')), |
201 | + 'channel': quote(meeting['channel'].replace('/', '-')), |
202 | + 'date': quote(meeting['starttime'].strftime(self.date_format)), |
203 | + 'format': quote(format), |
204 | + }) for format in self.formats) |
205 | + |
206 | + event.addresponse(u'Minutes available at %s', url, address=False) |
207 | + |
208 | + @authorise |
209 | + @match(r'^end\s+meeting$') |
210 | + def end_meeting(self, event): |
211 | + if not event.public: |
212 | + event.addresponse(u'Sorry, must be done in public') |
213 | + return |
214 | + if (event.source, event.channel) not in meetings: |
215 | + event.addresponse(u'Sorry, no meeting in progress.') |
216 | + return |
217 | + meeting = meetings[(event.source, event.channel)] |
218 | + |
219 | + meeting['endtime'] = event.time |
220 | + meeting['log'].append({ |
221 | + 'nick': event.sender['nick'], |
222 | + 'type': event.type, |
223 | + 'message': event.message['raw'], |
224 | + 'time': event.time, |
225 | + }) |
226 | + meeting['minutes'].append({ |
227 | + 'time': event.time, |
228 | + 'type': 'ended', |
229 | + 'subject': None, |
230 | + 'nick': event.sender['nick'], |
231 | + }) |
232 | + |
233 | + event.addresponse(u'Meeting Ended', address=False) |
234 | + self.write_minutes(event) |
235 | + del meetings[(event.source, event.channel)] |
236 | + |
237 | +class MeetingLogger(Processor): |
238 | + addressed = False |
239 | + processed = True |
240 | + priority = 1900 |
241 | + feature = 'meeting' |
242 | + |
243 | + def process(self, event): |
244 | + if 'channel' in event and 'source' in event \ |
245 | + and (event.source, event.channel) in meetings: |
246 | + meeting = meetings[(event.source, event.channel)] |
247 | + message = event.message |
248 | + if isinstance(message, dict): |
249 | + message = message['raw'] |
250 | + meeting['log'].append({ |
251 | + 'nick': event.sender['nick'], |
252 | + 'type': event.type, |
253 | + 'message': message, |
254 | + 'time': event.time, |
255 | + }) |
256 | + for response in event.responses: |
257 | + type = 'message' |
258 | + if response.get('action', False): |
259 | + type = 'action' |
260 | + elif response.get('notice', False): |
261 | + type = 'notice' |
262 | + |
263 | + meeting['log'].append({ |
264 | + 'nick': ibid.config['botname'], |
265 | + 'type': type, |
266 | + 'message': response['reply'], |
267 | + 'time': event.time, |
268 | + }) |
269 | + |
270 | +help['poll'] = u'Does a quick poll of channel members' |
271 | +class Poll(Processor): |
272 | + u""" |
273 | + [secret] poll on <topic> [until <time>] vote <option> [or <option>]... |
274 | + vote (<id> | <option>) [on <topic>] |
275 | + end poll |
276 | + """ |
277 | + feature = 'poll' |
278 | + permission = u'chairmeeting' |
279 | + |
280 | + polls = {} |
281 | + |
282 | + date_utc = BoolOption('date_utc', u'Interpret poll end times as UTC', False) |
283 | + poll_time = IntOption('poll_time', u'Default poll length', 5 * 60) |
284 | + |
285 | + @authorise |
286 | + @match(r'^(secret\s+)?(?:poll|ballot)\s+on\s+(.+?)\s+' |
287 | + r'(?:until\s+(.+?)\s+)?vote\s+(.+\sor\s.+)$') |
288 | + def start_poll(self, event, secret, topic, end, options): |
289 | + if not event.public: |
290 | + event.addresponse(u'Sorry, must be done in public') |
291 | + return |
292 | + |
293 | + if (event.source, event.channel) in self.polls: |
294 | + event.addresponse(u'Sorry, poll on %s in progress.', |
295 | + self.polls[(event.source, event.channel)].topic) |
296 | + return |
297 | + |
298 | + class PollContainer(object): |
299 | + pass |
300 | + poll = PollContainer() |
301 | + self.polls[(event.source, event.channel)] = poll |
302 | + |
303 | + poll.secret = secret is not None |
304 | + if end is None: |
305 | + poll.end = event.time + timedelta(seconds=self.poll_time) |
306 | + else: |
307 | + poll.end = parse(end) |
308 | + if poll.end.tzinfo is None and not self.date_utc: |
309 | + poll.end = poll.end.replace(tzinfo=tzlocal()) |
310 | + if poll.end.tzinfo is not None: |
311 | + poll.end = poll.end.astimezone(tzutc()).replace(tzinfo=None) |
312 | + if poll.end < event.time: |
313 | + event.addresponse(u"I can't end a poll in the past") |
314 | + return |
315 | + |
316 | + poll.topic = topic |
317 | + poll.options = re.split(r'\s+or\s+', options) |
318 | + poll.lower_options = [o.lower() for o in poll.options] |
319 | + poll.votes = {} |
320 | + |
321 | + event.addresponse( |
322 | + u'You heard that, voting has begun. ' |
323 | + u'The polls close at %(end)s. ' |
324 | + u'%(private)s' |
325 | + u'The Options Are:', { |
326 | + 'private': poll.secret |
327 | + and u'You may vote in public or private. ' |
328 | + or u'', |
329 | + 'end': format_date(poll.end), |
330 | + }, address=False) |
331 | + |
332 | + for i, o in enumerate(poll.options): |
333 | + event.addresponse(u'%(id)i: %(option)s', { |
334 | + 'id': i + 1, |
335 | + 'option': o, |
336 | + }, address=False) |
337 | + |
338 | + delay = poll.end - event.time |
339 | + poll.delayed_call = ibid.dispatcher.call_later( |
340 | + delay.days * 86400 + delay.seconds, |
341 | + self.end_poll, event) |
342 | + |
343 | + def locate_poll(self, event, selection, topic): |
344 | + "Attempt to find which poll the user is voting in" |
345 | + if event.public: |
346 | + if (event.source, event.channel) in self.polls: |
347 | + return self.polls[(event.source, event.channel)] |
348 | + else: |
349 | + if topic: |
350 | + polls = [p for p in self.polls.iteritems() |
351 | + if p.topic.lower() == topic.lower()] |
352 | + if len(polls) == 1: |
353 | + return polls[0] |
354 | + polls = [self.polls[p] for p in self.polls.iterkeys() |
355 | + if p[0] == event.source] |
356 | + if len(polls) == 1: |
357 | + return polls[0] |
358 | + elif len(polls) > 1: |
359 | + if not selection.isdigit(): |
360 | + possibles = [p for p in polls |
361 | + if selection.lower() in p.lower_options] |
362 | + if len(possibles) == 1: |
363 | + return possibles[0] |
364 | + event.addresponse(u'Sorry, I have more than one poll open. ' |
365 | + u'Please say "vote %s on <topic>"', selection) |
366 | + return |
367 | + event.addresponse(u'Sorry, no poll in progress') |
368 | + |
369 | + @match(r'^vote\s+(?:for\s+)?(.+?)(?:\s+on\s+(.+))?$') |
370 | + def vote(self, event, selection, topic): |
371 | + poll = self.locate_poll(event, selection, topic) |
372 | + log.debug(u'Poll: %s', repr(poll)) |
373 | + if poll is None: |
374 | + return |
375 | + |
376 | + if selection.isdigit() and int(selection) > 0 \ |
377 | + and int(selection) <= len(poll.options): |
378 | + selection = int(selection) - 1 |
379 | + else: |
380 | + try: |
381 | + selection = poll.lower_options.index(selection) |
382 | + except ValueError: |
383 | + event.addresponse( |
384 | + u"Sorry, I don't know of such an option for %s", |
385 | + poll.topic) |
386 | + return |
387 | + poll.votes[event.identity] = selection |
388 | + if not event.public: |
389 | + event.addresponse( |
390 | + u'Your vote on %(topic)s has been registered as %(option)s', { |
391 | + 'topic': poll.topic, |
392 | + 'option': poll.options[selection], |
393 | + }) |
394 | + else: |
395 | + event.processed = True |
396 | + |
397 | + @match('^end\s+poll$') |
398 | + @authorise |
399 | + def end_poll(self, event): |
400 | + if not event.public: |
401 | + event.addresponse(u'Sorry, must be done in public') |
402 | + return |
403 | + |
404 | + if (event.source, event.channel) not in self.polls: |
405 | + event.addresponse(u'Sorry, no poll in progress.') |
406 | + return |
407 | + |
408 | + poll = self.polls.pop((event.source, event.channel)) |
409 | + if poll.delayed_call.active(): |
410 | + poll.delayed_call.cancel() |
411 | + |
412 | + votes = [[poll.options[i], 0] |
413 | + for i in range(len(poll.options))] |
414 | + for vote in poll.votes.itervalues(): |
415 | + votes[vote][1] += 1 |
416 | + votes.sort(reverse=True, key=lambda x: x[1]) |
417 | + event.addresponse(u'The polls are closed. Totals:', address=False) |
418 | + |
419 | + position = (1, votes[0][1]) |
420 | + for o, v in votes: |
421 | + if v < position[1]: |
422 | + position = (position[0] + 1, v) |
423 | + event.addresponse( |
424 | + u'%(position)i: %(option)s - %(votes)i %(word)s', { |
425 | + 'position': position[0], |
426 | + 'option': o, |
427 | + 'votes': v, |
428 | + 'word': plural(v, u'vote', u'votes'), |
429 | + }, address=False) |
430 | + |
431 | +# vi: set et sta sw=4 ts=4: |
432 | |
433 | === added directory 'ibid/templates/meetings' |
434 | === added file 'ibid/templates/meetings/minutes.html' |
435 | --- ibid/templates/meetings/minutes.html 1970-01-01 00:00:00 +0000 |
436 | +++ ibid/templates/meetings/minutes.html 2009-12-02 11:45:24 +0000 |
437 | @@ -0,0 +1,59 @@ |
438 | +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
439 | +<html> |
440 | +<head> |
441 | + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> |
442 | + <title>Minutes: {{ meeting.title|e|default("Untitled") }}</title> |
443 | +</head> |
444 | +<body> |
445 | + <h1>Meeting about {{ meeting.title|e|default("something or the other") }}</h1> |
446 | + <div id="meetingmeta"> |
447 | + Convened at {{ meeting.starttime }} by {{ meeting.convenor|e }} |
448 | + in {{ meeting.channel|e }} on {{ meeting.source|e }} |
449 | + </div> |
450 | + |
451 | + <h2>Minutes</h2> |
452 | + <div id="minutes"> |
453 | +{%- for event in meeting.minutes %} |
454 | + <div class="line {{ event.type }}"> |
455 | + <span class="time">[{{ event.time.strftime('%H:%M:%S') }}]</span> |
456 | + <span class="type">{{ event.type|e|upper }}{{ ':' if event.subject else '' }}</span> |
457 | +{%- if event.subject %} |
458 | + <span class="subject">{{ event.subject|e }}</span> |
459 | +{%- endif %} |
460 | + <span class="nick">({{ event.nick|e }})</span> |
461 | + </div> |
462 | +{%- endfor %} |
463 | + </div> |
464 | + |
465 | + <h2>Present</h2> |
466 | + <ul id="present"> |
467 | +{%- for nick, name in meeting.attendees.iteritems() %} |
468 | + <li> |
469 | +{%- if name %} |
470 | + <span class="name">{{ name|e }}</span> |
471 | + <span class="nick hasname">({{ nick|e }})</span> |
472 | +{%- else %} |
473 | + <span class="nick noname">{{ nick|e }}</span> |
474 | +{%- endif %} |
475 | + </li> |
476 | +{%- endfor %} |
477 | + </ul> |
478 | + |
479 | + <h2>Raw Log</h2> |
480 | + <div id="log"> |
481 | +{%- for event in meeting.log %} |
482 | + <div class="line {{ event.type }}"> |
483 | + <span class="time">[{{ event.time.strftime('%H:%M:%S') }}]</span> |
484 | +{%- if event.type == 'message' %} |
485 | + <span class="nick"><{{ event.nick|e }}></span> |
486 | +{%- elif event.type == 'action' %} |
487 | + <span class="nick">* {{ event.nick|e }}</span> |
488 | +{%- elif event.type == 'notice' %} |
489 | + <span class="nick">- {{ event.nick|e }}</span> |
490 | +{%- endif %} |
491 | + <span class="message">{{ event.message|e }}</span> |
492 | + </div> |
493 | +{%- endfor %} |
494 | + </div> |
495 | +</body> |
496 | +</html> |
497 | |
498 | === added file 'ibid/templates/meetings/minutes.txt' |
499 | --- ibid/templates/meetings/minutes.txt 1970-01-01 00:00:00 +0000 |
500 | +++ ibid/templates/meetings/minutes.txt 2009-12-02 11:45:24 +0000 |
501 | @@ -0,0 +1,34 @@ |
502 | +Minutes from Meeting about {{ meeting.title|default("something or the other") }} |
503 | +Convened at {{ meeting.starttime }} by {{ meeting.convenor }} |
504 | +in {{ meeting.channel }} on {{ meeting.source }} |
505 | + |
506 | +Minutes |
507 | +======= |
508 | + |
509 | +{% for event in meeting.minutes -%} |
510 | +[{{ event.time.strftime('%H:%M:%S') }}] {{ event.type | upper }} |
511 | +{{- ': ' + event.subject if event.subject else '' }} ({{ event.nick }}) |
512 | +{% endfor %} |
513 | +Present |
514 | +======= |
515 | + |
516 | +{% for nick, name in meeting.attendees.iteritems() -%} |
517 | +{%- if name -%} |
518 | +* {{ name }} ({{ nick }}) |
519 | +{%- else -%} |
520 | +* {{ nick }} |
521 | +{%- endif %} |
522 | +{% endfor %} |
523 | +Raw Log |
524 | +======= |
525 | + |
526 | +{% for event in meeting.log -%} |
527 | +[{{ event.time.strftime('%H:%M:%S') }}] {# Preserve the space after the timestamp #} |
528 | +{%- if event.type == 'message' -%} |
529 | +<{{ event.nick }}> {{ event.message }} |
530 | +{%- elif event.type == 'action' -%} |
531 | +* {{ event.nick }} {{ event.message }} |
532 | +{%- elif event.type == 'notice' -%} |
533 | +- {{ event.nick }} {{ event.message }} |
534 | +{%- endif %} |
535 | +{% endfor %} |
536 | |
537 | === modified file 'scripts/ibid-plugin' |
538 | --- scripts/ibid-plugin 2009-10-26 11:08:37 +0000 |
539 | +++ scripts/ibid-plugin 2009-12-02 11:45:24 +0000 |
540 | @@ -26,6 +26,8 @@ |
541 | help="Only load the specified plugins, not the common base plugins") |
542 | parser.add_option("-c", "--configured", dest="load_configured", action="store_true", |
543 | help="Load all all configured plugins") |
544 | +parser.add_option("-p", "--public", dest="public", action="store_true", default=False, |
545 | + help="Make testchan public, it's private by default") |
546 | |
547 | (options, args) = parser.parse_args() |
548 | |
549 | @@ -37,7 +39,13 @@ |
550 | def auth_responses(event, permission): |
551 | return True |
552 | |
553 | -ibid.plugins.auth_responses = auth_responses |
554 | +class FakeAuth(object): |
555 | + def authorise(self, event, permission): |
556 | + return True |
557 | + |
558 | +ibid.auth = FakeAuth() |
559 | + |
560 | +#ibid.plugins.auth_responses = auth_responses |
561 | ibid.options = {'base': '.'} |
562 | |
563 | logging.basicConfig(level=logging.DEBUG) |
564 | @@ -106,7 +114,7 @@ |
565 | event.identity = identity_id |
566 | event.account = None |
567 | event.addressed = True |
568 | - event.public = False |
569 | + event.public = options.public |
570 | event.channel = u"testchan" |
571 | |
572 | try: |
It'll do for now.