def post(self): """ New Subscription @param username: str @param subreddit: str #return {} """ a = parser.parse_args() s_username, s_subreddit = \ a['username'], a['subreddit'] if not s_username \ or not s_subreddit: return {'message': 'all fields required'}, 400, cors try: s_user = User.objects(username=s_username).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors try: s_subreddit = Subreddit.objects(name=s_subreddit).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors try: Subscription.objects(Q(user=s_user) & Q(subreddit=s_subreddit)).get() except DoesNotExist: pass else: return {'message': 'already subscribed'}, 400, cors Subscription(user=s_user, subreddit=s_subreddit).save() return {}, 200, cors
def get(self): """ Subreddit Details @param subreddit: (str) @returns {name, profilePic, title, description, rules} """ a = request.args s_name = a.get('name', None) if not s_name: return {'message': 'all fields required'}, 400, cors try: sub = Subreddit.objects(name=s_name).get() except DoesNotExist: return {'message': 'subreddit already exists'}, 400, cors return { 'name': sub.name, 'profilePic': "temp1.jpg", 'title': sub.title, 'description': sub.desc, 'rules': sub.rules, 'topic': sub.topic }, 200, cors
def get(self): """ List of all subreddits @returns list({'name'}) """ r = [loads(i.json()) for i in Subreddit.objects()] return [{'name': i['name']} for i in r], 200, cors
def get_list(subreddits: list): """ Returns a list of subreddit objects :param subreddits: :return: list """ return [Subreddit(subreddit) for subreddit in subreddits]
def put(self): """ New Subreddit @param name: (str) @param title: (str) @param username: creator (str) @param desc: (str) @optional @param rules: (str) @optional @param topic: (str) @returns {'name'} """ a = parser.parse_args() s_name, s_title, s_username, s_desc, s_rules, s_topic = \ a['name'], a['title'], a['username'], a['desc'], a['rules'], a['topic'] if not s_name or \ not s_title or \ not s_desc or \ not s_username: return {'message': 'all fields required'}, 400, cors try: s_user = User.objects(username=s_username).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors try: Subreddit.objects(name=s_name).get() except DoesNotExist: pass else: return {'message': 'subreddit already exists'}, 400, cors Subreddit(name=s_name, title=s_title, createdby=s_user, desc=s_desc, rules=(s_rules or ''), topic=(s_topic or '')).save() s_user.karma += 5 s_user.save() return {'name': s_name}, 200, cors
def get(self): a = request.args q = a.get('q', None) if not q: return {'message': 'all fields required'}, 400, cors posts = Post.objects( Q(title__icontains=q) | Q(body__icontains=q) | Q(flair__icontains=q)) users = User.objects(Q(username__icontains=q) | Q(bio__icontains=q)) subs = Subreddit.objects( Q(name__icontains=q) | Q(title__icontains=q) | Q(desc__icontains=q)) return { 'posts': list(map(lambda x: loads(x.json()), posts)), 'users': list(map(lambda x: loads(x.json()), users)), 'subreddits': list(map(lambda x: loads(x.json()), subs)) }, 200, cors
def post(self): """ New Post @param username: (str) @param title: (str) @param subreddit: (str) @optional @param body: (str) @optional @param flair: (str) @returns {} """ a = parser.parse_args() print(a) p_username, p_title, p_subreddit, p_flair, p_body = \ a['username'], a['title'], a['subreddit'], a['flair'], a['body'] if not p_username \ or not p_title \ or not p_subreddit: return {'message': 'all fields required'}, 400, cors try: p_user = User.objects(username=p_username).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors try: p_subreddit = Subreddit.objects(name=p_subreddit).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors new_post = Post(author=p_user, title=p_title, subreddit=p_subreddit, flair=(p_flair or ''), body=(p_body or '')).save() p_user.karma += 4 p_user.save() return {'_id': str(new_post._id)}, 200, cors
def get(self): """ Get all posts or a subreddit's posts @optional @get_param subreddit: all posts if not given, else only of that subreddit @optional @get_param subreddit: all posts if not given, else only of that user @returns list({"author", "title", "subreddit", "posteddate", "votecount", "flair"}) """ a = request.args p_subreddit = a.get('subreddit', None) p_username = a.get('username', None) if p_subreddit: try: p_subreddit = Subreddit.objects(name=p_subreddit).get() except DoesNotExist: return {'message': 'subreddit does not exist'}, 400, cors if p_username: try: p_user = User.objects(username=p_username).get() except DoesNotExist: return {'message': 'user does not exist'}, 400, cors if p_subreddit: if p_username: posts = Post.objects( Q(author=p_user) & Q(subreddit=p_subreddit)) else: posts = Post.objects(subreddit=p_subreddit) else: if p_username: posts = Post.objects(author=p_user) else: posts = Post.objects() posts = posts.order_by('-posteddate') return [loads(i.json()) for i in posts], 200, cors
def update_from_wiki(subreddit, requester): """Updates conditions from the subreddit's wiki.""" global r username = cfg_file.get('reddit', 'username') try: page = subreddit.get_wiki_page(cfg_file.get('reddit', 'wiki_page_name')) except Exception: send_error_message(requester, subreddit.display_name, 'The wiki page could not be accessed. Please ensure the page ' 'http://www.reddit.com/r/{0}/wiki/{1} exists and that {2} ' 'has the "wiki" mod permission to be able to access it.' .format(subreddit.display_name, cfg_file.get('reddit', 'wiki_page_name'), username)) return False html_parser = HTMLParser.HTMLParser() page_content = html_parser.unescape(page.content_md) # check that all the conditions are valid yaml condition_defs = yaml.safe_load_all(page_content) condition_num = 1 try: for cond_def in condition_defs: condition_num += 1 except Exception as e: indented = '' for line in str(e).split('\n'): indented += ' {0}\n'.format(line) send_error_message(requester, subreddit.display_name, 'Error when reading conditions from wiki - ' 'Syntax invalid in section #{0}:\n\n{1}' .format(condition_num, indented)) return False # reload and actually process the conditions condition_defs = yaml.safe_load_all(page_content) condition_num = 1 kept_sections = [] for cond_def in condition_defs: # ignore any non-dict sections (can be used as comments, etc.) if not isinstance(cond_def, dict): continue cond_def = lowercase_keys_recursively(cond_def) try: check_condition_valid(cond_def) except ValueError as e: send_error_message(requester, subreddit.display_name, 'Invalid condition in section #{0} - {1}' .format(condition_num, e)) return False # create a condition for final checks condition = Condition(cond_def) # test to make sure that the final regex(es) are valid for pattern in condition.match_patterns.values(): try: re.compile(pattern) except Exception as e: send_error_message(requester, subreddit.display_name, 'Generated an invalid regex from section #{0} - {1}' .format(condition_num, e)) return False condition_num += 1 kept_sections.append(cond_def) # Update the subreddit, or add it if necessary try: db_subreddit = (session.query(Subreddit) .filter(Subreddit.name == subreddit.display_name.lower()) .one()) except NoResultFound: db_subreddit = Subreddit() db_subreddit.name = subreddit.display_name.lower() db_subreddit.last_submission = datetime.utcnow() - timedelta(days=1) db_subreddit.last_spam = datetime.utcnow() - timedelta(days=1) db_subreddit.last_comment = datetime.utcnow() - timedelta(days=1) session.add(db_subreddit) db_subreddit.conditions_yaml = page_content session.commit() r.send_message(requester, '{0} conditions updated'.format(username), "{0}'s conditions were successfully updated for /r/{1}" .format(username, subreddit.display_name)) return True
def __init__(self, subreddits: list): self.subreddits_generator = (Subreddit(subreddit) for subreddit in subreddits)
def update_from_wiki(subreddit, requester): """Updates conditions from the subreddit's wiki.""" global r username = cfg_file.get('reddit', 'username') try: page = subreddit.get_wiki_page(cfg_file.get('reddit', 'wiki_page_name')) except Exception: send_error_message(requester, subreddit.display_name, 'The wiki page could not be accessed. Please ensure the page ' 'http://www.reddit.com/r/{0}/wiki/{1} exists and that {2} ' 'has the "wiki" mod permission to be able to access it.' .format(subreddit.display_name, cfg_file.get('reddit', 'wiki_page_name'), username)) return html_parser = HTMLParser.HTMLParser() page_content = html_parser.unescape(page.content_md) # check that all the conditions are valid yaml condition_defs = yaml.safe_load_all(page_content) condition_num = 1 try: for cond_def in condition_defs: condition_num += 1 except Exception as e: indented = '' for line in str(e).split('\n'): indented += ' {0}\n'.format(line) send_error_message(requester, subreddit.display_name, 'Error when reading conditions from wiki - ' 'Syntax invalid in section #{0}:\n\n{1}' .format(condition_num, indented)) return # reload and actually process the conditions condition_defs = yaml.safe_load_all(page_content) condition_num = 1 kept_sections = [] for cond_def in condition_defs: # ignore any non-dict sections (can be used as comments, etc.) if not isinstance(cond_def, dict): continue # lowercase all keys cond_def = {k.lower(): v for k, v in cond_def.iteritems()} try: check_condition_valid(cond_def) except ValueError as e: send_error_message(requester, subreddit.display_name, 'Invalid condition in section #{0} - {1}' .format(condition_num, e)) return # create a condition for final checks condition = Condition(cond_def) # test to make sure that the final regex(es) are valid for pattern in condition.match_patterns.values(): try: re.compile(pattern) except Exception as e: send_error_message(requester, subreddit.display_name, 'Generated an invalid regex from section #{0} - {1}' .format(condition_num, e)) return condition_num += 1 kept_sections.append(cond_def) # Update the subreddit, or add it if necessary try: db_subreddit = (session.query(Subreddit) .filter(Subreddit.name == subreddit.display_name.lower()) .one()) except NoResultFound: db_subreddit = Subreddit() db_subreddit.name = subreddit.display_name.lower() db_subreddit.last_submission = datetime.utcnow() - timedelta(days=1) db_subreddit.last_spam = datetime.utcnow() - timedelta(days=1) db_subreddit.last_comment = datetime.utcnow() - timedelta(days=1) session.add(db_subreddit) db_subreddit.conditions_yaml = page_content session.commit() r.send_message(requester, '{0} conditions updated'.format(username), "{0}'s conditions were successfully updated for /r/{1}" .format(username, subreddit.display_name))
def main(): global r prawlogger = logging.getLogger('prawcore') prawlogger.setLevel(logging.WARN) while True: # Login retry loop try: logger.info('Logging in as {0}' .format(cfg_file.get('reddit', 'username'))) r = praw.Reddit(client_id = cfg_file.get('reddit', 'client_id'), client_secret = cfg_file.get('reddit', 'client_secret'), user_agent = cfg_file.get('reddit', 'user_agent'), username = cfg_file.get('reddit', 'username'), password = cfg_file.get('reddit', 'password')) # break except Exception as e: logger.error('ERROR: {0}'.format(e)) logger.debug(traceback.format_exc()) else: sr_dict = get_moderated_subreddits() # load conditions from wiki rule_dict = load_all_rules(sr_dict) pprint.pprint(rule_dict) break while True: # main execution loop sleep_after = True reload_mod_subs = False try: # First, process command messages for message in unread_messages(): try: command = message.body.strip().lower() sr_name = clean_sr_name(message.subject).lower() subreddit = r.subreddit(sr_name) # TODO: validate user is moderator if message.author not in subreddit.moderator(): message.reply('Error: You do not moderate /r/{0}'.format(subreddit.display_name)) continue # OK, validated if command == 'register': # do we know this sub? if sr_name in sr_dict.keys(): message.reply("I already moderate /r/{}.\n\n".format(sr_name)) continue # otherwise... try to accept mod invite try: subreddit.mod.accept_invite() except: # should be APIException(error_type='NO_INVITE_FOUND') message.reply("You must invite me to moderate /r/{} first." .format(sr_name)) raise else: # get sub from db if previously registered: db_subreddit = None try: db_subreddit = (session.query(Subreddit) .filter(Subreddit.name == sr_name) .one()) except NoResultFound: # add to DB db_subreddit = Subreddit() db_subreddit.name = subreddit.display_name.lower() db_subreddit.last_submission = datetime.utcnow() - timedelta(days=1) db_subreddit.last_spam = datetime.utcnow() - timedelta(days=1) db_subreddit.last_comment = datetime.utcnow() - timedelta(days=1) db_subreddit.conditions_yaml = '' session.add(db_subreddit) finally: # now that it definitely exists: set enabled # (should we clear old rules from the db?) db_subreddit.enabled = True session.commit() message.reply("I have joined /r/{}".format(db_subreddit.name)) elif command in ['update', 'status', 'enable', 'disable', 'leave']: # these require the same database query db_subreddit = None try: db_subreddit = (session.query(Subreddit) .filter(Subreddit.name == sr_name) .one()) except NoResultFound: message.reply("Subreddit /r/{} is not registered with me." .format(sr_name)) else: # only proceed if we get a database hit. if command == 'update': # refresh configuration for a subreddit # todo: cache duplicate requests from multiple mods reload_mod_subs = True update_from_wiki(db_subreddit, message) elif command == 'status': pass elif command == 'enable': db_subreddit.enabled = True reload_mod_subs = True elif command == 'disable': db_subreddit.enabled = False reload_mod_subs = True elif command == 'leave': # leave moderator of subreddit if db_subreddit.enabled: message.reply("Please disable me on this subreddit first.") else: # TODO not implemented yet reload_mod_subs = True raise NotImplementedError # the following commands should respond with the enabled status if command in ['status', 'enable', 'disable']: message.reply("Subreddit /r/{} is currently {}abled." .format(db_subreddit.name, 'en' if db_subreddit.enabled else 'dis')) finally: session.commit() elif command == 'help': # should this just provide a link, or real command explanations? raise NotImplementedError else: # invalid command message.reply("Invalid command.") except NotImplementedError: message.reply("Error: that feature is not yet implemented.") except KeyboardInterrupt: raise except Exception as e: logger.error('ERROR: {0}'.format(e)) logger.debug(traceback.format_exc()) message.reply("# ERROR:\n\n{}".format(indent_lines(str(e)))) finally: message.mark_read() # changed mod subs if reload_mod_subs: sr_dict = get_moderated_subreddits() rule_dict = load_all_rules(sr_dict) # Then process queues: submission, comment, spam, report, comment reply, username mention # TODO: queue for edited items... # Queue priority, in increasing specificity: # - reports: multi/about/reports?only=(links|comments) # - comment # - submission # - any # - spam: multi/about/spam?only=(links|comments) # - comment # - submission # - any # - edited: multi/about/edited?only=(links|comments) # - comment # - submission # - any # - reply: inbox # - mention: inbox # - submission: multi/new # - comment: multi/comments multi_mod_queues = ['reports', 'spam', 'edited'] # r.subreddit().mod.<q> multi_queues = ['new', 'comments'] # r.subreddit().<q> user_queues = ['comment_replies', 'submission_replies', 'mentions'] # r.user.inbox.<q> # proof of concept for sr_name, subreddit in sr_dict.items(): logger.debug("Checking items in /r/{}".format(sr_name)) sr = r.subreddit(sr_name) # mod-only level queues for item in sr.mod.spam(): for rule in rule_dict[sr_name]: rule.process(item) for item in sr.mod.reports(): for rule in rule_dict[sr_name]: rule.process(item) for item in sr.mod.edited(): for rule in rule_dict[sr_name]: rule.process(item) # sub-level queues for item in sr.mod.new(): for rule in rule_dict[sr_name]: rule.process(item) for item in sr.mod.comments(): for rule in rule_dict[sr_name]: rule.process(item) # user queues - not implemented # for queue in queue_funcs: # subreddits = [s for s in sr_dict # if s in cond_dict and len(cond_dict[s][queue]) > 0] # if len(subreddits) == 0: # continue # multireddits = build_multireddit_groups(subreddits) # # fetch and process the items for each multireddit # for multi in multireddits: # if queue == 'report': # limit = cfg_file.get('reddit', 'report_backlog_limit_hours') # stop_time = datetime.utcnow() - timedelta(hours=int(limit)) # else: # stop_time = max(getattr(sr, 'last_'+queue) # for sr in sr_dict.values() # if sr.name in multi) # queue_subreddit = r.get_subreddit('+'.join(multi)) # if queue_subreddit: # queue_func = getattr(queue_subreddit, queue_funcs[queue]) # items = queue_func(limit=None) # check_items(queue, items, stop_time, sr_dict, cond_dict) except KeyboardInterrupt: raise except Exception as e: logger.error('ERROR: {0}'.format(e)) logger.debug(traceback.format_exc()) session.rollback() finally: if sleep_after: logger.info('Sleeping for 10 seconds') sleep(10) logger.info('Sleep ended, resuming') logging.info("Looping")