aboutsummaryrefslogtreecommitdiff
path: root/bot
diff options
context:
space:
mode:
Diffstat (limited to 'bot')
-rw-r--r--bot/ircmeeting/agenda.py91
-rw-r--r--bot/ircmeeting/meeting.py26
l---------bot/tests/MeetBot1
l---------bot/tests/ircmeeting1
-rw-r--r--bot/tests/run_test.py71
-rw-r--r--bot/tests/test-script-1.html256
-rw-r--r--bot/tests/test-script-1.log.html91
-rw-r--r--bot/tests/test-script-1.txt98
8 files changed, 633 insertions, 2 deletions
diff --git a/bot/ircmeeting/agenda.py b/bot/ircmeeting/agenda.py
new file mode 100644
index 0000000..7ee9beb
--- /dev/null
+++ b/bot/ircmeeting/agenda.py
@@ -0,0 +1,91 @@
+import json
+import urllib
+
+class Agenda(object):
+
+ # Messages
+ empty_agenda_msg = "Agenda is empty so I can't help you manage meeting (and voting)."
+ current_item_msg = "Current agenda item is {}."
+ voting_already_open_msg = "Voting is already open. You can end it with #endvote."
+ voting_open_msg = "Voting started. Your choices are: {} Vote #vote <option number>.\n End voting with #endvote."
+ voting_close_msg = "Voting is closed."
+ voting_already_closed_msg = "Voting is already closed. You can start it with #startvote."
+ voting_open_so_item_not_changed_msg = "Voting is currently open so I didn't change item. Please #endvote first"
+ can_not_vote_msg = "You can not vote. Only {} can vote"
+ not_a_number_msg = "Your vote was not recognized as a number. Please retry."
+ out_of_range_msg = "Your vote was out of range!"
+ vote_confirm_msg = "You voted for #{} - {}"
+
+ # Internal
+ _voters = []
+ _votes = []
+ _agenda = []
+ _current_item = 0
+ _vote_open = False
+
+ def __init__(self, conf):
+ self.conf = conf
+
+ def get_agenda_item(self):
+ if self._current_item < len(self._agenda):
+ return str.format(self.current_item_msg, self._agenda[self._current_item][0])
+ else:
+ return self.empty_agenda_msg
+
+ def next_agenda_item(self):
+ if self._vote_open:
+ return voting_open_so_item_not_changed_msg
+ else:
+ if (self._current_item + 1) < len(self._agenda):
+ self._current_item += 1
+ return(self.get_agenda_item())
+
+ def prev_agenda_item(self):
+ if self._vote_open:
+ return voting_open_so_item_not_changed_msg
+ else:
+ if self._current_item > 0:
+ self._current_item -= 1
+ return(self.get_agenda_item())
+
+ def start_vote(self):
+ if self._vote_open:
+ return self.voting_already_open_msg
+ self._vote_open = True
+ options = "\n"
+ for i in range(len(self._agenda[self._current_item][1])):
+ options += str.format("{}. {}\n", i, self._agenda[self._current_item][i])
+ return str.format(self.voting_open_msg, options)
+
+ def end_vote(self):
+ if self._vote_open:
+ self._vote_open = False
+ return voting_already_closed_msg
+ return voting_close_msg
+
+ def get_data(self):
+ self._voters = self._get_json(self.conf.voters_url)
+ self._agenda = self._get_json(self.conf.agenda_url)
+ self._votes = { }
+ for i in self._agenda:
+ self._votes[i[0]] = { }
+
+ def vote(self, nick, line):
+ if not nick in self._voters:
+ return str.format(self.can_not_vote_msg, ", ".join(self._voters))
+ if not line.isdigit():
+ return self.not_a_number_msg
+
+ opt = int(line)
+
+ if opt < 0 or opt >= len(self._agenda[self._current_item][1]):
+ return self.out_of_range_msg
+
+ self._votes[self._agenda[self._current_item][0]][nick] = self._agenda[self._current_item][1][opt]
+ return str.format(self.vote_confirm_msg, opt, self._agenda[self._current_item][opt])
+
+ def _get_json(self, url):
+ str = urllib.urlopen(url).read()
+ str = urllib.unquote(str)
+ result = json.loads(str)
+ return result
diff --git a/bot/ircmeeting/meeting.py b/bot/ircmeeting/meeting.py
index 85880a6..b22dac6 100644
--- a/bot/ircmeeting/meeting.py
+++ b/bot/ircmeeting/meeting.py
@@ -36,6 +36,7 @@ import stat
import writers
import items
+import agenda
reload(writers)
reload(items)
@@ -96,6 +97,11 @@ class Config(object):
input_codec = 'utf-8'
output_codec = 'utf-8'
# Functions to do the i/o conversion.
+
+ # Meeting management urls
+ voters_url = 'http://localhost:3000/users/voters'
+ agenda_url = 'http://localhost:3000/agendas/current_items'
+
def enc(self, text):
return text.encode(self.output_codec, 'replace')
def dec(self, text):
@@ -118,7 +124,6 @@ class Config(object):
#'.rst.html':writers.HTMLfromReST,
}
-
def __init__(self, M, writeRawLog=False, safeMode=False,
extraConfig={}):
self.M = M
@@ -134,6 +139,7 @@ class Config(object):
for extension, writer in self.writer_map.iteritems():
self.writers[extension] = writer(self.M)
self.safeMode = safeMode
+ self.agenda = agenda.Agenda(self)
def filename(self, url=False):
# provide a way to override the filename. If it is
# overridden, it must be a full path (and the URL-part may not
@@ -308,6 +314,22 @@ class MeetingCommands(object):
self.reply(messageline)
if line.strip():
self.do_meetingtopic(nick=nick, line=line, time_=time_, **kwargs)
+ self.config.agenda.get_data()
+ self.reply(self.config.agenda.get_agenda_item())
+
+ def do_nextitem(self, nick, time_, line, **kwargs):
+ self.reply(self.config.agenda.next_agenda_item())
+
+ def do_previtem(self, nick, time_, line, **kwargs):
+ self.reply(self.config.agenda.prev_agenda_item())
+
+ def do_startvote(self, nick, time_, line, **kwargs):
+ for messageline in self.config.agenda.start_vote().split('\n'):
+ self.reply(messageline)
+
+ def do_vote(self, nick, time_, line, **kwargs):
+ self.reply(self.config.agenda.vote(nick, line))
+
def do_endmeeting(self, nick, time_, **kwargs):
"""End the meeting."""
if not self.isChair(nick): return
@@ -454,7 +476,7 @@ class MeetingCommands(object):
commands = [ "#"+x[3:] for x in dir(self) if x[:3]=="do_" ]
commands.sort()
self.reply("Available commands: "+(" ".join(commands)))
-
+
class Meeting(MeetingCommands, object):
diff --git a/bot/tests/MeetBot b/bot/tests/MeetBot
new file mode 120000
index 0000000..f06815a
--- /dev/null
+++ b/bot/tests/MeetBot
@@ -0,0 +1 @@
+../MeetBot \ No newline at end of file
diff --git a/bot/tests/ircmeeting b/bot/tests/ircmeeting
new file mode 120000
index 0000000..90e99f6
--- /dev/null
+++ b/bot/tests/ircmeeting
@@ -0,0 +1 @@
+../ircmeeting \ No newline at end of file
diff --git a/bot/tests/run_test.py b/bot/tests/run_test.py
index 213cd43..f304a2b 100644
--- a/bot/tests/run_test.py
+++ b/bot/tests/run_test.py
@@ -7,6 +7,7 @@ import shutil
import sys
import tempfile
import unittest
+import time
os.environ['MEETBOT_RUNNING_TESTS'] = '1'
import ircmeeting.meeting as meeting
@@ -14,6 +15,12 @@ import ircmeeting.writers as writers
running_tests = True
+def parse_time(time_):
+ try: return time.strptime(time_, "%H:%M:%S")
+ except ValueError: pass
+ try: return time.strptime(time_, "%H:%M")
+ except ValueError: pass
+
def process_meeting(contents, extraConfig={}, dontSave=True,
filename='/dev/null'):
"""Take a test script, return Meeting object of that meeting.
@@ -337,9 +344,73 @@ class MeetBotTest(unittest.TestCase):
assert M.config.filename().endswith('somechannel-blah1234'),\
"Filename not as expected: "+M.config.filename()
+ def test_agenda(self):
+ """ Test agenda management
+ """
+ logline_re = re.compile(r'\[?([0-9: ]*)\]? *<[@+]?([^>]+)> *(.*)')
+ loglineAction_re = re.compile(r'\[?([0-9: ]*)\]? *\* *([^ ]+) *(.*)')
+
+ M = process_meeting('#startmeeting')
+ log = []
+ M._sendReply = lambda x: log.append(x)
+ M.config.agenda._voters = ['x', 'z']
+ M.config.agenda._agenda = [['first item', ['opt1', 'opt2']], ['second item', []]]
+ M.config.agenda._votes = { }
+ for i in M.config.agenda._agenda:
+ M.config.agenda._votes[i[0]] = { }
+
+
+ M.starttime = time.gmtime(0)
+ contents = """20:13:50 <x> #nextitem
+ 20:13:50 <x> #nextitem
+ 20:13:50 <x> #previtem
+ 20:13:50 <x> #previtem
+ 20:13:50 <x> #startvote
+ 20:13:50 <x> #vote 10
+ 20:13:50 <x> #vote 1
+ 20:13:50 <y> #vote 0
+ 20:13:50 <z> #vote 0
+ 20:13:50 <x> #endvote
+ 20:13:50 <x> #endmeeting"""
+
+ for line in contents.split('\n'):
+ # match regular spoken lines:
+ m = logline_re.match(line)
+ if m:
+ time_ = parse_time(m.group(1).strip())
+ nick = m.group(2).strip()
+ line = m.group(3).strip()
+ if M.owner is None:
+ M.owner = nick ; M.chairs = {nick:True}
+ M.addline(nick, line, time_=time_)
+ # match /me lines
+ m = loglineAction_re.match(line)
+ if m:
+ time_ = parse_time(m.group(1).strip())
+ nick = m.group(2).strip()
+ line = m.group(3).strip()
+ M.addline(nick, "ACTION "+line, time_=time_)
+
+ self.assert_(M.config.agenda._votes == {'first item': {u'x': 'opt2', u'z': 'opt1'}, 'second item': {}})
+
+ answers = ['Current agenda item is second item.',
+ 'Current agenda item is second item.',
+ 'Current agenda item is first item.',
+ 'Current agenda item is first item.',
+ 'Voting started. Your choices are: ',
+ '0. first item',
+ "1. ['opt1', 'opt2']",
+ ' Vote #vote <option number>.',
+ ' End voting with #endvote.',
+ 'Your vote was out of range!',
+ "You voted for #1 - ['opt1', 'opt2']",
+ 'You can not vote. Only x, z can vote',
+ 'You voted for #0 - first item']
+ self.assert_(log[0:len(answers)] == answers)
if __name__ == '__main__':
os.chdir(os.path.join(os.path.dirname(__file__), '.'))
+
if len(sys.argv) <= 1:
unittest.main()
else:
diff --git a/bot/tests/test-script-1.html b/bot/tests/test-script-1.html
new file mode 100644
index 0000000..fa42143
--- /dev/null
+++ b/bot/tests/test-script-1.html
@@ -0,0 +1,256 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+<title>#test-script-1 Meeting</title>
+<style type="text/css">
+/* This is for the .html in the HTML2 writer */
+body {
+ font-family: Helvetica, sans-serif;
+ font-size:14px;
+}
+h1 {
+ text-align: center;
+}
+a {
+ color:navy;
+ text-decoration: none;
+ border-bottom:1px dotted navy;
+}
+a:hover {
+ text-decoration:none;
+ border-bottom: 0;
+ color:#0000B9;
+}
+hr {
+ border: 1px solid #ccc;
+}
+/* The (nick, time) item pairs, and other body text things. */
+.details {
+ font-size: 12px;
+ font-weight:bold;
+}
+/* The 'AGREED:', 'IDEA', etc, prefix to lines. */
+.itemtype {
+ font-style: normal; /* un-italics it */
+ font-weight: bold;
+}
+/* Example: change single item types. Capitalized command name.
+/* .TOPIC { color:navy; } */
+/* .AGREED { color:lime; } */
+
+</style>
+</head>
+
+<body>
+<h1>#test-script-1 Meeting</h1>
+<span class="details">
+Meeting started by MrBeige at 20:13:46 UTC
+(<a href="test-script-1.log.html">full logs</a>).</span>
+
+<br><br>
+
+
+
+<h3>Meeting summary</h3>
+<ol>
+<li>
+<ol type="a">
+ <li><span class="INFO">this command is just before the first
+ topic</span> <span class="details">(<a
+ href='test-script-1.log.html#l-2'>T-Rex</a>, 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">General command tests</b> <span class="details">(<a href='test-script-1.log.html#l-7'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><i class="itemtype">ACCEPTED</i>: <span class="ACCEPTED"><font
+ color="green">we will include this new format if we so
+ choose.</font></span> <span class="details">(<a
+ href='test-script-1.log.html#l-8'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">REJECTED</i>: <span class="REJECTED"><font
+ color="red">we will not include this new format.</font></span> <span
+ class="details">(<a href='test-script-1.log.html#l-9'>MrBeige</a>,
+ 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">Test of all commands with different arguments</b> <span class="details">(<a href='test-script-1.log.html#l-16'>MrBeige</a>, 20:13:50)</span>
+<br></li>
+<li><b class="TOPIC"></b> <span class="details">(<a href='test-script-1.log.html#l-17'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA"></span> <span
+ class="details">(<a href='test-script-1.log.html#l-18'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><span class="INFO"></span> <span class="details">(<a
+ href='test-script-1.log.html#l-19'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">ACTION</i>: <span class="ACTION"></span> <span
+ class="details">(<a href='test-script-1.log.html#l-20'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><i class="itemtype">AGREED</i>: <span class="AGREED"></span> <span
+ class="details">(<a href='test-script-1.log.html#l-21'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><i class="itemtype">HELP</i>: <span class="HELP"></span> <span
+ class="details">(<a href='test-script-1.log.html#l-22'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><i class="itemtype">ACCEPTED</i>: <span class="ACCEPTED"><font
+ color="green"></font></span> <span class="details">(<a
+ href='test-script-1.log.html#l-23'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">REJECTED</i>: <span class="REJECTED"><font
+ color="red"></font></span> <span class="details">(<a
+ href='test-script-1.log.html#l-24'>MrBeige</a>, 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">Commands with non-ascii</b> <span class="details">(<a href='test-script-1.log.html#l-25'>MrBeige</a>, 20:13:50)</span>
+<br></li>
+<li><b class="TOPIC">üáç€</b> <span class="details">(<a href='test-script-1.log.html#l-26'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA">üáç€</span> <span
+ class="details">(<a href='test-script-1.log.html#l-27'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><span class="INFO">üáç€</span> <span class="details">(<a
+ href='test-script-1.log.html#l-28'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">ACTION</i>: <span class="ACTION">üáç€</span>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-29'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">AGREED</i>: <span class="AGREED">üáç€</span>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-30'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">HELP</i>: <span class="HELP">üáç€</span> <span
+ class="details">(<a href='test-script-1.log.html#l-31'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><i class="itemtype">ACCEPTED</i>: <span class="ACCEPTED"><font
+ color="green">üáç€</font></span> <span class="details">(<a
+ href='test-script-1.log.html#l-32'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">REJECTED</i>: <span class="REJECTED"><font
+ color="red">üáç€</font></span> <span class="details">(<a
+ href='test-script-1.log.html#l-33'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA">blah</span> <span
+ class="details">(<a href='test-script-1.log.html#l-35'>MrBeige</a>,
+ 20:13:50)</span></li>
+ <li><i class="itemtype">ACTION</i>: <span class="ACTION">blah</span>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-36'>MrBeige</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">AGREED</i>: <span class="AGREED">blah</span>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-37'>Utahraptor</a>, 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">Escapes</b> <span class="details">(<a href='test-script-1.log.html#l-38'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA">blah_ blah_ ReST
+ link reference...</span> <span class="details">(<a
+ href='test-script-1.log.html#l-41'>Utahraptor</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA">blah blah
+ blah</span> <span class="details">(<a
+ href='test-script-1.log.html#l-42'>ReST1_</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under_score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-44'>ReST2_</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under_score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-45'>Re_ST</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under1_1score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-46'>Re_ST</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under1_score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-47'>Re_ST</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under_1score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-48'>Re_ST</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under-_score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-49'>Re_ST</a>, 20:13:50)</span></li>
+ <li><i class="itemtype">IDEA</i>: <span
+ class="IDEA">under_-score</span> <span class="details">(<a
+ href='test-script-1.log.html#l-50'>Re_ST</a>, 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">Links</b> <span class="details">(<a href='test-script-1.log.html#l-51'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><a
+ href="http://test&lt;b&gt;.zgib.net">http://test&lt;b&gt;.zgib.net</a>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-52'>Utahraptor</a>, 20:13:50)</span></li>
+ <li><a
+ href="ftp://test&lt;b&gt;.zgib.net">ftp://test&lt;b&gt;.zgib.net</a>
+ " <span class="details">(<a
+ href='test-script-1.log.html#l-53'>Utahraptor</a>, 20:13:50)</span></li>
+ <li><a href="mailto://a@bla%22h.com">mailto://a@bla"h.com</a> <span
+ class="details">(<a
+ href='test-script-1.log.html#l-54'>Utahraptor</a>, 20:13:50)</span></li>
+ <li><a
+ href="http://test.zgib.net/&amp;testpage">http://test.zgib.net/&amp;testpage</a>
+ <span class="details">(<a
+ href='test-script-1.log.html#l-55'>Utahraptor</a>, 20:13:50)</span></li>
+ <li>prefix <a
+ href="http://test.zgib.net/&amp;testpage">http://test.zgib.net/&amp;testpage</a>
+ suffix <span class="details">(<a
+ href='test-script-1.log.html#l-56'>Utahraptor</a>, 20:13:50)</span></li>
+ <li>prefix <a
+ href="ftp://test.zg%22ib.net/&amp;testpage">ftp://test.zg"ib.net/&amp;testpage</a>
+ suffix <span class="details">(<a
+ href='test-script-1.log.html#l-57'>Utahraptor</a>, 20:13:50)</span></li>
+ <li>prefix <a
+ href="mailto://a@blah.com&amp;testpage">mailto://a@blah.com&amp;testpage</a>
+ suffix <span class="details">(<a
+ href='test-script-1.log.html#l-58'>Utahraptor</a>, 20:13:50)</span></li>
+ <li>prefix <a href="http://google.com/">http://google.com/</a>. suffix
+ <span class="details">(<a
+ href='test-script-1.log.html#l-59'>Utahraptor</a>, 20:13:50)</span></li>
+ <li>prefix (<a href="http://google.com/">http://google.com/</a>)
+ suffix <span class="details">(<a
+ href='test-script-1.log.html#l-60'>Utahraptor</a>, 20:13:50)</span></li>
+</ol>
+<br></li>
+<li><b class="TOPIC">Character sets</b> <span class="details">(<a href='test-script-1.log.html#l-61'>MrBeige</a>, 20:13:50)</span>
+<ol type="a">
+ <li><i class="itemtype">IDEA</i>: <span class="IDEA">Nick with
+ accents.</span> <span class="details">(<a
+ href='test-script-1.log.html#l-63'>Üţáhraptõr</a>, 20:13:50)</span></li>
+</ol>
+</li>
+</ol>
+<br><br>
+
+
+
+<span class="details">
+Meeting ended at 20:13:52 UTC
+(<a href="test-script-1.log.html">full logs</a>).</span>
+
+<br><br>
+
+
+
+<h3>Action items</h3>
+<ol>
+ <li></li>
+ <li>üáç€</li>
+ <li>blah</li>
+</ol>
+<br><br>
+
+
+
+<h3>People present (lines said)</h3>
+<ol>
+ <li>MrBeige (35)</li>
+ <li>Utahraptor (13)</li>
+ <li>Re_ST (6)</li>
+ <li>T-Rex (5)</li>
+ <li>ReST2_ (2)</li>
+ <li>Üţáhraptõr (2)</li>
+ <li>ReST1_ (1)</li>
+ <li>áccents (0)</li>
+ <li>not-here (0)</li>
+ <li>** (0)</li>
+ <li>áccenẗs (0)</li>
+ <li>someone-not-present (0)</li>
+ <li>&lt;b&gt; (0)</li>
+</ol>
+<br><br>
+
+
+
+<span class="details">Generated by <a href="http://wiki.debian.org/MeetBot">MeetBot</a> 0.1.4.</span>
+</body></html>
diff --git a/bot/tests/test-script-1.log.html b/bot/tests/test-script-1.log.html
new file mode 100644
index 0000000..5e7642c
--- /dev/null
+++ b/bot/tests/test-script-1.log.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+<title>#test-script-1 log</title>
+<style type="text/css">
+/* For the .log.html */
+pre { /*line-height: 125%;*/
+ white-space: pre-wrap; }
+body { background: #f0f0f0; }
+
+body .tm { color: #007020 } /* time */
+body .nk { color: #062873; font-weight: bold } /* nick, regular */
+body .nka { color: #007020; font-weight: bold } /* action nick */
+body .ac { color: #00A000 } /* action line */
+body .hi { color: #4070a0 } /* hilights */
+/* Things to make particular MeetBot commands stick out */
+body .topic { color: #007020; font-weight: bold }
+body .topicline { color: #000080; font-weight: bold }
+body .cmd { color: #007020; font-weight: bold }
+body .cmdline { font-weight: bold }
+
+</style>
+</head>
+
+<body>
+<pre><a name="l-1"></a><span class="tm">20:13:46</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#startmeeting</span><span class="cmdline"></span>
+<a name="l-2"></a><span class="tm">20:13:50</span><span class="nk"> &lt;T-Rex&gt;</span> <span class="cmd">#info </span><span class="cmdline">this command is just before the first topic</span>
+<a name="l-3"></a><span class="tm">20:13:50</span><span class="nk"> &lt;T-Rex&gt;</span> <span class="topic">#topic </span><span class="topicline">Test of topics</span>
+<a name="l-4"></a><span class="tm">20:13:50</span><span class="nk"> &lt;T-Rex&gt;</span> <span class="topic">#topic </span><span class="topicline">Second topic</span>
+<a name="l-5"></a><span class="tm">20:13:50</span><span class="nk"> &lt;T-Rex&gt;</span> <span class="cmd">#meetingtopic </span><span class="cmdline">the meeting topic</span>
+<a name="l-6"></a><span class="tm">20:13:50</span><span class="nk"> &lt;T-Rex&gt;</span> <span class="topic">#topic </span><span class="topicline">With áccents</span>
+<a name="l-7"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">General command tests</span>
+<a name="l-8"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#accepted </span><span class="cmdline">we will include this new format if we so choose.</span>
+<a name="l-9"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#rejected </span><span class="cmdline">we will not include this new format.</span>
+<a name="l-10"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#chair </span><span class="cmdline">Utahraptor T-Rex not-here</span>
+<a name="l-11"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#chair </span><span class="cmdline">Utahraptor T-Rex</span>
+<a name="l-12"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#nick </span><span class="cmdline">someone-not-present</span>
+<a name="l-13"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#chair </span><span class="cmdline">áccents</span>
+<a name="l-14"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#nick </span><span class="cmdline">áccenẗs</span>
+<a name="l-15"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#unchar </span><span class="cmdline">not-here</span>
+<a name="l-16"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">Test of all commands with different arguments</span>
+<a name="l-17"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic</span><span class="topicline"></span>
+<a name="l-18"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#idea</span><span class="cmdline"></span>
+<a name="l-19"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#info</span><span class="cmdline"></span>
+<a name="l-20"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#action</span><span class="cmdline"></span>
+<a name="l-21"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#agreed</span><span class="cmdline"></span>
+<a name="l-22"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#halp</span><span class="cmdline"></span>
+<a name="l-23"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#accepted</span><span class="cmdline"></span>
+<a name="l-24"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#rejected</span><span class="cmdline"></span>
+<a name="l-25"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">Commands with non-ascii</span>
+<a name="l-26"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">üáç€</span>
+<a name="l-27"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#idea </span><span class="cmdline">üáç€</span>
+<a name="l-28"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#info </span><span class="cmdline">üáç€</span>
+<a name="l-29"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#action </span><span class="cmdline">üáç€</span>
+<a name="l-30"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#agreed </span><span class="cmdline">üáç€</span>
+<a name="l-31"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#halp </span><span class="cmdline">üáç€</span>
+<a name="l-32"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#accepted </span><span class="cmdline">üáç€</span>
+<a name="l-33"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#rejected </span><span class="cmdline">üáç€</span>
+<a name="l-34"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#item </span><span class="cmdline">blah</span>
+<a name="l-35"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#idea </span><span class="cmdline">blah</span>
+<a name="l-36"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#action </span><span class="cmdline">blah</span>
+<a name="l-37"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#agreed </span><span class="cmdline">blah</span>
+<a name="l-38"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">Escapes</span>
+<a name="l-39"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#nick </span><span class="cmdline">&lt;b&gt;</span>
+<a name="l-40"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#nick </span><span class="cmdline">**</span>
+<a name="l-41"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#idea </span><span class="cmdline">blah_ blah_ ReST link reference...</span>
+<a name="l-42"></a><span class="tm">20:13:50</span><span class="nk"> &lt;ReST1_&gt;</span> <span class="cmd">#idea </span><span class="cmdline">blah blah blah</span>
+<a name="l-43"></a><span class="tm">20:13:50</span><span class="nk"> &lt;ReST2_&gt;</span> this is some text
+<a name="l-44"></a><span class="tm">20:13:50</span><span class="nk"> &lt;ReST2_&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under_score</span>
+<a name="l-45"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under_score</span>
+<a name="l-46"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under1_1score</span>
+<a name="l-47"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under1_score</span>
+<a name="l-48"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under_1score</span>
+<a name="l-49"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under-_score</span>
+<a name="l-50"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Re_ST&gt;</span> <span class="cmd">#idea </span><span class="cmdline">under_-score</span>
+<a name="l-51"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">Links</span>
+<a name="l-52"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">http://test&lt;b&gt;.zgib.net</span>
+<a name="l-53"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">ftp://test&lt;b&gt;.zgib.net "</span>
+<a name="l-54"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">mailto://a@bla"h.com</span>
+<a name="l-55"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">http://test.zgib.net/&amp;testpage</span>
+<a name="l-56"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">prefix http://test.zgib.net/&amp;testpage suffix</span>
+<a name="l-57"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">prefix ftp://test.zg"ib.net/&amp;testpage suffix</span>
+<a name="l-58"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">prefix mailto://a@blah.com&amp;testpage suffix</span>
+<a name="l-59"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">prefix http://google.com/. suffix</span>
+<a name="l-60"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Utahraptor&gt;</span> <span class="cmd">#link </span><span class="cmdline">prefix (http://google.com/) suffix</span>
+<a name="l-61"></a><span class="tm">20:13:50</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="topic">#topic </span><span class="topicline">Character sets</span>
+<a name="l-62"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Üţáhraptõr&gt;</span> Nick with accents.
+<a name="l-63"></a><span class="tm">20:13:50</span><span class="nk"> &lt;Üţáhraptõr&gt;</span> <span class="cmd">#idea </span><span class="cmdline">Nick with accents.</span>
+<a name="l-64"></a><span class="tm">20:13:52</span><span class="nk"> &lt;MrBeige&gt;</span> <span class="cmd">#endmeeting</span><span class="cmdline"></span></pre>
+</body></html>
diff --git a/bot/tests/test-script-1.txt b/bot/tests/test-script-1.txt
new file mode 100644
index 0000000..0e279bb
--- /dev/null
+++ b/bot/tests/test-script-1.txt
@@ -0,0 +1,98 @@
+======================
+#test-script-1 Meeting
+======================
+
+
+Meeting started by MrBeige at 20:13:46 UTC. The full logs are available
+at test-script-1.log.html .
+
+
+
+Meeting summary
+---------------
+
+* this command is just before the first topic (T-Rex, 20:13:50)
+* General command tests (MrBeige, 20:13:50)
+ * ACCEPTED: we will include this new format if we so choose.
+ (MrBeige, 20:13:50)
+ * REJECTED: we will not include this new format. (MrBeige, 20:13:50)
+
+* Test of all commands with different arguments (MrBeige, 20:13:50)
+
+* (MrBeige, 20:13:50)
+ * IDEA: (MrBeige, 20:13:50)
+ * (MrBeige, 20:13:50)
+ * ACTION: (MrBeige, 20:13:50)
+ * AGREED: (MrBeige, 20:13:50)
+ * HELP: (MrBeige, 20:13:50)
+ * ACCEPTED: (MrBeige, 20:13:50)
+ * REJECTED: (MrBeige, 20:13:50)
+
+* Commands with non-ascii (MrBeige, 20:13:50)
+
+* üáç€ (MrBeige, 20:13:50)
+ * IDEA: üáç€ (MrBeige, 20:13:50)
+ * üáç€ (MrBeige, 20:13:50)
+ * ACTION: üáç€ (MrBeige, 20:13:50)
+ * AGREED: üáç€ (MrBeige, 20:13:50)
+ * HELP: üáç€ (MrBeige, 20:13:50)
+ * ACCEPTED: üáç€ (MrBeige, 20:13:50)
+ * REJECTED: üáç€ (MrBeige, 20:13:50)
+ * IDEA: blah (MrBeige, 20:13:50)
+ * ACTION: blah (MrBeige, 20:13:50)
+ * AGREED: blah (Utahraptor, 20:13:50)
+
+* Escapes (MrBeige, 20:13:50)
+ * IDEA: blah_ blah_ ReST link reference... (Utahraptor, 20:13:50)
+ * IDEA: blah blah blah (ReST1_, 20:13:50)
+ * IDEA: under_score (ReST2_, 20:13:50)
+ * IDEA: under_score (Re_ST, 20:13:50)
+ * IDEA: under1_1score (Re_ST, 20:13:50)
+ * IDEA: under1_score (Re_ST, 20:13:50)
+ * IDEA: under_1score (Re_ST, 20:13:50)
+ * IDEA: under-_score (Re_ST, 20:13:50)
+ * IDEA: under_-score (Re_ST, 20:13:50)
+
+* Links (MrBeige, 20:13:50)
+ * LINK: http://test<b>.zgib.net (Utahraptor, 20:13:50)
+ * LINK: ftp://test<b>.zgib.net " (Utahraptor, 20:13:50)
+ * LINK: mailto://a@bla"h.com (Utahraptor, 20:13:50)
+ * LINK: http://test.zgib.net/&testpage (Utahraptor, 20:13:50)
+ * LINK: prefix http://test.zgib.net/&testpage suffix (Utahraptor,
+ 20:13:50)
+ * LINK: prefix ftp://test.zg"ib.net/&testpage suffix (Utahraptor,
+ 20:13:50)
+ * LINK: prefix mailto://a@blah.com&testpage suffix (Utahraptor,
+ 20:13:50)
+ * LINK: prefix http://google.com/. suffix (Utahraptor, 20:13:50)
+ * LINK: prefix (http://google.com/) suffix (Utahraptor, 20:13:50)
+
+* Character sets (MrBeige, 20:13:50)
+ * IDEA: Nick with accents. (Üţáhraptõr, 20:13:50)
+
+
+
+Meeting ended at 20:13:52 UTC.
+
+
+
+People present (lines said)
+---------------------------
+
+* MrBeige (35)
+* Utahraptor (13)
+* Re_ST (6)
+* T-Rex (5)
+* ReST2_ (2)
+* Üţáhraptõr (2)
+* ReST1_ (1)
+* áccents (0)
+* not-here (0)
+* ** (0)
+* áccenẗs (0)
+* someone-not-present (0)
+* <b> (0)
+
+
+
+Generated by `MeetBot`_ 0.1.4 \ No newline at end of file