Example #1
0
def process_coc(post, config):
    """
    Adds the username of the redditor to the db as accepting the code of
    conduct.

    :param post: The Comment object containing the claim.
    :param config: the global config dict.
    :return: None.
    """
    result = config.redis.sadd('accepted_CoC', post.author.name)

    modchat_emote = random.choice([
        ':tada:', ':confetti_ball:', ':party-lexi:', ':party-parrot:', ':+1:',
        ':trophy:', ':heartpulse:', ':beers:', ':gold:', ':upvote:',
        ':coolio:', ':derp:', ':lenny1::lenny2:', ':panic:',
        ':fidget-spinner:', ':fb-like:'
    ])

    # Have they already been added? If 0, then just act like they said `claim`
    # instead. If they're actually new, then send a message to slack.
    if result == 1:
        send_to_modchat(
            f'[u/{post.author.name}](http://www.reddit.com/u/{post.author.name})'
            f' has just accepted the CoC! {modchat_emote}',
            config,
            channel='new_volunteers')
    process_claim(post, config)
Example #2
0
def process_mod_intervention(post, config):
    """
    Triggers an alert in slack with a link to the comment if there is something
    offensive or in need of moderator intervention
    """
    if not isinstance(post, RedditComment):
        # Why are we here if it's not a comment?
        return

    # Collect all offenses (noted by the above regular expressions) from the
    # original
    phrases = []
    for regex in MOD_SUPPORT_PHRASES:
        matches = regex.search(post.body)
        if not matches:
            continue

        phrases.append(matches.group())

    if len(phrases) == 0:
        # Nothing offensive here, why did this function get triggered?
        return

    # Wrap each phrase in double-quotes (") and commas in between
    phrases = '"' + '", "'.join(phrases) + '"'

    send_to_modchat(
        f':rotating_light::rotating_light: Mod Intervention Needed '
        f':rotating_light::rotating_light: '
        f'\n\nDetected use of {phrases} {post.submission.shortlink}', config)
Example #3
0
def set_meta_flair_on_other_posts(config):
    """
    Loops through the 10 newest posts on ToR and sets the flair to
    'Meta' for any post that is not authored by the bot or any of
    the moderators.

    :param config: the active config object.
    :return: None.
    """
    for post in config.tor.new(limit=10):

        if (
            post.author != config.r.redditor('transcribersofreddit') and
            post.author not in config.tor_mods and
            post.link_flair_text != flair.meta
        ):
            logging.info(
                f'Flairing post {post.fullname} by author {post.author} with '
                f'Meta. '
            )
            flair_post(post, flair.meta)
            send_to_modchat(
                f'New meta post: <{post.url}|{post.title}>)',
                config
            )
Example #4
0
def forward_to_slack(item, config):
    username = item.author.name

    send_to_modchat(
        f'Unhandled message by '
        f'<https://reddit.com/user/{username}|u/{username}> -- '
        f'*{item.subject}*:\n{item.body}', config)
    logging.info(
        f'Received unhandled inbox message from {username}. \n Subject: '
        f'{item.subject}\n\nBody: {item.body} ')
Example #5
0
def check_inbox(config):
    """
    Goes through all the unread messages in the inbox. It deliberately
    leaves mail which does not fit into either category so that it can
    be read manually at a later point.

    :return: None.
    """
    # Sort inbox, then act on it
    # Invert the inbox so we're processing oldest first!
    for item in reversed(list(config.r.inbox.unread(limit=None))):
        # Very rarely we may actually get a message from Reddit itself.
        # In this case, there will be no author attribute.
        if item.author is None:
            send_to_modchat(
                f'We received a message without an author. Subject: '
                f'{item.subject}', config)
            item.mark_read()

        elif item.author.name == 'transcribot':
            item.mark_read()

        elif item.author.name in config.redis.smembers('blacklist'):
            logging.info(
                f'Skipping inbox item from {item.author.name} who is on the '
                f'blacklist ')
            item.mark_read()
            continue

        elif item.subject == 'username mention':
            logging.info(f'Received mention! ID {item}')

            # noinspection PyUnresolvedReferences
            try:
                process_mention(item)
            except (AttributeError, RedditClientException):
                # apparently this crashes with an AttributeError if someone
                # calls the bot and immediately deletes their comment. This
                # should fix that.
                continue
            item.mark_read()

        elif item.subject in ('comment reply', 'post reply'):
            process_reply(item, config)

        elif item.subject[0] == '!':
            # Handle our special commands
            process_command(item, config)
            item.mark_read()
            continue

        else:
            item.mark_read()
            forward_to_slack(item, config)
Example #6
0
def verified_posted_transcript(post, config):
    """
    Because we're using basic gamification, we need to put in at least
    a few things to make it difficult to game the system. When a user
    says they've completed a post, we check the parent post for a top-level
    comment by the user who is attempting to complete the post and for the
    presence of the key. If it's all there, we update their flair and mark
    it complete. Otherwise, we ask them to please contact the mods.

    Process:
    Get source link, check all comments, look for a root level comment
    by the author of the post and verify that the key is in their post.
    Return True if found, False if not.

    :param post: The Comment object that contains the string 'done'.
    :param config: the global config object.
    :return: True if a post is found, False if not.
    """
    top_parent = get_parent_post_id(post, config.r)

    linked_resource = config.r.submission(
        top_parent.id_from_url(top_parent.url))
    # get rid of the "See More Comments" crap
    linked_resource.comments.replace_more(limit=0)
    for top_level_comment in linked_resource.comments.list():
        if (_author_check(post, top_level_comment)
                and _footer_check(top_level_comment, config)):
            return True

    # Did their transcript get flagged by the spam filter? Check their history.
    if _author_history_check(post, config):
        send_to_modchat(f'Found removed post: {post.submission.shortlink}',
                        config,
                        channel='#removed_posts')
        return True
    else:
        return False
Example #7
0
def process_command(reply, config):
    """
    This function processes any commands send to the bot via PM with a subject
    that stars with a !. The basic flow is read JSON file, look for key with
    same subject, check if the caller is mod, or is in the list of allowed
    people, then reply with the results of pythonFunction.

    To add a new command: add an entry to commands.json, (look at the other
    commands already listed), and add your function to admin_commands.py.

    :param reply: Object, the message object that contains the requested
        command
    :param config: the global config object
    :return: None
    """

    # Trim off the ! from the start of the string
    requested_command = reply.subject[1:]

    with open('commands.json', newline='') as commands_file:
        commands = json.load(commands_file)
        logging.debug(f'Searching for command {requested_command}, '
                      f'from {reply.author.name}.')

        try:
            command = commands['commands'][requested_command]

        except KeyError:
            if from_moderator(reply, config):
                reply.reply("That command hasn't been implemented yet ):"
                            "\n\nMessage a dev to make your dream come true.")

            logging.warning(f"Error, command: {requested_command} not found!"
                            f" (from {reply.author.name})")

            return

        # command found
        logging.info(
            f'{reply.author.name} is attempting to run {requested_command}')

        # Mods are allowed to do any command, and some people are whitelisted
        # per command to be able to use them
        if (reply.author.name not in command['allowedNames']
                and not from_moderator(reply, config)):
            logging.info(
                f"{reply.author.name} failed to run {requested_command},"
                f"because they aren't a mod, or aren't whitelisted to use this"
                f" command")
            username = reply.author.name
            send_to_modchat(
                f":banhammer: Someone did something bad! "
                f"[u/{username}](https://reddit.com/user/{username}) tried to "
                f"run {requested_command}!", config)

            reply.reply(
                random.choice(commands['notAuthorizedResponses']).format(
                    random.choice(config.no_gifs)))

            return

        logging.debug(f'Now executing command {requested_command},'
                      f' by {reply.author.name}.')

        result = globals()[command['pythonFunction']](reply, config)

        if result is not None:
            reply.reply(result)