def edit(self, event, details): if event.msg.channel.id != self.config.event_channel: return parts = details.split("|") if len(parts) < 3: event.msg.reply( "<@{}> It seems you're missing parts, the syntax for this command is `+edit <trello link> | <section (destination or info)> | <new info>`".format( event.author.id)) return if len(parts) > 3: parts[2] = "|".join(parts[2:]) link = parts[0] section = parts[1].strip(" ").lower() info = parts[2] trello_info = TrelloUtils.getCardInfo(event, link) if trello_info is None: event.msg.reply("I can't even fetch info for that, you sure you reported that one?") return if not trello_info["id"] in self.reported_cards.keys(): event.msg.reply("I don't have a report for that card, how do you expect me to edit a non existing thing?") return report_info = self.reported_cards[trello_info["id"]] if report_info["author_id"] != str(event.author.id): event.msg.reply( "I think there's been a case of mistaken identity here, this report was made by {} and it looks like you are {}".format( self.participants[str(report_info["author_id"])], str(event.author))) return dmessage = event.guild.channels[self.config.event_channel].get_message(report_info["message_id"]) content = dmessage.content new_message = "" lines = content.splitlines() if section == "destination": new_message = "\n".join(lines[:2]) while not lines[2].startswith("**Submitted by**:"): lines.pop(2) new_message += "\n**Destination**: {}\n{}".format(sanitize.S(info, escape_codeblocks=True), "\n".join(lines[2:])) elif section == "info": count = 0 while not lines[count].startswith("**Detailed info**"): count += 1 new_message = "\n".join(lines[:count]) new_message += "\n**Detailed info**: {}\n{}".format(sanitize.S(info, escape_codeblocks=True), "\n".join(lines[-1:])) else: event.msg.reply("Unknown section") return if len(new_message) > 2000: event.msg.reply( "<@{}> Sorry, but would make the report too long for me to process, would mind removing {} characters? Then everything should be fine again.".format( event.author.id, len(new_message) - 2000)) return print(new_message) dmessage.edit(new_message) event.channel.send_message("<@{}>, your report has been updated!".format(event.author.id)) log_to_bot_log(self.bot, ":pencil: {} has updated the {} of their submission for <https://trello.com/c/{}>".format( str(event.author), section.lower(), trello_info["shortLink"]))
def revoke(self, event, report): """Revoke a submission""" if event.msg.channel.id != self.config.event_channel: return trello_info = TrelloUtils.getCardInfo(event, report) if trello_info is None: # invalid card event.msg.reply("I can't even fetch info for that, you sure you reported that one?") return if not trello_info["id"] in self.reported_cards.keys(): # not reported yet event.msg.reply("I don't have a report for that card, how do you expect me to edit a non existing thing?") return report_info = self.reported_cards[trello_info["id"]] if report_info["author_id"] != str(event.author.id): # someone else reported event.msg.reply( "I think there's been a case of mistaken identity here, this report was made by {} and it looks like you are {}".format( self.participants[str(report_info["author_id"])], str(event.author))) return # delete message and entry event.msg.channel.get_message(report_info["message_id"]).delete() del self.reported_cards[trello_info["id"]] event.msg.reply(":warning: Your submission has been nuked <@{}>!".format(event.author.id)) log_to_bot_log(self.bot, f":outbox_tray: {event.author} (``{event.author.id}``) has revoked <https://trello.com/c/{trello_info['shortLink']}>") self.save_event_stats()
def calc_event_stats(self): info = {"all": {"Approved": 0, "Denied": 0, "Submitted": 0}} total = dict() lists = dict() vs = dict() for id, board in self.config.boards.items(): info[id] = {"Approved": 0, "Denied": 0, "Submitted": 0} lists[id] = dict() for l in board["lists"]: lists[id][l] = 0 total[board["name"]] = 0 for id in self.participants.keys(): info[id] = {"Approved": 0, "Denied": 0, "Submitted": 0} vs[id] = 0 for report in self.reported_cards.values(): info["all"][report["status"]] += 1 info[report["board"]][report["status"]] += 1 info[report["author_id"]][report["status"]] += 1 total[self.config.boards[report["board"]]["name"]] += 1 lists[report["board"]][report["list"]] += 1 vs[report["author_id"]] += 1 info["total"] = total #transform lists to use their names rather then IDs info["lists"] = dict() info["vs"] = vs for board_id, content in lists.items(): info["lists"][board_id] = dict() for k, v in content.items(): list_info = TrelloUtils.getListInfo(k) info["lists"][board_id][list_info["name"]] = v return info
def remove_report(self, event, report): trello_info = TrelloUtils.getCardInfo(event, report) if trello_info is None: event.msg.reply("I can't even fetch info for that, you sure someone reported that one?") return if not trello_info["id"] in self.reported_cards.keys(): event.msg.reply( "I don't have a report for that card, how do you expect me to edit a non existing thing?") return report_info = self.reported_cards[trello_info["id"]] event.guild.channels[self.config.event_channel].get_message(report_info["message_id"]).delete() del self.reported_cards[trello_info["id"]] event.msg.reply(":warning: Submission has been nuked <@{}>!".format(event.author.id)) log_to_bot_log(self.bot, ":wastebasket: {} has removed <https://trello.com/c/{}>".format(str(event.author), trello_info[ 'shortLink']))
def import_event(self, event, channel_id): if self.status != "Scheduled": event.msg.reply( "I have event data loaded, import aborted to prevent data corruption, please remove/rename the current eventstats file and reboot") return if not channel_id in event.guild.channels.keys(): event.msg.reply("I cannot find a channel with that ID") return # we're importing so no enforced anti duping, collect all for chronological processing event.msg.reply("Starting import...") channel = event.guild.channels[channel_id] messages = [] message_info = dict() for message in channel.messages_iter(chunk_size=100, direction=MessageIterator.Direction.UP, before=event.msg.id): messages.append(message.id) message_info[message.id] = { "author_id": str(message.author.id), "author_name": str(message.author), "content": message.content } messages.sort() print("Collected {} messages for processing".format(len(messages))) def get_url(m): result = re.search("(?P<url>https?://trello.com/c/[^\s]+)", m) return result.group("url") if result is not None else None invalid = 0 dupes = 0 for m_id in messages: link = get_url(message_info[m_id]["content"]) if link is None: state = "Invalid" invalid += 1 self.bot.client.api.channels_messages_reactions_create(int(channel_id), m_id, "❓") else: trello_info = TrelloUtils.getCardInfo(None, link) trello_id = trello_info["id"] if trello_info in (False, None) or trello_info["idBoard"] not in self.config.boards.keys(): state = "Invalid" invalid += 1 self.bot.client.api.channels_messages_reactions_create(int(channel_id), m_id, "❓") elif trello_info['id'] in self.reported_cards.keys(): # already reported state = "Dupe" dupes += 1 if trello_id not in self.dupes.keys(): self.dupes[trello_id] = 1 else: self.dupes[trello_id] += 1 self.bot.client.api.channels_messages_reactions_create(int(channel_id), m_id, "🚫") else: board = self.config.boards[trello_info["idBoard"]] if trello_info["idList"] not in board["lists"] or trello_info["closed"] is True: state = "Invalid" invalid += 1 self.bot.client.api.channels_messages_reactions_create(int(channel_id), m_id, "❓") else: state = "Valid" print("{} - {}".format(m_id, state)) if state == "Valid": self.reported_cards[trello_info['id']] = dict( author_id=message_info[m_id]["author_id"], board=trello_info["idBoard"], list=trello_info["idList"], message_id=m_id, status="Submitted", ) if message_info[m_id]["author_id"] not in self.participants.keys(): self.participants[message_info[m_id]["author_id"]] = message_info[m_id]["author_name"] event.msg.reply(""" Data import complete Imported entries: {} Dupes encountered: {} ({} tickets) Invalid entries skipped: {} """.format(len(self.reported_cards.keys()), dupes, len(self.dupes.keys()), invalid)) # override event channel from import self.config.event_channel = int(channel_id) self.end_event(event)
def template(self, event, submission = None): """Make a new submission""" if self.status != "Started": return # no event going on, pretend nothing happened #noleeks if event.channel.id != self.config.event_channel: # ignore users running this in the wrong channel, also prevents non hunters from submitting return help_message = "<@{}> It seems you're missing parts, the syntax for this command is `+submit <trello link> | <where this ticket should be moved to> | <why it should be moved there and/or new steps>`".format( event.author.id) if submission is None: # no params given, print help info event.msg.reply(help_message) return parts = submission.split("|") if len(parts) < 3: # missing things we need event.msg.reply(help_message) return if len(parts) > 3: # for some reason they used a | in their report, re-assemble it so we don't void things parts[2] = "|".join(parts[2:]) link = parts[0] destination = parts[1] info = parts[2] # fetch the trello info and validate trello_info = TrelloUtils.getCardInfo(event, link) error = None if trello_info is False: # wrong type of link, user already informed, we're done here return if trello_info is None: # no info, non existant card or from a private board error = "<@{}> Unable to fetch info about that card, are you sure it exists? Cause I don't feel like playing hide and seek.".format( event.author.id) elif trello_info["idBoard"] not in self.config.boards.keys(): # not a discord board error = "This card is not from one of the discord bug boards, what do you expect me to do with this?" elif trello_info['id'] in self.reported_cards.keys(): # already reported report = self.reported_cards[trello_info['id']] # hit by sniper? timediv = datetime.utcnow() - datetime.utcfromtimestamp(report["report_time"]) hours, remainder = divmod(int(timediv.total_seconds()), 3600) minutes, seconds = divmod(remainder, 60) error = "<@{}> Looks like {} beat you to the punch. Better luck next time {}".format(event.author.id, self.participants[str( report[ "author_id"])], "SNIPED!" if minutes < 2 else "<:dupebutton:341981924010491904>") if error is None: # all good so far board = self.config.boards[trello_info["idBoard"]] listname = TrelloUtils.getListInfo(trello_info["idList"])["name"] if trello_info["idList"] not in board["lists"]: # this list is not valid for this event error = "<@{}> This card is in the {} list instead of an event list, thanks for the submission but no thanks.".format( event.author.id, listname) elif trello_info["closed"] is True: # archived card error = "<@{}> _cough cough_ that card has been archived and collected way too much dust for me to do anything with it".format( event.author.id) if error is not None: # card failed one of the checks, inform user and terminate processing event.msg.reply(error) return else: # valid submission, processing... message = """ **Board**: {} {} **Source list**: {} **Destination**: {} **Submitted by**: {} **Detailed info**: {} **Trello link**: {}""".format(board["name"], board["emoji"], listname, destination, str(event.author.id), info, trello_info["shortUrl"]) # sanitze the entire thing, no pinging or breaking codeblocks message = sanitize.S(message, escape_codeblocks=True) if len(message) > 2000: # discord only accepts essays up to 2000 characters event.msg.reply( "<@{}> Sorry, but that report is too long for me to process, would you mind removing {} characters? Then everything should be fine again.".format( event.author.id, len(message) - 2000)) return # send the submission and clean input dmessage = event.msg.reply(message) event.msg.delete() # add to tracking self.reported_cards[trello_info['id']] = dict( author_id=str(event.author.id), board=trello_info["idBoard"], list=trello_info["idList"], message_id=dmessage.id, status="Submitted", report_time=datetime.utcnow().timestamp() ) if not str(event.author.id) in self.participants.keys(): # this person has not submitted anything yet, special message self.participants[str(event.author.id)] = str(event.author) event.msg.reply( "<@{}> Achievement get! Successfully submitted your first event entry :tada:".format(event.author.id)) else: event.msg.reply("<@{}> Thanks for your submission!".format(event.author.id)) log_to_bot_log(self.bot, f":inbox_tray: {event.author} (``{event.author.id}``) has submitted <https://trello.com/c/{trello_info['shortLink']}>") self.save_event_stats()