Exemplo n.º 1
0
def do_subreddits(mod_subreddit, sr_dict, start_utc):
    """Checks conditions and performs actions for subreddits in sr_dict"""    
    
    
    # check reports
    items = mod_subreddit.get_reports(limit=1000)
    stop_time = datetime.utcnow() - REPORT_BACKLOG_LIMIT
    check_items('report', items, sr_dict, stop_time)

    # check spam
    items = mod_subreddit.get_modqueue(limit=1000)
    stop_time = (session.query(func.max(Subreddit.last_spam))
                 .filter(Subreddit.enabled == True).one()[0])
    check_items('spam', items, sr_dict, stop_time)    
    
    # check new submissions
    items = mod_subreddit.get_new_by_date(limit=1000)
    stop_time = (session.query(func.max(Subreddit.last_submission))
                 .filter(Subreddit.enabled == True).one()[0])
    check_items('submission', items, sr_dict, stop_time)

    # check new comments
#     comment_multi = '+'.join([s.name for s in subreddits
    comment_multi = '+'.join([s.name for s in sr_dict.itervalues()
                              if not s.reported_comments_only])
    if comment_multi:
        comment_multi_sr = r.get_subreddit(comment_multi)
        items = comment_multi_sr.get_comments(limit=1000)
        stop_time = (session.query(func.max(Subreddit.last_comment))
                     .filter(Subreddit.enabled == True).one()[0])
        check_items('comment', items, sr_dict, stop_time)

    # respond to modmail
    try:
        respond_to_modmail(r.user.get_modmail(), start_utc)
    except Exception as e:
        logging.error('  ERROR: %s', e)
Exemplo n.º 2
0
def respond_to_modmail(modmail, start_time):
    """Responds to modmail if any submitters sent one before approval."""
    cache = list()
    # respond to any modmail sent in the last 5 mins
    time_window = timedelta(minutes=5)
    approvals = session.query(ActionLog).filter(
                    and_(ActionLog.action == 'approve',
                         ActionLog.action_time >= start_time - time_window)
                    ).all()

    for item in approvals:
        found = None
        done = False

        for i in cache:
            if datetime.utcfromtimestamp(i.created_utc) < item.created_utc:
                done = True
                break
            if (i.dest.lower() == '#'+item.subreddit.name.lower() and
                    i.author.name == item.user and
                    not i.replies):
                found = i
                break

        if not found and not done:
            for i in modmail:
                cache.append(i)
                if datetime.utcfromtimestamp(i.created_utc) < item.created_utc:
                    break
                if (i.dest.lower() == '#'+item.subreddit.name.lower() and
                        i.author.name == item.user and
                        not i.replies):
                    found = i
                    break

        if found:
            found.reply('Your submission has been approved automatically by '+
                cfg_file.get('reddit', 'username')+'. For future submissions '
                'please wait at least 5 minutes before messaging the mods, '
                'this post would have been approved automatically even '
                'without you sending this message.')
Exemplo n.º 3
0
def main():
    logging.config.fileConfig(path_to_cfg)
    start_utc = datetime.utcnow()
    start_time = time()

    global r
    try:
        r = reddit.Reddit(user_agent=cfg_file.get('reddit', 'user_agent'))
        logging.info('Logging in as %s', cfg_file.get('reddit', 'username'))
        r.login(cfg_file.get('reddit', 'username'),
            cfg_file.get('reddit', 'password'))
    except Exception as e:
        logging.error('  ERROR: %s', e)

    mod_subreddit = r.get_subreddit('mod')


    #
    # Do actions on individual subreddits
    #
    logging.info('CHECKING SUBREDDITS')
    
    # get subreddit list
    subreddits = session.query(Subreddit).filter(Subreddit.enabled == True).all()
    sr_dict = dict()
    for subreddit in subreddits:
        sr_dict[subreddit.name.lower()] = subreddit
    
    # do actions on subreddits
    do_subreddits(mod_subreddit, sr_dict, start_utc)
    

    #
    # Do actions on networks
    #
    logging.info('CHECKING NETWORKS')
    
    mods_checked = 0

    # get network list
    networks = session.query(Network).filter(Network.enabled == True).all()
    
    # do actions on each network
    for network in networks:
        # get subreddits in network
        network_subs = session.query(Subreddit).filter(Subreddit.network == network.id, Subreddit.enabled == True).all()
        network_sr_dict = dict()
        for subreddit in network_subs:
            network_sr_dict[subreddit.name.lower()] = subreddit
        
        # do subreddit actions on subreddits
        do_subreddits(mod_subreddit, network_sr_dict, start_utc)
        
        # check network mods
        logging.info('Checking network moderators')
        for subreddit in network_sr_dict.itervalues():
            # only check subs in networks
            if subreddit.network:
                mods_checked += check_network_moderators(network, network_sr_dict)
    
    logging.info('  Checked %s networks, added %s moderators', len(networks), mods_checked)

    logging.info('Completed full run in %s', elapsed_since(start_time))
Exemplo n.º 4
0
def check_condition(item, condition):
    """Checks an item against a single condition (and sub-conditions).
    
    Returns True if it matches, or False if not
    """
    start_time = time()

    if condition.attribute == 'user':
        if item.author:
            test_string = item.author.name
    elif (condition.attribute == 'body' and
            isinstance(item, reddit.objects.Submission)):
        test_string = item.selftext
    elif condition.attribute.startswith('media_'):
        if item.media:
            try:
                if condition.attribute == 'media_user':
                    test_string = item.media['oembed']['author_name']
                elif condition.attribute == 'media_title':
                    test_string = item.media['oembed']['description']
                elif condition.attribute == 'media_description':
                    test_string = item.media['oembed']['description']
            except KeyError:
                test_string = ''
        else:
            test_string = ''
    elif condition.attribute == 'meme_name':
        test_string = get_meme_name(item)
    else:
        test_string = getattr(item, condition.attribute)
    if not test_string:
        test_string = ''

    if condition.inverse:
        logging.debug('        Check #%s: "%s" NOT match ^%s$',
                        condition.id,
                        test_string.encode('ascii', 'ignore'),
                        condition.value.encode('ascii', 'ignore').lower())
    else:
        logging.debug('        Check #%s: "%s" match ^%s$',
                        condition.id,
                        test_string.encode('ascii', 'ignore'),
                        condition.value.encode('ascii', 'ignore').lower())

    if re.search('^'+condition.value+'$',
            test_string.lower(),
            re.DOTALL|re.UNICODE|re.IGNORECASE):
        satisfied = True
    else:
        satisfied = False

    # flip the result it's an inverse condition
    if condition.inverse:
        satisfied = not satisfied

    # check number of reports if necessary
    if satisfied and condition.num_reports is not None:
        if condition.auto_reapproving != False:
            # get number of reports already cleared
            try:
                entry = (session.query(AutoReapproval).filter(
                         AutoReapproval.permalink == get_permalink(item))
                        .one())
                previous_reports = entry.total_reports
            except NoResultFound:
                previous_reports = 0
            total_reports = item.num_reports + previous_reports
        else:
            total_reports = item.num_reports

        satisfied = (total_reports >= condition.num_reports)
    elif satisfied and condition.num_reports is None:
        satisfied = (item.num_reports == 0)

    # check user conditions if necessary
    if satisfied:
        satisfied = check_user_conditions(item, condition)
        logging.debug('          User condition result = %s', satisfied)

    # make sure all sub-conditions are satisfied as well
    if satisfied:
        if condition.additional_conditions:
            logging.debug('        Checking sub-conditions:')
        for sub_condition in condition.additional_conditions:
            match = check_condition(item, sub_condition)
            if not match:
                satisfied = False
                break
        if condition.additional_conditions:
            logging.debug('        Sub-condition result = %s', satisfied)

    logging.debug('        Result = %s in %s',
                    satisfied, elapsed_since(start_time))
    return satisfied
Exemplo n.º 5
0
def perform_action(subreddit, item, condition):
    """Performs the action for the condition(s).
    
    Also delivers the comment (if set) and creates an ActionLog entry.
    """
    
    global r
    disclaimer = ('\n\n*I am a bot, and this action was performed '
                    'automatically. Please [contact the moderators of this '
                    'subreddit](http://www.reddit.com/message/compose?'
                    'to=%23'+item.subreddit.display_name+') if you have any '
                    'questions or concerns.*')

    # build the comment if multiple conditions were matched
    if isinstance(condition, list):
        if any([c.comment for c in condition]):
            if condition[0].action == 'alert':
                verb = 'alerted'
            else:
                verb = condition[0].action+'d'

            comment = ('This has been '+verb+' for the following reasons:\n\n')
            for c in condition:
                if c.comment:
                    comment += '* '+c.comment+'\n'
            post_comment(item, comment)

        # bit of a hack and only logs and uses attributes from first
        # condition matched, should find a better method
        condition = condition[0]
    else:
        comment = condition.comment

    # abort if it's an alert and we've already alerted on this item
    if condition.action == 'alert':
        try:
            session.query(ActionLog).filter(
                and_(ActionLog.permalink == get_permalink(item),
                     ActionLog.action == 'alert')).one()
            return
        except NoResultFound:
            pass

    # perform the action
    if condition.action == 'remove':
        item.remove(condition.spam)
    elif condition.action == 'approve':
        item.approve()
    elif condition.action == 'set_flair':
        item.set_flair(condition.set_flair_text,
                       condition.set_flair_class)

    # deliver the comment if set
    if comment:
        if condition.comment_method == 'comment':
            post_comment(item, comment+disclaimer)
        elif condition.comment_method == 'modmail':
            r.compose_message('#'+subreddit.name,
                              'AutoModerator condition matched',
                              get_permalink(item)+'\n\n'+comment)
        elif condition.comment_method == 'message':
            r.compose_message(item.author.name,
                              'AutoModerator condition matched',
                              get_permalink(item)+'\n\n'+comment+disclaimer)

    # log the action taken
    action_log = ActionLog()
    action_log.subreddit_id = subreddit.id
    action_log.user = item.author.name
    action_log.permalink = get_permalink(item)
    action_log.created_utc = datetime.utcfromtimestamp(item.created_utc)
    action_log.action_time = datetime.utcnow()
    action_log.action = condition.action
    action_log.matched_condition = condition.id

    if isinstance(item, reddit.objects.Submission):
        action_log.title = item.title
        action_log.url = item.url
        action_log.domain = item.domain
        logging.info('  /r/%s: %s submission "%s"',
                        subreddit.name,
                        condition.action,
                        item.title.encode('ascii', 'ignore'))
    elif isinstance(item, reddit.objects.Comment):
        logging.info('  /r/%s: %s comment by user %s',
                        subreddit.name,
                        condition.action,
                        item.author.name)

    session.add(action_log)
    session.commit()
Exemplo n.º 6
0
def check_items(name, items, sr_dict, stop_time):
    """Checks the items generator for any matching conditions."""
    item_count = 0
    skip_count = 0
    skip_subs = set()
    start_time = time()
    seen_subs = set()

    logging.info('Checking new %ss', name)

    try:
        for item in items:
            # skip any items in /new that have been approved
            if name == 'submission' and item.approved_by:
                continue

            item_time = datetime.utcfromtimestamp(item.created_utc)
            if item_time <= stop_time:
                break

            try:
                subreddit = sr_dict[item.subreddit.display_name.lower()]
            except KeyError:
                skip_count += 1
                skip_subs.add(item.subreddit.display_name.lower())
                continue

            conditions = (subreddit.conditions
                            .filter(Condition.parent_id == None)
                            .all())
            conditions = filter_conditions(name, conditions)

            item_count += 1

            if subreddit.name not in seen_subs:
                setattr(subreddit, 'last_'+name, item_time)
                seen_subs.add(subreddit.name)

            # check removal conditions, stop checking if any matched
            if check_conditions(subreddit, item,
                    [c for c in conditions if c.action == 'remove']):
                continue

            # check set_flair conditions 
            check_conditions(subreddit, item,
                    [c for c in conditions if c.action == 'set_flair'])

            # check approval conditions
            check_conditions(subreddit, item,
                    [c for c in conditions if c.action == 'approve'])

            # check alert conditions
            check_conditions(subreddit, item,
                    [c for c in conditions if c.action == 'alert'])

            # if doing reports, check auto-reapproval if enabled
            if (name == 'report' and subreddit.auto_reapprove and
                    item.approved_by is not None):
                try:
                    # see if this item has already been auto-reapproved
                    entry = (session.query(AutoReapproval).filter(
                            AutoReapproval.permalink == get_permalink(item))
                            .one())
                    in_db = True
                except NoResultFound:
                    entry = AutoReapproval()
                    entry.subreddit_id = subreddit.id
                    entry.permalink = get_permalink(item)
                    entry.original_approver = item.approved_by.name
                    entry.total_reports = 0
                    entry.first_approval_time = datetime.utcnow()
                    in_db = False

                if (in_db or item.approved_by.name !=
                        cfg_file.get('reddit', 'username')):
                    item.approve()
                    entry.total_reports += item.num_reports
                    entry.last_approval_time = datetime.utcnow()

                    session.add(entry)
                    session.commit()
                    logging.info('  Re-approved %s', entry.permalink)
                            
        session.commit()
    except Exception as e:
        logging.error('  ERROR: %s', e)
        session.rollback()

    logging.info('  Checked %s items, skipped %s items in %s (skips: %s)',
            item_count, skip_count, elapsed_since(start_time),
            ', '.join(skip_subs))