Adding ability for a non-chair to end a meeting after time

This is particularly useful if the chair forgot to end the meeting
or has been net split out and the nick is no longer useable.

Change-Id: Id5c0c11ec94898f3a79ac253a9d804baec79a85d
This commit is contained in:
Bob Ball 2013-07-12 14:11:35 +01:00
parent b06edb2517
commit 5f8bdbeef1
3 changed files with 51 additions and 22 deletions

View File

@ -92,11 +92,13 @@ class MeetBot(callbacks.Plugin):
# Start meeting if we are requested
if payload[:13] == '#startmeeting':
if M is not None:
irc.error("Can't start another meeting, one is in progress.")
irc.error("Can't start another meeting, one is in progress."
" Use #endmeeting first.")
return
name = payload[13:].strip()
if not name:
irc.error("A meeting name is required, e.g., '#startmeeting Marketing Committee'")
irc.error("A meeting name is required, e.g., "
"'#startmeeting Marketing Committee'")
return
# This callback is used to send data to the channel:
def _setTopic(x):

View File

@ -88,11 +88,12 @@ class Config(object):
# you have to use doubled percent signs. Also, it gets split by
# '\n' and each part between newlines get said in a separate IRC
# message.
startMeetingMessage = ("Meeting started %(starttime)s %(timeZone)s. "
"The chair is %(chair)s. Information about MeetBot at "
"%(MeetBotInfoURL)s.\n"
"Useful Commands: #action #agreed #help #info #idea #link "
"#topic #startvote.")
startMeetingMessage = ("Meeting started %(starttime)s %(timeZone)s "
"and is due to finish in %(length)d minutes. "
"The chair is %(chair)s. Information about MeetBot at "
"%(MeetBotInfoURL)s.\n"
"Useful Commands: #action #agreed #help #info #idea #link "
"#topic #startvote.")
endMeetingMessage = ("Meeting ended %(endtime)s %(timeZone)s. "
"Information about MeetBot at %(MeetBotInfoURL)s . "
"(v %(__version__)s)\n"
@ -309,6 +310,7 @@ class MeetingCommands(object):
def do_startmeeting(self, nick, time_, line, **kwargs):
"""Begin a meeting."""
self.starttime = time_
self.expectedend = time.mktime(time_) + self.length * 60
repl = self.replacements()
message = self.config.startMeetingMessage%repl
for messageline in message.split('\n'):
@ -318,7 +320,8 @@ class MeetingCommands(object):
self.do_meetingname(nick=nick, line=line, time_=time_, **kwargs)
def do_endmeeting(self, nick, time_, **kwargs):
"""End the meeting."""
if not self.isChair(nick): return
# Chairs can end the meeting early - anyone can end it after the meeting length
if (not self.isChair(nick)) and (self.expectedend > time.mktime(time_)): return
if self.oldtopic:
self.topic(self.oldtopic)
self.endtime = time_
@ -541,7 +544,7 @@ class Meeting(MeetingCommands, object):
filename=None, writeRawLog=False,
setTopic=None, sendReply=None, getRegistryValue=None,
safeMode=False, channelNicks=None,
extraConfig={}, network='nonetwork'):
extraConfig={}, network='nonetwork', length=60):
if getRegistryValue is not None:
self._registryValue = getRegistryValue
if sendReply is not None:
@ -551,6 +554,7 @@ class Meeting(MeetingCommands, object):
self.owner = owner
self.channel = channel
self.network = network
self.length = length
self.currenttopic = ""
self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode,
extraConfig=extraConfig)
@ -603,7 +607,7 @@ class Meeting(MeetingCommands, object):
return (nick == self.owner or nick in self.chairs)
def save(self, **kwargs):
return self.config.save(**kwargs)
# Primary enttry point for new lines in the log:
# Primary entry point for new lines in the log:
def addline(self, nick, line, time_=None):
"""This is the way to add lines to the Meeting object.
"""
@ -666,6 +670,7 @@ class Meeting(MeetingCommands, object):
repl['starttime'] = time.asctime(self.starttime)
if getattr(self, "endtime", None) is not None:
repl['endtime'] = time.asctime(self.endtime)
repl['length'] = self.length
repl['__version__'] = __version__
repl['chair'] = self.owner
repl['urlBasename'] = self.config.filename(url=True)
@ -677,9 +682,11 @@ class Meeting(MeetingCommands, object):
def parse_time(time_):
try: return time.strptime(time_, "%H:%M:%S")
# Need a date > 1970 to convert to a timestamp for comparisons.
# Without a year here, Python assumes 1900.
try: return time.strptime("01/01/2000 "+time_, "%m/%d/%Y %H:%M:%S")
except ValueError: pass
try: return time.strptime(time_, "%H:%M")
try: return time.strptime("01/01/2000 "+time_, "%m/%d/%Y %H:%M")
except ValueError: pass
logline_re = re.compile(r'\[?([0-9: ]*)\]? *<[@+]?([^>]+)> *(.*)')
loglineAction_re = re.compile(r'\[?([0-9: ]*)\]? *\* *([^ ]+) *(.*)')
@ -688,12 +695,17 @@ loglineAction_re = re.compile(r'\[?([0-9: ]*)\]? *\* *([^ ]+) *(.*)')
def process_meeting(contents, channel, filename,
extraConfig = {},
dontSave=False,
safeMode=True):
M = Meeting(channel=channel, owner=None,
filename=filename, writeRawLog=False, safeMode=safeMode,
extraConfig=extraConfig)
if dontSave:
M.config.dontSave = True
safeMode=True,
existingMeeting=None):
# Allow tests part way through the meeting
if existingMeeting:
M = existingMeeting
else:
M = Meeting(channel=channel, owner=None,
filename=filename, writeRawLog=False, safeMode=safeMode,
extraConfig=extraConfig)
if dontSave:
M.config.dontSave = True
# process all lines
for line in contents.split('\n'):
# match regular spoken lines:

View File

@ -15,16 +15,17 @@ import ircmeeting.writers as writers
running_tests = True
def process_meeting(contents, extraConfig={}, dontSave=True,
filename='/dev/null'):
filename='/dev/null', existingMeeting=None):
"""Take a test script, return Meeting object of that meeting.
To access the results (a dict keyed by extensions), use M.save(),
with M being the return of this function.
"""
return meeting.process_meeting(contents=contents,
channel="#none", filename=filename,
dontSave=dontSave, safeMode=False,
extraConfig=extraConfig)
channel="#none", filename=filename,
dontSave=dontSave, safeMode=False,
extraConfig=extraConfig,
existingMeeting=existingMeeting)
class MeetBotTest(unittest.TestCase):
@ -248,6 +249,20 @@ class MeetBotTest(unittest.TestCase):
assert re.search(r'href.*mailto://a@mail.com.*suffix',
results), "URL missing 5"
def test_lateEnd(self):
"""Test that anyone can end a meeting late
"""
script = """
20:13:50 <x> #startmeeting
20:43:57 <y> #endmeeting
"""
M = process_meeting(script)
assert M._meetingIsOver == False, "Early call from non-chair should fail"
process_meeting("22:13:52 <z> #endmeeting", existingMeeting=M)
assert M._meetingIsOver, "Late call from non-chair should succeed"
results = M.save()['.txt']
assert 'Meeting ended at 22:13:52' in results
def t_css(self):
"""Runs all CSS-related tests.
"""