Ejemplo n.º 1
0
    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"]))
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
 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']))
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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()