| Total Complexity | 65 |
| Total Lines | 562 |
| Duplicated Lines | 7.65 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like sopel.modules.meetbot often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # coding=utf-8 |
||
| 2 | """ |
||
| 3 | meetbot.py - Sopel Meeting Logger Module |
||
| 4 | This module is an attempt to implement some of the functionality of Debian's meetbot |
||
| 5 | Copyright © 2012, Elad Alfassa, <[email protected]> |
||
| 6 | Licensed under the Eiffel Forum License 2. |
||
| 7 | |||
| 8 | https://sopel.chat |
||
| 9 | """ |
||
| 10 | from __future__ import absolute_import, division, print_function, unicode_literals |
||
| 11 | |||
| 12 | import codecs |
||
| 13 | import collections |
||
| 14 | import os |
||
| 15 | import re |
||
| 16 | import time |
||
| 17 | from string import punctuation, whitespace |
||
| 18 | |||
| 19 | from sopel import formatting, module, tools |
||
| 20 | from sopel.config.types import (FilenameAttribute, StaticSection, |
||
| 21 | ValidatedAttribute) |
||
| 22 | from sopel.modules.url import find_title |
||
| 23 | |||
| 24 | |||
| 25 | UNTITLED_MEETING = "Untitled meeting" |
||
| 26 | |||
| 27 | |||
| 28 | class MeetbotSection(StaticSection): |
||
| 29 | """Configuration file section definition""" |
||
| 30 | |||
| 31 | meeting_log_path = FilenameAttribute( |
||
| 32 | "meeting_log_path", directory=True, default="~/www/meetings" |
||
| 33 | ) |
||
| 34 | """Path to meeting logs storage directory |
||
| 35 | |||
| 36 | This should be an absolute path, accessible on a webserver.""" |
||
| 37 | |||
| 38 | meeting_log_baseurl = ValidatedAttribute( |
||
| 39 | "meeting_log_baseurl", default="http://localhost/~sopel/meetings" |
||
| 40 | ) |
||
| 41 | """Base URL for the meeting logs directory""" |
||
| 42 | |||
| 43 | |||
| 44 | def configure(config): |
||
| 45 | """ |
||
| 46 | | name | example | purpose | |
||
| 47 | | ---- | ------- | ------- | |
||
| 48 | | meeting\\_log\\_path | /home/sopel/www/meetings | Path to meeting logs storage directory (should be an absolute path, accessible on a webserver) | |
||
| 49 | | meeting\\_log\\_baseurl | http://example.com/~sopel/meetings | Base URL for the meeting logs directory | |
||
| 50 | """ |
||
| 51 | config.define_section("meetbot", MeetbotSection) |
||
| 52 | config.meetbot.configure_setting( |
||
| 53 | "meeting_log_path", "Enter the directory to store logs in." |
||
| 54 | ) |
||
| 55 | config.meetbot.configure_setting( |
||
| 56 | "meeting_log_baseurl", "Enter the base URL for the meeting logs." |
||
| 57 | ) |
||
| 58 | |||
| 59 | |||
| 60 | def setup(bot): |
||
| 61 | bot.config.define_section("meetbot", MeetbotSection) |
||
| 62 | |||
| 63 | |||
| 64 | meetings_dict = collections.defaultdict(dict) # Saves metadata about currently running meetings |
||
| 65 | """ |
||
| 66 | meetings_dict is a 2D dict. |
||
| 67 | |||
| 68 | Each meeting should have: |
||
| 69 | channel |
||
| 70 | time of start |
||
| 71 | head (can stop the meeting, plus all abilities of chairs) |
||
| 72 | chairs (can add infolines to the logs) |
||
| 73 | title |
||
| 74 | current subject |
||
| 75 | comments (what people who aren't voiced want to add) |
||
| 76 | |||
| 77 | Using channel as the meeting ID as there can't be more than one meeting in a |
||
| 78 | channel at the same time. |
||
| 79 | """ |
||
| 80 | |||
| 81 | # To be defined on meeting start as part of sanity checks, used by logging |
||
| 82 | # functions so we don't have to pass them bot |
||
| 83 | meeting_log_path = "" |
||
| 84 | meeting_log_baseurl = "" |
||
| 85 | |||
| 86 | # A dict of channels to the actions that have been created in them. This way |
||
| 87 | # we can have .listactions spit them back out later on. |
||
| 88 | meeting_actions = {} |
||
| 89 | |||
| 90 | |||
| 91 | # Get the logfile name for the meeting in the requested channel |
||
| 92 | # Used by all logging functions and web path |
||
| 93 | def figure_logfile_name(channel): |
||
| 94 | if meetings_dict[channel]["title"] == UNTITLED_MEETING: |
||
| 95 | name = "untitled" |
||
| 96 | else: |
||
| 97 | name = meetings_dict[channel]["title"] |
||
| 98 | # Real simple sluggifying. |
||
| 99 | # May not handle unicode or unprintables well. Close enough. |
||
| 100 | for character in punctuation + whitespace: |
||
| 101 | name = name.replace(character, "-") |
||
| 102 | name = name.strip("-") |
||
| 103 | timestring = time.strftime( |
||
| 104 | "%Y-%m-%d-%H:%M", time.gmtime(meetings_dict[channel]["start"]) |
||
| 105 | ) |
||
| 106 | filename = timestring + "_" + name |
||
| 107 | return filename |
||
| 108 | |||
| 109 | |||
| 110 | # Start HTML log |
||
| 111 | def log_html_start(channel): |
||
| 112 | logfile_filename = os.path.join( |
||
| 113 | meeting_log_path + channel, figure_logfile_name(channel) + ".html" |
||
| 114 | ) |
||
| 115 | logfile = codecs.open(logfile_filename, "a", encoding="utf-8") |
||
| 116 | timestring = time.strftime( |
||
| 117 | "%Y-%m-%d %H:%M", time.gmtime(meetings_dict[channel]["start"]) |
||
| 118 | ) |
||
| 119 | title = "%s at %s, %s" % (meetings_dict[channel]["title"], channel, timestring) |
||
| 120 | logfile.write( |
||
| 121 | ( |
||
| 122 | "<!doctype html><html><head><meta charset='utf-8'>\n" |
||
| 123 | "<title>{title}</title>\n</head><body>\n<h1>{title}</h1>\n" |
||
| 124 | ).format(title=title) |
||
| 125 | ) |
||
| 126 | logfile.write( |
||
| 127 | "<h4>Meeting started by %s</h4><ul>\n" % meetings_dict[channel]["head"] |
||
| 128 | ) |
||
| 129 | logfile.close() |
||
| 130 | |||
| 131 | |||
| 132 | # Write a list item in the HTML log |
||
| 133 | def log_html_listitem(item, channel): |
||
| 134 | logfile_filename = os.path.join( |
||
| 135 | meeting_log_path + channel, figure_logfile_name(channel) + ".html" |
||
| 136 | ) |
||
| 137 | logfile = codecs.open(logfile_filename, "a", encoding="utf-8") |
||
| 138 | logfile.write("<li>" + item + "</li>\n") |
||
| 139 | logfile.close() |
||
| 140 | |||
| 141 | |||
| 142 | # End the HTML log |
||
| 143 | def log_html_end(channel): |
||
| 144 | logfile_filename = os.path.join( |
||
| 145 | meeting_log_path + channel, figure_logfile_name(channel) + ".html" |
||
| 146 | ) |
||
| 147 | logfile = codecs.open(logfile_filename, "a", encoding="utf-8") |
||
| 148 | current_time = time.strftime("%H:%M:%S", time.gmtime()) |
||
| 149 | logfile.write("</ul>\n<h4>Meeting ended at %s UTC</h4>\n" % current_time) |
||
| 150 | plainlog_url = meeting_log_baseurl + tools.web.quote( |
||
| 151 | channel + "/" + figure_logfile_name(channel) + ".log" |
||
| 152 | ) |
||
| 153 | logfile.write('<a href="%s">Full log</a>' % plainlog_url) |
||
| 154 | logfile.write("\n</body>\n</html>\n") |
||
| 155 | logfile.close() |
||
| 156 | |||
| 157 | |||
| 158 | # Write a string to the plain text log |
||
| 159 | def log_plain(item, channel): |
||
| 160 | logfile_filename = os.path.join( |
||
| 161 | meeting_log_path + channel, figure_logfile_name(channel) + ".log" |
||
| 162 | ) |
||
| 163 | logfile = codecs.open(logfile_filename, "a", encoding="utf-8") |
||
| 164 | current_time = time.strftime("%H:%M:%S", time.gmtime()) |
||
| 165 | logfile.write("[" + current_time + "] " + item + "\r\n") |
||
| 166 | logfile.close() |
||
| 167 | |||
| 168 | |||
| 169 | # Check if a meeting is currently running |
||
| 170 | def is_meeting_running(channel): |
||
| 171 | try: |
||
| 172 | return meetings_dict[channel]["running"] |
||
| 173 | except KeyError: |
||
| 174 | return False |
||
| 175 | |||
| 176 | |||
| 177 | # Check if nick is a chair or head of the meeting |
||
| 178 | def is_chair(nick, channel): |
||
| 179 | try: |
||
| 180 | return ( |
||
| 181 | nick.lower() == meetings_dict[channel]["head"] or |
||
| 182 | nick.lower() in meetings_dict[channel]["chairs"] |
||
| 183 | ) |
||
| 184 | except KeyError: |
||
| 185 | return False |
||
| 186 | |||
| 187 | |||
| 188 | # Start meeting (also performs all required sanity checks) |
||
| 189 | @module.commands("startmeeting") |
||
| 190 | @module.example(".startmeeting") |
||
| 191 | @module.example(".startmeeting Meeting Title") |
||
| 192 | @module.require_chanmsg("Meetings can only be started in channels") |
||
| 193 | def startmeeting(bot, trigger): |
||
| 194 | """ |
||
| 195 | Start a meeting.\ |
||
| 196 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 197 | """ |
||
| 198 | if is_meeting_running(trigger.sender): |
||
| 199 | bot.say("There is already an active meeting here!") |
||
| 200 | return |
||
| 201 | # Start the meeting |
||
| 202 | meetings_dict[trigger.sender]["start"] = time.time() |
||
| 203 | if not trigger.group(2): |
||
| 204 | meetings_dict[trigger.sender]["title"] = UNTITLED_MEETING |
||
| 205 | else: |
||
| 206 | meetings_dict[trigger.sender]["title"] = trigger.group(2) |
||
| 207 | meetings_dict[trigger.sender]["head"] = trigger.nick.lower() |
||
| 208 | meetings_dict[trigger.sender]["running"] = True |
||
| 209 | meetings_dict[trigger.sender]["comments"] = [] |
||
| 210 | |||
| 211 | # Set up paths and URLs |
||
| 212 | global meeting_log_path |
||
| 213 | meeting_log_path = bot.config.meetbot.meeting_log_path |
||
| 214 | if not meeting_log_path.endswith(os.sep): |
||
| 215 | meeting_log_path += os.sep |
||
| 216 | |||
| 217 | global meeting_log_baseurl |
||
| 218 | meeting_log_baseurl = bot.config.meetbot.meeting_log_baseurl |
||
| 219 | if not meeting_log_baseurl.endswith("/"): |
||
| 220 | meeting_log_baseurl = meeting_log_baseurl + "/" |
||
| 221 | |||
| 222 | channel_log_path = meeting_log_path + trigger.sender |
||
| 223 | if not os.path.isdir(channel_log_path): |
||
| 224 | try: |
||
| 225 | os.makedirs(channel_log_path) |
||
| 226 | except Exception: # TODO: Be specific |
||
| 227 | bot.say( |
||
| 228 | "Meeting not started: Couldn't create log directory for this channel" |
||
| 229 | ) |
||
| 230 | meetings_dict[trigger.sender] = collections.defaultdict(dict) |
||
| 231 | raise |
||
| 232 | # Okay, meeting started! |
||
| 233 | log_plain("Meeting started by " + trigger.nick.lower(), trigger.sender) |
||
| 234 | log_html_start(trigger.sender) |
||
| 235 | meeting_actions[trigger.sender] = [] |
||
| 236 | bot.say( |
||
| 237 | ( |
||
| 238 | formatting.bold("Meeting started!") + " use {0}action, {0}agreed, " |
||
| 239 | "{0}info, {0}link, {0}chairs, {0}subject, and {0}comments to " |
||
| 240 | "control the meeting. To end the meeting, type {0}endmeeting" |
||
| 241 | ).format(bot.config.core.help_prefix) |
||
| 242 | ) |
||
| 243 | bot.say( |
||
| 244 | ( |
||
| 245 | "Users without speaking permission can participate by sending me " |
||
| 246 | "a PM with `{0}comment {1}` followed by their comment." |
||
| 247 | ).format(bot.config.core.help_prefix, trigger.sender) |
||
| 248 | ) |
||
| 249 | |||
| 250 | |||
| 251 | # Change the current subject (will appear as <h3> in the HTML log) |
||
| 252 | @module.commands("subject") |
||
| 253 | @module.example(".subject roll call") |
||
| 254 | def meetingsubject(bot, trigger): |
||
| 255 | """ |
||
| 256 | Change the meeting subject.\ |
||
| 257 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 258 | """ |
||
| 259 | if not is_meeting_running(trigger.sender): |
||
| 260 | bot.say("There is no active meeting") |
||
| 261 | return |
||
| 262 | if not trigger.group(2): |
||
| 263 | bot.say("What is the subject?") |
||
| 264 | return |
||
| 265 | if not is_chair(trigger.nick, trigger.sender): |
||
| 266 | bot.say("Only meeting head or chairs can do that") |
||
| 267 | return |
||
| 268 | meetings_dict[trigger.sender]["current_subject"] = trigger.group(2) |
||
| 269 | logfile_filename = os.path.join( |
||
| 270 | meeting_log_path + trigger.sender, figure_logfile_name(trigger.sender) + ".html" |
||
| 271 | ) |
||
| 272 | logfile = codecs.open(logfile_filename, "a", encoding="utf-8") |
||
| 273 | logfile.write("</ul><h3>" + trigger.group(2) + "</h3><ul>") |
||
| 274 | logfile.close() |
||
| 275 | log_plain( |
||
| 276 | "Current subject: {} (set by {})".format(trigger.group(2), trigger.nick), |
||
| 277 | trigger.sender, |
||
| 278 | ) |
||
| 279 | bot.say(formatting.bold("Current subject:") + " " + trigger.group(2)) |
||
| 280 | |||
| 281 | |||
| 282 | # End the meeting |
||
| 283 | @module.commands("endmeeting") |
||
| 284 | @module.example(".endmeeting") |
||
| 285 | def endmeeting(bot, trigger): |
||
| 286 | """ |
||
| 287 | End a meeting.\ |
||
| 288 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 289 | """ |
||
| 290 | if not is_meeting_running(trigger.sender): |
||
| 291 | bot.say("There is no active meeting") |
||
| 292 | return |
||
| 293 | if not is_chair(trigger.nick, trigger.sender): |
||
| 294 | bot.say("Only meeting head or chairs can do that") |
||
| 295 | return |
||
| 296 | meeting_length = time.time() - meetings_dict[trigger.sender]["start"] |
||
| 297 | bot.say( |
||
| 298 | formatting.bold("Meeting ended!") + |
||
| 299 | " Total meeting length %d minutes" % (meeting_length // 60) |
||
| 300 | ) |
||
| 301 | log_html_end(trigger.sender) |
||
| 302 | htmllog_url = meeting_log_baseurl + tools.web.quote( |
||
| 303 | trigger.sender + "/" + figure_logfile_name(trigger.sender) + ".html" |
||
| 304 | ) |
||
| 305 | log_plain( |
||
| 306 | "Meeting ended by %s. Total meeting length: %d minutes" |
||
| 307 | % (trigger.nick, meeting_length // 60), |
||
| 308 | trigger.sender, |
||
| 309 | ) |
||
| 310 | bot.say("Meeting minutes: " + htmllog_url) |
||
| 311 | meetings_dict[trigger.sender] = collections.defaultdict(dict) |
||
| 312 | del meeting_actions[trigger.sender] |
||
| 313 | |||
| 314 | |||
| 315 | # Set meeting chairs (people who can control the meeting) |
||
| 316 | @module.commands("chairs") |
||
| 317 | @module.example(".chairs Tyrope Jason elad") |
||
| 318 | def chairs(bot, trigger): |
||
| 319 | """ |
||
| 320 | Set the meeting chairs.\ |
||
| 321 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 322 | """ |
||
| 323 | if not is_meeting_running(trigger.sender): |
||
| 324 | bot.say("There is no active meeting") |
||
| 325 | return |
||
| 326 | if not trigger.group(2): |
||
| 327 | bot.say( |
||
| 328 | "Who are the chairs? Try `{}chairs Alice Bob Cindy`".format( |
||
| 329 | bot.config.core.help_prefix |
||
| 330 | ) |
||
| 331 | ) |
||
| 332 | return |
||
| 333 | if trigger.nick.lower() == meetings_dict[trigger.sender]["head"]: |
||
| 334 | meetings_dict[trigger.sender]["chairs"] = trigger.group(2).lower().split(" ") |
||
| 335 | chairs_readable = trigger.group(2).lower().replace(" ", ", ") |
||
| 336 | log_plain("Meeting chairs are: " + chairs_readable, trigger.sender) |
||
| 337 | log_html_listitem( |
||
| 338 | "<span style='font-weight: bold'>Meeting chairs are:</span> %s" |
||
| 339 | % chairs_readable, |
||
| 340 | trigger.sender, |
||
| 341 | ) |
||
| 342 | bot.say(formatting.bold("Meeting chairs are:") + " " + chairs_readable) |
||
| 343 | else: |
||
| 344 | bot.say("Only meeting head can set chairs") |
||
| 345 | |||
| 346 | |||
| 347 | # Log action item in the HTML log |
||
| 348 | @module.commands("action") |
||
| 349 | @module.example(".action elad will develop a meetbot") |
||
| 350 | def meetingaction(bot, trigger): |
||
| 351 | """ |
||
| 352 | Log an action in the meeting log.\ |
||
| 353 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 354 | """ |
||
| 355 | if not is_meeting_running(trigger.sender): |
||
| 356 | bot.say("There is no active meeting") |
||
| 357 | return |
||
| 358 | if not trigger.group(2): |
||
| 359 | bot.say( |
||
| 360 | "Try `{}action Bob will do something`".format(bot.config.core.help_prefix) |
||
| 361 | ) |
||
| 362 | return |
||
| 363 | if not is_chair(trigger.nick, trigger.sender): |
||
| 364 | bot.say("Only meeting head or chairs can do that") |
||
| 365 | return |
||
| 366 | log_plain("ACTION: " + trigger.group(2), trigger.sender) |
||
| 367 | log_html_listitem( |
||
| 368 | "<span style='font-weight: bold'>Action: </span>" + trigger.group(2), |
||
| 369 | trigger.sender, |
||
| 370 | ) |
||
| 371 | meeting_actions[trigger.sender].append(trigger.group(2)) |
||
| 372 | bot.say(formatting.bold("ACTION:") + " " + trigger.group(2)) |
||
| 373 | |||
| 374 | |||
| 375 | @module.commands("listactions") |
||
| 376 | @module.example(".listactions") |
||
| 377 | def listactions(bot, trigger): |
||
| 378 | if not is_meeting_running(trigger.sender): |
||
| 379 | bot.say("There is no active meeting") |
||
| 380 | return |
||
| 381 | for action in meeting_actions[trigger.sender]: |
||
| 382 | bot.say(formatting.bold("ACTION:") + " " + action) |
||
| 383 | |||
| 384 | |||
| 385 | # Log agreed item in the HTML log |
||
| 386 | View Code Duplication | @module.commands("agreed") |
|
|
|
|||
| 387 | @module.example(".agreed Bowties are cool") |
||
| 388 | def meetingagreed(bot, trigger): |
||
| 389 | """ |
||
| 390 | Log an agreement in the meeting log.\ |
||
| 391 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 392 | """ |
||
| 393 | if not is_meeting_running(trigger.sender): |
||
| 394 | bot.say("There is no active meeting") |
||
| 395 | return |
||
| 396 | if not trigger.group(2): |
||
| 397 | bot.say("Try `{}agreed Bowties are cool`".format(bot.config.core.help_prefix)) |
||
| 398 | return |
||
| 399 | if not is_chair(trigger.nick, trigger.sender): |
||
| 400 | bot.say("Only meeting head or chairs can do that") |
||
| 401 | return |
||
| 402 | log_plain("AGREED: " + trigger.group(2), trigger.sender) |
||
| 403 | log_html_listitem( |
||
| 404 | "<span style='font-weight: bold'>Agreed: </span>" + trigger.group(2), |
||
| 405 | trigger.sender, |
||
| 406 | ) |
||
| 407 | bot.say(formatting.bold("AGREED:") + " " + trigger.group(2)) |
||
| 408 | |||
| 409 | |||
| 410 | # Log link item in the HTML log |
||
| 411 | @module.commands("link") |
||
| 412 | @module.example(".link http://example.com") |
||
| 413 | def meetinglink(bot, trigger): |
||
| 414 | """ |
||
| 415 | Log a link in the meeing log.\ |
||
| 416 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 417 | """ |
||
| 418 | if not is_meeting_running(trigger.sender): |
||
| 419 | bot.say("There is no active meeting") |
||
| 420 | return |
||
| 421 | if not trigger.group(2): |
||
| 422 | bot.say( |
||
| 423 | "Try `{}link https://relevant-website.example/`".format( |
||
| 424 | bot.config.core.help_prefix |
||
| 425 | ) |
||
| 426 | ) |
||
| 427 | return |
||
| 428 | if not is_chair(trigger.nick, trigger.sender): |
||
| 429 | bot.say("Only meeting head or chairs can do that") |
||
| 430 | return |
||
| 431 | link = trigger.group(2) |
||
| 432 | if not link.startswith("http"): |
||
| 433 | link = "http://" + link |
||
| 434 | try: |
||
| 435 | title = find_title(link, verify=bot.config.core.verify_ssl) |
||
| 436 | except Exception: # TODO: Be specific |
||
| 437 | title = "" |
||
| 438 | log_plain("LINK: %s [%s]" % (link, title), trigger.sender) |
||
| 439 | log_html_listitem('<a href="%s">%s</a>' % (link, title), trigger.sender) |
||
| 440 | bot.say(formatting.bold("LINK:") + " " + link) |
||
| 441 | |||
| 442 | |||
| 443 | # Log informational item in the HTML log |
||
| 444 | View Code Duplication | @module.commands("info") |
|
| 445 | @module.example(".info all board members present") |
||
| 446 | def meetinginfo(bot, trigger): |
||
| 447 | """ |
||
| 448 | Log an informational item in the meeting log.\ |
||
| 449 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 450 | """ |
||
| 451 | if not is_meeting_running(trigger.sender): |
||
| 452 | bot.say("There is no active meeting") |
||
| 453 | return |
||
| 454 | if not trigger.group(2): |
||
| 455 | bot.say( |
||
| 456 | "Try `{}info some informative thing`".format(bot.config.core.help_prefix) |
||
| 457 | ) |
||
| 458 | return |
||
| 459 | if not is_chair(trigger.nick, trigger.sender): |
||
| 460 | bot.say("Only meeting head or chairs can do that") |
||
| 461 | return |
||
| 462 | log_plain("INFO: " + trigger.group(2), trigger.sender) |
||
| 463 | log_html_listitem(trigger.group(2), trigger.sender) |
||
| 464 | bot.say(formatting.bold("INFO:") + " " + trigger.group(2)) |
||
| 465 | |||
| 466 | |||
| 467 | # called for every single message |
||
| 468 | # Will log to plain text only |
||
| 469 | @module.rule("(.*)") |
||
| 470 | @module.priority("low") |
||
| 471 | def log_meeting(bot, trigger): |
||
| 472 | if not is_meeting_running(trigger.sender): |
||
| 473 | return |
||
| 474 | |||
| 475 | # Handle live prefix changes with cached regex |
||
| 476 | if ( |
||
| 477 | "meetbot_prefix" not in bot.memory or |
||
| 478 | bot.memory["meetbot_prefix"] != bot.config.core.prefix |
||
| 479 | ): |
||
| 480 | commands = [ |
||
| 481 | "startmeeting", |
||
| 482 | "subject", |
||
| 483 | "endmeeting", |
||
| 484 | "chairs", |
||
| 485 | "action", |
||
| 486 | "listactions", |
||
| 487 | "agreed", |
||
| 488 | "link", |
||
| 489 | "info", |
||
| 490 | "comments", |
||
| 491 | ] |
||
| 492 | |||
| 493 | bot.memory["meetbot_command_regex"] = re.compile( |
||
| 494 | "{}({})( |$)".format(bot.config.core.prefix, "|".join(commands)) |
||
| 495 | ) |
||
| 496 | bot.memory["meetbot_prefix"] = bot.config.core.prefix |
||
| 497 | |||
| 498 | if bot.memory["meetbot_command_regex"].match(trigger): |
||
| 499 | return |
||
| 500 | log_plain("<" + trigger.nick + "> " + trigger, trigger.sender) |
||
| 501 | |||
| 502 | |||
| 503 | @module.commands("comment") |
||
| 504 | @module.require_privmsg() |
||
| 505 | def take_comment(bot, trigger): |
||
| 506 | """ |
||
| 507 | Log a comment, to be shown with other comments when a chair uses .comments. |
||
| 508 | Intended to allow commentary from those outside the primary group of people |
||
| 509 | in the meeting. |
||
| 510 | |||
| 511 | Used in private message only, as `.comment <#channel> <comment to add>` |
||
| 512 | |||
| 513 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 514 | """ |
||
| 515 | if not trigger.group(4): # <2 arguements were given |
||
| 516 | bot.say( |
||
| 517 | "Usage: {}comment <#channel> <comment to add>".format( |
||
| 518 | bot.config.core.help_prefix |
||
| 519 | ) |
||
| 520 | ) |
||
| 521 | return |
||
| 522 | |||
| 523 | target, message = trigger.group(2).split(None, 1) |
||
| 524 | target = tools.Identifier(target) |
||
| 525 | if not is_meeting_running(target): |
||
| 526 | bot.say("There is no active meeting in that channel.") |
||
| 527 | else: |
||
| 528 | meetings_dict[trigger.group(3)]["comments"].append((trigger.nick, message)) |
||
| 529 | bot.say( |
||
| 530 | "Your comment has been recorded. It will be shown when the " |
||
| 531 | "chairs tell me to show the comments." |
||
| 532 | ) |
||
| 533 | bot.say( |
||
| 534 | "A new comment has been recorded.", meetings_dict[trigger.group(3)]["head"] |
||
| 535 | ) |
||
| 536 | |||
| 537 | |||
| 538 | @module.commands("comments") |
||
| 539 | def show_comments(bot, trigger): |
||
| 540 | """ |
||
| 541 | Show the comments that have been logged for this meeting with .comment. |
||
| 542 | |||
| 543 | See [meetbot module usage]({% link _usage/meetbot-module.md %}) |
||
| 544 | """ |
||
| 545 | if not is_meeting_running(trigger.sender): |
||
| 546 | return |
||
| 547 | if not is_chair(trigger.nick, trigger.sender): |
||
| 548 | bot.say("Only meeting head or chairs can do that") |
||
| 549 | return |
||
| 550 | comments = meetings_dict[trigger.sender]["comments"] |
||
| 551 | if comments: |
||
| 552 | msg = "The following comments were made:" |
||
| 553 | bot.say(msg) |
||
| 554 | log_plain("<%s> %s" % (bot.nick, msg), trigger.sender) |
||
| 555 | for comment in comments: |
||
| 556 | msg = "<%s> %s" % comment |
||
| 557 | bot.say(msg) |
||
| 558 | log_plain("<%s> %s" % (bot.nick, msg), trigger.sender) |
||
| 559 | meetings_dict[trigger.sender]["comments"] = [] |
||
| 560 | else: |
||
| 561 | bot.say("No comments have been recorded") |
||
| 562 |