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)
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)
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 )
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} ')
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)
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
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)