MeetBot is a plugin to the IRC bot supybot to facilitate taking of notes during IRC meetings. This allows you to better communicate with your project or groups after the IRC meeting, as well as keep the meeting more under control and on-topic.
Let's go through, step by step, how a typical meeting might run:
< MrBeige> #startmeeting
We use the #startmeeting command to tell MeetBot to start the meeting. The person who calls the command becomes the chair - having the power to guide the meeting. However, by default MeetBot allows other participants to enter most things into the logs, since inviting contributions is generally a good thing.:
< MeetBot> Meeting started Wed Jun 17 05:00:49 2009 UTC. The chair is MrBeige. < MeetBot> Information about MeetBot at http://wiki.debian.org/MeetBot , Useful Commands: #action #agreed #help #info #idea #link #topic.
MeetBot gives us a little bit of information about the meeting.:
< MrBeige> #topic should we release or not? -!- MeetBot changed the topic of #meetbot-test to: should we release or not?
We use the #topic command to tell MeetBot to move to the first topic. MeetBot sets the topic in the channel to the topic which is given on the line. Don't worry, the topic will be restored at the end of the meeting.:
< MrBeige> #info we have two major blocking bugs: the character set conversion, and the segfaults heisenbug in the save routine.
When there is important things said, we don't want them to be lost in the irclogs. Thus, we use the #info command to make a note of it in the meeting minutes. It is also highlighted in the irclogs which MeetBot takes.:
< MrBeige> #agreed we give one week to fix these (no other changes accepted), and then release
We also have the #agreed command to use. This can only be used by the chairs of the meeting, and should (obviously) be used to document agreement. The rest of the line goes into the minutes as the thing agreed on.:
< MrBeige> #action MrGreen and MrMauve work together to fix the bugs < MrBeige> #action MrBeige releases when done
We have the #action command. This one is works just like the last two, but has one extra feature: at the end of the meeting, it makes a list of "Action Items", useful for being sure things get taken care of. But there is more: it also makes a list of action items sorted by nick. This can be used to easily see what is assigned to you. In order for an item to be sorted by a nick, that nick has got to say something during the meeting (but also see the #nick command), and you have to use their nick exactly (use tab completion!).:
< MrBeige> #topic goals for release after next -!- MeetBot changed the topic of #meetbot-test to: goals for release after next
Moving on to the next topic...:
... < MrBeige> #info make it better ... < MrBeige> #info release faster ...
Record some of the important items from this section.:
< MrBeige> #endmeeting
Hit the #endmeeting command. The meeting ends, and logs and minutes are saved:
-!- MeetBot changed the topic of #meetbot-test to: General discussion of MeetBot < MeetBot> Meeting ended Wed Jun 17 05:03:45 2009 UTC. Information about MeetBot at http://wiki.debian.org/MeetBot . < MeetBot> Minutes: http://rkd.zgib.net/meetbot/meetbot-test/meetbot-test.html < MeetBot> Log: http://rkd.zgib.net/meetbot/meetbot-test/meetbot-test.log.html
MeetBot conveniently tells us where all of the logs are stored. You can look at the logs and minutes online.
All commands are case-insensitive, and use the # prefix character. Not all commands have output. This might be confusing, because you don't know if it's been acted on or not. However, this is a conscious design decision to try to keep out of the way and not distract from the real people. If something goes wrong, you can adjust and have MeetBot re-process the logs later.
Add new chairs to the meeting. The rest of the line is a list of nicks, separated by commas and/or spaces. The nick which started the meeting is the owner and can't be de-chaired. The command replies with a list of the current chairs, for verification (Chairs only.) Example:
< MrBeige> #chair MrGreen MsAlizarin < MeetBot> Current chairs are: MsAlizarin MrBeige MrGreen
#action
Add an ACTION item to the minutes. Provide irc nicks of people involved, and will be both a complete listing of action items, and a listing of action items sorted by nick at the end of the meeting. This is very useful for making sure this gets done. Example:
< MrBeige> #action MrGreen will read the entire Internet to determine why the hive cluster is under attack.If MrGreen has said something during the meeting, this will be automatically assigned to him.
Add an INFO item to the minutes. Example:
< MrBeige> #info We need to spawn more overlords before the next release.
#link
Add a link to the minutes. The URL will be properly detected within the line in most cases - the URL can't contain spaces. This command is automatically detected if the line starts with http:, https:, mailto:, and some other common protocols defined in the UrlProtocols configuration variable. Examples:
< MrBeige> #link http://wiki.debian.org/MeetBot/ is the main page < MrBeige> http://wiki.debian.org/MeetBot/ is the main page < MrBeige> #link the main page is http://wiki.debian.org/MeetBot/ so go there < MrBeige> the main page is http://wiki.debian.org/MeetBot/ so go there. (This will NOT be detected automatically)
Provide a friendly name which can be used as a variable in the filename patterns. For example, you can set filenamePattern = '%(channel)s/%%Y/%(meetingname)s.%%F-%%H.%%M' to allow #meetingname to categorize multiple types of meeting occurring in one channel.
All spaces are removed from the rest of the line and the string is converted to lowercase. If meetingname is not provided, it defaults to channel. (Chairs only.)
Please contribute to this section!
Unfortunately, MeetBot seems a bit complex to configure. In order to keep things straight, keep this in mind: MeetBot has two distinct pieces. The first (meeting.py and friends) is the meeting parser and note/logfile generator. This part can run independently, without the supybot plugin. The second part interfaces the core meeting.py to supybot, to make it usable via IRC.
When reading about how to run MeetBot, keep this in mind, and if something is applicable to meeting.py features, just supybot features, or both.
This design split greatly increases modularity (a "good thing"), and also allows the Replay functionality. It should also allow other bot plugins to easily be written.
Let's say you had a meeting which happened a while ago, and you would like to update the logs to a newer format. If supybot was the only way to use MeetBot, you'd be out of luck. Luckily, there is an independent way to replay meetings:
python /path/to/meeting.py replay /path/to/old_meeting.log.txt
You run the meeting.py file as a script, giving the subcommand replay and then a path to a .log.txt file from a previous meeting (or from some other source of IRC logs, it's essentially the irssi/xchat format). It parses it and processes the meeting, and outputs all of the usual .html, .log.html, and so on files in the directory parallel to the input file.
This is useful if you want to upgrade your output formats, MeetBot broke and you lost the realtime log and want to generate a summary using your own logfiles, remove or change something in the logs that was incorrect during the meeting. As such, this is an important feature of MeetBot.
However, this does make configuration changes harder. Since the replay function works independent of supybot, any configuration that is done in supybot will be invisible to the replay function. Thus, we have to have a non-supybot mechanism of configuring MeetBot. There was a supybot way of configuring MeetBot added later, which can adjust most variables. However, if something is configured here, it won't be seen if a file is replayed. This might be OK, or it might not be, depending on the variable.
This is the non-supybot method of configuration, and allows the most flexibility. It works for configuring supybot, too, but requires shell access and a MeetBot reload to change.
Configuration is done by creating a file meetingLocalConfig.py in the plugin directory, or somewhere in your PYTHONPATH. It works by (automatically, not user-visible) subclassing the Config class.
Here is a minimal usage example. You need at least this much to make it run. Put this in meetingLocalConfig.py before you first start supybot:
class Config(object): # These two are **required**: logFileDir = '/home/richard/meetbot/' logUrlPrefix = 'http://rkd.zgib.net/meetbot/'
Two other more commonly used options are:
filenamePattern = '%(channel)s/%%Y/%(channel)s.%%F-%%H.%%M' MeetBotInfoURL = 'http://some_other_side.tld'
Place all of the configuration variables inside of the class body like this.
meetingLocalConfig.py is imported via python, so all the usual benefits and caveats of that apply. It causes a subclass of the main Config object. Thus, you can do some advanced (or just crazy) things like add a new meeting command, meeting agenda item type, or more. Some of these ideas are documented under the "Advanced configuration" section below.
To reload a configuration in a running supybot, you can just reload the plugin in supybot --- the module is reloaded. Specifically, /msg YourBotName reload MeetBot.
This is the system that configures MeetBot based on the supybot registry system. Thus, it can be adjusted by anyone with the proper supybot capabilities. However, the configuration in the supybot registry won't be used if the replay functionality is used (see above). Thus, for example, if you configure the MediaWiki writer using supybot.plugins.MeetBot.writer_map, and then replay the meeting, the MediaWiki output will not be updated.
To enable this system, first the supybot.plugins.MeetBot.enableSupybotBasedConfig variable must be set to True. Then the MeetBot plugin must be reloaded:
/msg YourBot config supybot.plugins.MeetBot.enableSupybotBasedConfig True /msg YourBot reload MeetBot
Now you can list the values available for configuration (the list below may not be up to date):
/msg YourBot config list supybot.plugins.MeetBot ----> #endMeetingMessage, #filenamePattern, #input_codec, #logFileDir, #logUrlPrefix, #MeetBotInfoURL, #output_codec, #pygmentizeStyle, #specialChannelFilenamePattern, #startMeetingMessage, #timeZone, #usefulCommands, enableSupybotBasedConfig, and public
Setting a value for a variable:
/msg YourBot config supybot.plugins.MeetBot.logUrlPrefix http://meetings.yoursite.net/
Most variables (those with # prepended) can be set on a per-channel basis (they are set up as channel-specific variables).
At present, not all variables are exported to supybot. All string and boolean variables are, as well certain other variables for which a wrapper has been written (writer_map in particular). If a variable doesn't appear in the supybot registry, it can't be set via the registry.
If you want to disable supybot-based config for security reasons, set dontBotConfig to True in your custom configuration class in meetingLocalConfig.py.
These variables are set either in meetingLocalConfig.py (in the Config class) or in the supybot registry.
This defaults to '%(channel)s/%%Y/%(channel)s.%%F-%%H.%%M', and is the pattern used for replacements to identify the name of the file basename (including possible sub-directories) in which to store the meeting output files. This is the suffix to logFileDir and logUrlPrefix.
Variables available for replacement using %(name)s include: channel, network, meetingname. Double percent signs (e.g.: %%Y are time formats, from time.strftime.
You should not include filename extensions here. Those are found from the writers, via the variable writer_map.
writer_map tells how we want to output the results of the meeting. It is, as you can guess, a mapping from filename extensions (.html, .log.html, ...) to what we want to output to that file (writers.HTML, writers.HTMLlog, ...)
Using meetingLocalConfig.py, it is python dictionary listing what output formats will be used to write our final results to a file, along with extensions to use. For example, in meetingLocalConfig.py:
import writers class Config: writer_map = { '.log.html':writers.HTMLlog, '.html': writers.HTML, '.txt': writers.RST, '.mw':writers.MediaWiki, }
If an extension begins in .none the output will not be written to a file. Note that you can't have the same extension multiple times due to the way python dictionaries work: use .none1, .none2, etc.
This can be configured through supybot. To do this, set supybot.plugins.MeetBot.writer_map to a space-separated list of WriterName:.extension pairs (note the different ordering from the python dictionary). For example, to list the current setting (in private message with the bot):
<MrBeige> config plugins.MeetBot.writer_map <MeetBot> HTML2:.html MediaWiki:.mw HTMLlog2:.log.html Text:.txt
And to set it (again, in private message with the bot):
<MrBeige> config plugins.MeetBot.writer_map HTML2:.html MediaWiki:.mw HTMLlog2:.log.html Text:.txt
There is a special way to pass arguments to writers. Learn by example:
writer_map = { '.mw|mwsite=http://site.net|mwpath=Meetings':writers.MediaWiki, }
or via supybot:
config plugins.MeetBot.writer_map MediaWiki:.mw|mwsite=http://site.net|mwpath=Meetings
The available writers are (with default extensions, if enabled by default):
MediaWiki output.
The MediaWiki writer has the ability to upload to a MediaWiki site directly. You use the custom variables mwsite: site name to upload to, mwpath: subpage to upload to (final location is %(mwpath)/%(file_basename)), ``mwusername and mwpassword: username and password to log in as.
An upload is attempted if mwsite is given. A login is attempted if mwusername is given. An example configuration:
writer_map = { '.mw|mwsite=http://site.net|mwpath=Meetings':writers.MediaWiki, }
To use the templating engine, you must specify the template file to use. This is done via a special argument syntax. Instead of an file extension name, the extension should be specified as .EXTENSION_NAME|template=TEMPLATE_FILE, with the metavariables explaining what the parts do. For example, in meetingLocalConfig.py one would do:
class Config: writer_map = { ... '.tmpl.txt|template=+template.txt' = writers.Template, }When setting a template writer by the suybot registry, one would do:
/msg YourBot config plugins.MeetBot.writer_map <other writers> Template:.EXTENSION_NAME|template=TEMPLATE_FILE ...TEMPLATE_FILE is an absolute or relative filename. As a special case, +TEMPLATE_NAME can be used to specify a path relative to the MeetBot source directory. This is used to include the default templates: +template.html or +template.txt .
Obsolete writers are:
These variables are set either in meetingLocalConfig.py (in the Config class) or in the supybot registry.
An int listing which permissions to remove when using the #restrictlogs command. It is best to use the python stat module to set it:
RestrictPerm = stat.S_IRWXO|stat.S_IRWXG
startMeetingMessage
input_codec
cssFile_minutes and cssFile_log
If given, this is a file containing CSS for the .html and .log.html outputs (HTML2 and HTMLlog2 writers). Embedding control is described below.
If this value is the null string or 'default', then the default CSS is used (see css-*-default.css in the MeetBot distribution). If this value is 'none', then no stylesheet information is written whatsoever.
Note that for when embedded (see below), cssFile should be a filesystem path readable locally. When you are not embedding, cssFile should be the URL to the stylesheet, and this value given is included literally to the output.
cssEmbed_minutes and cssEmbed_log
If these are True, then the contents of cssFile (above) are read and embedded into the HTML document. If these are False, then a stylesheet link is written.
This gives a few examples of things you can do via meetingLocalConfig.py. Most people probably won't need these things, and they aren't thoroughly explained here.
You can make a per-channel config:
class Config(object): def init_hook(self): if self.M.channel == '#some-channel': self.logFileDir = '/some/directory' else: self.logFileDir = '/some/other/directory'
Make a per-channel writer_map (note that you shouldn't change writer_map in place):
import writers class Config(object): def init_hook(self): if self.M.channel == '#some-channel': self.writer_map = self.writer_map.copy() self.writer_map['.mw'] = writers.MediaWiki
The display styles (in html writers) can be modified also, by using the starthtml and endhtml attributes (put this in meetingLocalConfig.py:
import items items.Agreed.starthtml = '<font color="red">' items.Agreed.endhtml = '</font>'
Adding a new custom command via meetingLocalConfig.py. (This likely won't make sense unless you examine the code a bit and know esoteric things about python method types):
import types class Config(object): def init(self): def do_party(self, nick, time_, **kwargs): self.reply("We're having a party in this code!") self.reply(("Owner, Chairs: %s %s"%( self.owner,sorted(self.chairs.keys())))) self.M.do_party = types.MethodType( do_party, self.M, self.M.__class__)
Make a command alias. Make #weagree an alias for #agreed:
class Config(object): def init(self): self.M.do_weagree = self.M.do_agreed
These commands are for the bot owners to manage all meetings served by their bot. The expected use of these commands is when the bot is on many channels as a public service, and the bot owner sometimes needs to be able to monitor and adjust the overall situation, even if she is not the chair of a meeting.
All of these are regular supybot commands (as opposed to the commands above). That means that the supybot capability system applies, and they can be given either in any channel, either by direct address (BotName: <command> <args> ...) or with the bot prefix character (e.g. @<commandname> <args> ...). If there are commands with the same name in multiple plugins, you need to prefix the command with the plugin name (for example, BotName: meetbot recent instead of BotName: recent)
These are restricted to anyone with the admin capability on the bot.
To connect to multiple IRC networks, use the supybot Network plugin to manage them. First, load the Network plugin, then use the connect command to connect to the other network. Finally, you need to tell supybot to join channels on the new. To do that, you can use network command <other_network> join <channel>. (Alternatively, you can /msg the bot through the other network, but you'll have to register your nick to it on the other network in order for it to accept commands from you.)
To speak with other developers and users, please join #meetbot on irc.oftc.net.
Code contributions to MeetBot are encouraged, but you probably want to check with others in #meetbot first to discuss general plans.
MeetBot is primarily used as a supybot plugin, however, it is designed to not be limited to use with supybot. Thus, there are some design choices which are slightly more complicated.
meeting.py contains the core of the MeetBot code. Most meeting functionality modifications would begin here.
items.py contains MeetingItem objects of different classes. These hold data about different #commands, most importantly their formatting information.
writers.py contains the code to write the output files. It depends on the objects in items.py to be able to format themselves, and the various classes in here
plugin.py, config.py, test.py, __init__.py are all supybot based files. (yes, the supybot/not-supybot split is not as rigorous as it should be). All of the supybot commands to interact with the meeting and send lines to the Meeting object are in plugin.py. If you want to add a supybot-based feature, this would be the place to start.
To get a copy of the repo, the first time, use the get command:
darcs get http://code.zgib.net/MeetBot/ # dev darcs get http://darcs.debian.org/darcs/collab-maint/MeetBot/ # stable
After that, to get code updates, use the pull command:
darcs get http://code.zgib.net/MeetBot/ # dev darcs get http://darcs.debian.org/darcs/collab-maint/MeetBot/ # stable
Darcs truly supports "cherry-picking": you can pull patches from either branch at will (They will be kept synchronized enough so that this works). You may skip any patches you do not desire, and pull any later patches as long as you have all earlier dependencies.
To send code back, you can use darcs diff -u for a simple strategy, or you may record and send actual darcs patches. To record darcs patches at first:
darcs record # 1) interactively select the group of changes # (y/n questions) # 2) Enter a patch name. Say yes for entering a # long coment # 3) Enter in a descriptive comment. See other # patches for a model, but I tend to use a # bulleted list.
The send command will send a patch to the developers via a HTTP POST:
darcs send http://code.zgib.net/MeetBot/
If it is not signed with an authorized PGP key, it will be forwarded to the developers, and the developers can manually approve and apply the patch. Developers can have their PGP key added.
There are many other useful darcs commands. Discuss on #meetbot if you would like to find out more.
The darcs push command is the counterpart to pull, and used to move changes around when you have direct write access to the remote repository.
The channel #meetbot on irc.oftc.net is the best place to go.