Esempio n. 1
0
 def edit_comment(posted: praw.models.Comment) -> None:
     """edits comment to reflect all users pinged"""
     body: str = "\n\n".join([f"Pinged members of {group} group.", "---",
         self._footer([("Subscribe to this group", f"Add yourself to group {group}", "addtogroup", f"{group}"),
                       ("Unsubscribe from this group", f"Unsubscribe from group {group}", "unsubscribe", f"{group}"),
                       ("Unsubscribe from all groups", f"Unsubscribe from all groups", "unsubscribe", "")])])
     posted.edit(body)
Esempio n. 2
0
def make_reply(c: praw.models.Comment) -> None:
    """
    Post a reply to the comment c with BOT_OUTPUT
    """
    # Post a reply if possible
    try:
        print("Posting Reply...")
        c.reply(BOT_OUTPUT)
        print("Reply Made")

    # If reddit returns an error
    except Exception as e:
        print("Reddit returned an error, no reply was made.")
Esempio n. 3
0
 def handle_mention(self, mention: praw.models.Comment) -> None:
     """handles parsing and reply"""
     self.logger.debug("Found potential mention")
     split: List[str] = mention.body.lower().split()
     for trigger, ideology in self.ideologies.items():
         if trigger in split:
             self.logger.debug("ideology request found in %s", str(mention))
             try:
                 mention.reply(ideology.generate())
                 return
             except:
                 self.logger.error("Reply failed to comment: %s",
                                   str(mention))
Esempio n. 4
0
    def getaliases(self, comment: praw.models.Comment, subcommands: list,
                   config: dict):
        if self._botdb.ignore_user(comment.author.name):
            log.info("Ignoring comment by {}".format(comment.author.name))
            return

        aliases = self._botdb.aliases()
        response = 'Current aliases:\n\n'
        for name, alias in sorted(aliases, key=lambda g: g[1]):
            response += ' * {} = {}\n'.format(alias, name)

        log.info('Responding to getalaises request with {} aliases'.format(
            len(aliases)))
        comment.reply(response)
Esempio n. 5
0
    def removalRequest(self, comment: praw.models.Comment, subcommands: list,
                       config: dict):
        # if self._botdb.ignore_user(comment.author.name):
        #     log.info("Ignoring comment by {}".format(comment.author.name))
        #     return

        # for now removals are limited to admins
        if not self._botdb.is_admin(comment.author.name):
            log.info('got remove command from non admin {}, ignoring.'.format(
                comment.author.name))
            return

        if comment.is_root:
            log.error(
                'removal requested on top-level comment {}, ignoring'.format(
                    comment.id))
            return

        botmessage: praw.models.Comment = comment.parent()
        try:
            # delete the post
            botmessage.delete()
            # attempt to unmark the parent as read
            if not botmessage.is_root:
                self._botdb.remove_comment(botmessage.parent)
        except boardgamegeek.exceptions.BoardGameGeekError as e:
            log.error('Error deleting comment {} by {}'.format(
                original.id, original.author.name))
        return
Esempio n. 6
0
    def _process_comment(self, comment: praw.models.Comment):
        if comment.is_root or \
           comment.author.name.lower().endswith("_bot") or \
           comment.parent().author.name != config.username:
            return

        for reg in self.regexes:
            matches = reg.search(comment.body.lower())
            if not matches:
                continue

            cmd = matches.group()
            attrname = cmd.split(" ")[0][1:]

            if not hasattr(self, attrname):
                continue

            logging.info("%s: %s" % (comment.author.name, cmd))

            try:
                sess = self.Session()
                getattr(self, attrname)(sess, comment, *matches.groups())
            except Exception as e:
                logging.error(e)
                sess.rollback()
            else:
                sess.commit()
            finally:
                sess.close()
Esempio n. 7
0
    def write_comment(self, response: Response,
                      comment: praw.models.Comment) -> None:
        """log comment and writes to file"""
        with open('replied_comments.txt', 'a') as file:
            file.write(str(comment))
            file.write(' ')

        if not random.randint(0, 10):
            self.logger.debug("Comment lucky, posting")
            try:
                comment.reply(str(response))
            except praw.exceptions.APIException:
                self.logger.exception()
            else:
                self.logger.debug("Comment posted")
        else:
            self.logger.debug("Comment unlucky, not posted")
Esempio n. 8
0
    def getInfo(self,
                comment: praw.models.Comment,
                subcommands: list,
                config: dict,
                replyTo=None):
        '''Reply to comment with game information. If replyTo is given reply to original else
        reply to given comment.'''
        if self._botdb.ignore_user(comment.author.name):
            log.info("Ignoring comment by {}".format(comment.author.name))
            return

        mode = None
        if len(subcommands) > 0 and subcommands[0].lower(
        ) in self.DISPLAY_MODES:
            mode = subcommands[0].lower()
        else:
            mode = self.DEFAULT_DISPLAY_MODE
        columns = subcommands[1:] if mode == 'tabular' else None

        sort = self.DEFAULT_SORT
        if self.NO_SORT_KEYWORD in subcommands:
            sort = None
        else:
            for sort_type in self.SORT_FUNCTIONS.keys():
                if sort_type in subcommands:
                    sort = sort_type
                    break

        footer = '\n' + config['footer'] if 'footer' in config else ''

        bolded = self._getBoldedEntries(comment)
        response = None
        if bolded:
            response = self._getInfoResponseBody(comment, bolded, mode,
                                                 columns, sort)
        if response:
            if replyTo:
                replyTo.reply(response + footer)
            else:
                comment.reply(response + footer)
            log.info('Replied to info request for comment {}'.format(
                comment.id))
        else:
            log.warn('Did not find anything to reply to in comment {}'.format(
                comment.id))
Esempio n. 9
0
    def alias(self, comment: praw.models.Comment, subcommands: list,
              config: dict):
        '''add an alias to the database.'''
        if not self._botdb.is_admin(comment.author.name):
            log.info('got alias command from non admin {}, ignoring.'.format(
                comment.author.name))
            return

        response = 'executing alias command.\n\n'
        # TODO: use bold fn
        for match in re.findall('\*\*([^\*]+)\*\*=\*\*([^\*]+)\*\*',
                                comment.body):
            mess = 'Adding alias to database: "{}" = "{}"'.format(
                match[0], match[1])
            log.info(mess)
            response += mess + '\n\n'
            self._botdb.add_alias(match[0], match[1])

        comment.reply(response)
Esempio n. 10
0
def form_comment(imgur: ImgurClient, comment: praw.models.Comment,
                 formulae: List[str], contexts: List[str],
                 hypercontexts: List[str]) -> None:
    """Makes and posts an appropriate reply to a comment

    :param imgur: The client to use for uploading
    :type imgur: ImgurClient
    :param comment: The comment to process and respond to
    :type comment: praw.models.Comment
    :param formulae: A list of formulae to render
    :type formulae: List[str]
    :param contexts: A list of contexts to use
    :type contexts: List[str]
    :param hypercontexts: A list of contexts to use for the hyperlinks
    :type hypercontexts: List[str]
    """
    reply = ""
    try:
        for index, formula in enumerate(formulae):
            image_generation(formula).save("test.png")
            url = get_imgur(imgur, "test.png")
            try:
                if len(contexts[index]) == 0:
                    raise ValueError("Context is too short")
                reply += f"{contexts[index]}\n\n"
            except Exception:
                pass
            try:
                if len(hypercontexts[index]) == 0:
                    raise ValueError("Hypercontext is too short")
                reply += f"[{hypercontexts[index]}]({url})\n\n"
            except Exception:
                reply += f"[Click here for LaTeX render]({url})\n\n"
        reply += "^(This bot was made by) [u/Oryv](https://www.reddit.com/u/Oryv) ^(for /r/HomeworkHelp)"
        print("Replied to comment")
        comment.reply(reply)
        comment.mark_read()

    # This covers improper LaTeX syntax
    except RuntimeError as e:
        print(f"Something went wrong: {e}")
        comment.mark_read()

    # This covers being banned from a subreddit
    except requests.exceptions.HTTPError as e:
        print(f"Something went wrong: {e}")
        comment.mark_read()

    except Exception as e:
        print(f"Something went wrong: {e}")
Esempio n. 11
0
def default_mention_reply(message: praw.models.Comment, choices: List[str]) -> dict:
    """Post a random selection of choices as a reply to a message.

    Args:
        message (Comment): The message to respond to.
        choices (List[str]): The reply body options to choose from.

    Returns:
        dict: The posted comment metadata as a datastore-able dict.
    """
    body = f"{random.choice(choices)}"
    comment = message.reply(body)
    return _build_obj(comment)
Esempio n. 12
0
def handleNewComment(comment: praw.models.Comment):
    discord_codes = extractURLS(comment, discord_invite_pattern)
    print(discord_codes)
    anyIllegal = False
    for code in discord_codes:
        data = getInviteData(code)
        print(data)
        features = data["guild"]["features"]
        if  "DISCOVERABLE" not in features \
        and "PARTNERED" not in features \
        and "VERIFIED" not in features:
            anyIllegal = True
            break
    if anyIllegal:
        logging.info("Reporting " + comment.id)
        comment.report("Self promotion; not verified/partnered/discoverable (auto-detected /u/mlapibot)")
        try:
            url = "https://www.reddit.com/comments/{0}/comment/{1}/".format(comment.submission.id, comment.id)
            e = webHook.getEmbed("Reported Comment",
                comment.body, url, comment.author.name)
            webHook._sendWebhook(e)
        except:
            pass
Esempio n. 13
0
    def expandURLs(self, comment: praw.models.Comment, subcommands: list,
                   config: dict):
        if self._botdb.ignore_user(comment.author.name):
            log.info("Ignoring comment by {}".format(comment.author.name))
            return

        replyTo = None
        mode = None
        if len(subcommands) > 0 and subcommands[0].lower(
        ) in self.DISPLAY_MODES:
            mode = subcommands[0].lower()
        else:
            mode = self.DEFAULT_DISPLAY_MODE

        footer = '\n' + config['footer'] if 'footer' in config else ''

        body = comment.body
        urls = [
            ('#' + id)
            for id in re.findall('boardgamegeek.com/(?:boardgame|thing)/(\d+)',
                                 body,
                                 flags=re.UNICODE)
        ]

        response = self._getInfoResponseBody(comment, urls, mode)
        log.error('footer {} ({})'.format(footer, type(footer)))
        if response:
            if replyTo:
                replyTo.reply(response + footer)
            else:
                comment.reply(response + footer)
            log.info('Replied to info request for comment {}'.format(
                comment.id))
        else:
            log.warn('Did not find anything to reply to in comment {}'.format(
                comment.id))
Esempio n. 14
0
def check_comment_depth(comment: praw.models.Comment, max_depth=3) -> bool:
    """
    Check if comment is in a allowed depth range

    :param comment: :class:`praw.models.Comment` to count the depth of
    :param max_depth: Maximum allowed depth
    :return: True if comment is in depth range between 0 and max_depth
    """
    count = 0
    while not comment.is_root:
        count += 1
        if count > max_depth:
            return False

        comment = comment.parent()

    return True
Esempio n. 15
0
    def remediate_comment(
        self,
        comment: praw.models.Comment,
        to_thread: praw.models.Submission,
    ):
        """Reply to a comment and direct them to the new thread

        Args:
            comment (praw.models.Comment): Comment to redirect
            to_thread (praw.models.Submission): Thread to redirect them to
        """
        msg = (
            f"Hi u/{comment.author}, I created a "
            f"[new Entering & Transitioning thread]({to_thread.permalink}). "
            "Since you haven't received any replies yet, "
            "please feel free to resubmit your comment in the new thread.")
        reply = comment.reply(msg)
        reply.mod.distinguish(how="yes")
Esempio n. 16
0
    def getParentInfo(self, comment: praw.models.Comment, subcommands: list,
                      config: dict):
        '''Allows others to call the bot to getInfo for parent posts.'''
        if self._botdb.ignore_user(comment.author.name):
            log.info("Ignoring comment by {}".format(comment.author.name))
            return

        log.debug('Got getParentInfo comment in id {}'.format(comment.id))

        if comment.is_root:
            # error here - this comment should be in response to a u/r2d8 comment.
            log.info('Got a repair comment as root, ignoring.')
            return

        parent = comment.parent()
        self.getInfo(parent,
                     subcommands=subcommands,
                     config=config,
                     replyTo=comment)
Esempio n. 17
0
 def xyzzy(self, comment: praw.models.Comment, subcommands: list,
           config: dict):
     comment.reply('Nothing happens.')
Esempio n. 18
0
    def repairComment(self, comment: praw.models.Comment, subcommands: list,
                      config: dict):
        '''Look for maps from missed game names to actual game names. If
        found repair orginal comment.'''
        if self._botdb.ignore_user(comment.author.name):
            log.info("Ignoring comment by {}".format(comment.author.name))
            return
        #
        # The repair is done by replacing the new games names with the old (wrong)
        # games names in the original /u/r2d8 response, then recreating the entire
        # post by regenerating it with the new (fixed) bolded game names. The just replacing
        # the orginal response with the new one.
        #
        log.debug('Got repair response, id {}'.format(comment.id))

        if comment.is_root:
            # error here - this comment should be in response to a u/r2d8 comment.
            log.info('Got a repair comment as root, ignoring.')
            return

        parent = comment.parent()
        if parent.author.name != self._botname:
            log.info(
                'Parent of repair comment is not authored by the bot, ignoring.'
            )
            return

        # Look for patterns of **something**=**somethingelse**. This line creates a dict
        # of something: somethingelse for each one pattern found.
        repairs = {
            match[0]: match[1]
            for match in re.findall('\*\*([^\*]+)\*\*=\*\*([^\*]+)\*\*',
                                    comment.body)
        }

        pbody = parent.body
        for wrongName, repairedName in repairs.items():
            # check to see if it's actually a game.
            log.info('Repairing {} --> {}'.format(wrongName, repairedName))
            alias = self._botdb.get_name_from_alias(repairedName)
            tmp_name = alias if alias else repairedName
            tmp_game = self._bggQueryGame(
                tmp_name)  # with caching it's ok to check twice
            if tmp_game:
                # In the parent body we want to replace [NAME](http://... with **NAME**(http://
                pbody = pbody.replace('[' + wrongName + ']',
                                      '**' + tmp_name + '**')
            else:
                log.info(
                    '{} seems to not be a game name according to BGG, ignoring.'
                    .format(tmp_name))

        # Now re-bold the not found strings so they are re-searched or re-added to the not found list.
        for nf in re.findall(
                '\[([\w|\s]+)]\(http://boardgamegeek.com/geeksearch.php',
                pbody):
            pbody += ' **{}**'.format(nf)

        # now re-insert the original command to retain the mode.
        grandparent = parent.parent()
        modes = list()
        if not grandparent:
            log.error('Cannot find original GP post. Assuming normal mode.')
        else:
            modes = re.findall('[getparent|get]info\s(\w+)', grandparent.body)

        targetmode = modes[0] if modes else self.DEFAULT_DISPLAY_MODE

        parent = parent.edit(pbody)
        bolded = self._getBoldedEntries(comment)
        new_reply = self._getInfoResponseBody(parent, bolded, targetmode)

        # should check for Editiable class somehow here. GTL
        log.debug('Replacing bot comment {} with: {}'.format(
            parent.id, new_reply))
        parent.edit(new_reply)
Esempio n. 19
0
 def edit_comment(posted: praw.models.Comment) -> None:
     """edits comment to reflect all users pinged"""
     body: str = "\n\n".join([f"Pinged members of {group} group.", "---", self._footer([("Request to be added to this group", "addtogroup", group)])])
     posted.edit(body)