# coding=utf-8 """ meetbot.py - Sopel meeting logger module Copyright © 2012, Elad Alfassa, Licensed under the Eiffel Forum License 2. This module is an attempt to implement at least some of the functionallity of Debian's meetbot """ import time import os from config.types import StaticSection, FilenameAttribute, ValidatedAttribute from module import example, commands, rule, priority from tools import Ddict, Identifier import codecs class MeetbotSection(StaticSection): meeting_log_path = FilenameAttribute('meeting_log_path', directory=True, default='~/www/meetings') """Path to meeting logs storage directory This should be an absolute path, accessible on a webserver.""" meeting_log_baseurl = ValidatedAttribute( 'meeting_log_baseurl', default='http://localhost/~sopel/meetings' ) """Base URL for the meeting logs directory""" def configure(config): config.define_section('meetbot', MeetbotSection) config.meetbot.configure_setting( 'meeting_log_path', 'Enter the directory to store logs in.' ) config.meetbot.configure_setting( 'meeting_log_baseurl', 'Enter the base URL for the meeting logs.', ) def setup(bot): bot.config.define_section('meetbot', MeetbotSection) meetings_dict = Ddict(dict) # Saves metadata about currently running meetings """ meetings_dict is a 2D dict. Each meeting should have: channel time of start head (can stop the meeting, plus all abilities of chairs) chairs (can add infolines to the logs) title current subject comments (what people who aren't voiced want to add) Using channel as the meeting ID as there can't be more than one meeting in a channel at the same time. """ meeting_log_path = '' # To be defined on meeting start as part of sanity checks, used by logging functions so we don't have to pass them bot meeting_log_baseurl = '' # To be defined on meeting start as part of sanity checks, used by logging functions so we don't have to pass them bot meeting_actions = {} # A dict of channels to the actions that have been created in them. This way we can have .listactions spit them back out later on. #Get the logfile name for the meeting in the requested channel #Used by all logging functions def figure_logfile_name(channel): if meetings_dict[channel]['title'] is 'Untitled meeting': name = 'untitled' else: name = meetings_dict[channel]['title'] # Real simple sluggifying. This bunch of characters isn't exhaustive, but # whatever. It's close enough for most situations, I think. for c in ' ./\\:*?"<>|&*`': name = name.replace(c, '-') timestring = time.strftime('%Y-%m-%d-%H:%M', time.gmtime(meetings_dict[channel]['start'])) filename = timestring + '_' + name return filename #Start HTML log def logHTML_start(channel): logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.html', 'a', encoding='utf-8') timestring = time.strftime('%Y-%m-%d %H:%M', time.gmtime(meetings_dict[channel]['start'])) title = '%s at %s, %s' % (meetings_dict[channel]['title'], channel, timestring) logfile.write('\n\n\n\n%TITLE%\n\n\n

%TITLE%

\n'.replace('%TITLE%', title)) logfile.write('

Meeting started by %s

\n

Meeting ended at %s UTC

\n' % current_time) plainlog_url = meeting_log_baseurl + channel + '/' + figure_logfile_name(channel) + '.log' logfile.write('Full log' % plainlog_url) logfile.write('\n\n') logfile.close() #Write a string to the plain text log def logplain(item, channel): current_time = time.strftime('%H:%M:%S', time.gmtime()) logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.log', 'a', encoding='utf-8') logfile.write('[' + current_time + '] ' + item + '\r\n') logfile.close() #Check if a meeting is currently running def ismeetingrunning(channel): try: if meetings_dict[channel]['running']: return True else: return False except: return False #Check if nick is a chair or head of the meeting def ischair(nick, channel): try: if nick.lower() == meetings_dict[channel]['head'] or nick.lower() in meetings_dict[channel]['chairs']: return True else: return False except: return False #Start meeting (also preforms all required sanity checks) @commands('startmeeting') @example('.startmeeting title or .startmeeting') def startmeeting(bot, trigger): """ Start a meeting. https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module """ if ismeetingrunning(trigger.sender): bot.say('Can\'t do that, there is already a meeting in progress here!') return if trigger.is_privmsg: bot.say('Can only start meetings in channels') return #Start the meeting meetings_dict[trigger.sender]['start'] = time.time() if not trigger.group(2): meetings_dict[trigger.sender]['title'] = 'Untitled meeting' else: meetings_dict[trigger.sender]['title'] = trigger.group(2) meetings_dict[trigger.sender]['head'] = trigger.nick.lower() meetings_dict[trigger.sender]['running'] = True meetings_dict[trigger.sender]['comments'] = [] global meeting_log_path meeting_log_path = bot.config.meetbot.meeting_log_path if not meeting_log_path.endswith('/'): meeting_log_path = meeting_log_path + '/' global meeting_log_baseurl meeting_log_baseurl = bot.config.meetbot.meeting_log_baseurl if not meeting_log_baseurl.endswith('/'): meeting_log_baseurl = meeting_log_baseurl + '/' if not os.path.isdir(meeting_log_path + trigger.sender): try: os.makedirs(meeting_log_path + trigger.sender) except Exception: bot.say("Can't create log directory for this channel, meeting not started!") meetings_dict[trigger.sender] = Ddict(dict) raise return #Okay, meeting started! logplain('Meeting started by ' + trigger.nick.lower(), trigger.sender) logHTML_start(trigger.sender) meeting_actions[trigger.sender] = [] bot.say('Meeting started! use .action, .agreed, .info, .chairs, .subject and .comments to control the meeting. to end the meeting, type .endmeeting') bot.say('Users without speaking permission can use .comment ' + trigger.sender + ' followed by their comment in a PM with me to ' 'vocalize themselves.') #Change the current subject (will appear as

in the HTML log) @commands('subject') @example('.subject roll call') def meetingsubject(bot, trigger): """ Change the meeting subject. https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module """ if not ismeetingrunning(trigger.sender): bot.say('Can\'t do that, start meeting first') return if not trigger.group(2): bot.say('what is the subject?') return if not ischair(trigger.nick, trigger.sender): bot.say('Only meeting head or chairs can do that') return meetings_dict[trigger.sender]['current_subject'] = trigger.group(2) logfile = codecs.open(meeting_log_path + trigger.sender + '/' + figure_logfile_name(trigger.sender) + '.html', 'a', encoding='utf-8') logfile.write('

' + trigger.group(2) + '