def perform_actions(self, item, data): """Execute all the rule's actions against the item.""" for key, target in self.targets.iteritems(): target_item = self.get_target_item(item, data, key) target.perform_actions(target_item, data) if self.comment: comment = self.build_message(self.comment, item, data, disclaimer=True) # TODO: shouldn't have to do all this manually if isinstance(item, Comment): link = data["link"] parent_comment = item else: link = item parent_comment = None new_comment, inbox_rel = Comment._new( ACCOUNT, link, parent_comment, comment, None) new_comment.distinguished = "yes" new_comment._commit() queries.queue_vote(ACCOUNT, new_comment, True, None) queries.new_comment(new_comment, inbox_rel) g.stats.simple_event("automoderator.comment") if self.modmail: message = self.build_message(self.modmail, item, data, permalink=True) subject = replace_placeholders( self.modmail_subject, data, self.matches) subject = subject[:100] new_message, inbox_rel = Message._new(ACCOUNT, data["subreddit"], subject, message, None) new_message.distinguished = "yes" new_message._commit() queries.new_message(new_message, inbox_rel) g.stats.simple_event("automoderator.modmail") if self.message and not data["author"]._deleted: message = self.build_message(self.message, item, data, disclaimer=True, permalink=True) subject = replace_placeholders( self.message_subject, data, self.matches) subject = subject[:100] new_message, inbox_rel = Message._new(ACCOUNT, data["author"], subject, message, None) queries.new_message(new_message, inbox_rel) g.stats.simple_event("automoderator.message") PerformedRulesByThing.mark_performed(item, self)
def notify_user_added(rel_type, author, user, target): msgs = user_added_messages.get(rel_type) if not msgs: return srname = target.path.rstrip("/") d = { "url": srname, "title": "%s: %s" % (srname, target.title), "author": "/u/" + author.name, "user": "******" + user.name, } if "pm" in msgs and author != user: subject = msgs["pm"]["subject"] % d msg = msgs["pm"]["msg"] % d if rel_type in ("moderator_invite", "contributor"): # send the message from the subreddit item, inbox_rel = Message._new( author, user, subject, msg, request.ip, sr=target, from_sr=True, can_send_email=False, is_auto_modmail=True) else: item, inbox_rel = Message._new( author, user, subject, msg, request.ip, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False) if "modmail" in msgs: subject = msgs["modmail"]["subject"] % d msg = msgs["modmail"]["msg"] % d if rel_type == "moderator_invite": # Don't send the separate moderator invite message from the # system user to new modmail, since the one sent to the invitee # will already show up in there. # TODO: when new modmail is fully deployed, the "modmail" dict # should be completely removed from the moderator_invite section # of user_added_messages, and this check removed. if feature.is_enabled('new_modmail', subreddit=target.name): return modmail_author = Account.system_user() else: modmail_author = author item, inbox_rel = Message._new(modmail_author, target, subject, msg, request.ip, sr=target, is_auto_modmail=True) queries.new_message(item, inbox_rel)
def _conversation(trees, parent): from r2.models import Message if parent._id in trees: convo = trees[parent._id] if convo: m = Message._byID(convo[0], data=True) if not convo or m.first_message == m.parent_id: return [(parent._id, convo)] # if we get to this point, either we didn't find the conversation, # or the first child of the result was not the actual first child. # To the database! m = Message._query(Message.c.first_message == parent._id, data=True) return compute_message_trees([parent] + list(m))
def process_message(msg): msg_dict = json.loads(msg.body) if msg_dict["event"] == "new_message": message_id36 = msg_dict["message_id36"] message = Message._byID36(message_id36, data=True) send_modmail_email(message) elif msg_dict["event"] == "blocked_muted": subreddit_id36 = msg_dict["subreddit_id36"] sr = Subreddit._byID36(subreddit_id36, data=True) parent_id36 = msg_dict["parent_id36"] parent = Message._byID36(parent_id36, data=True) sender_email = msg_dict["sender_email"] incoming_email_id = msg_dict["incoming_email_id"] send_blocked_muted_email(sr, parent, sender_email, incoming_email_id)
def get_email_ids(message): parent_email_id = None other_email_ids = [] if message.parent_id: parent = Message._byID(message.parent_id, data=True) if parent.email_id: other_email_ids.append(parent.email_id) parent_email_id = parent.email_id if message.first_message: first_message = Message._byID(message.first_message, data=True) if first_message.email_id: other_email_ids.append(first_message.email_id) return parent_email_id, other_email_ids
def __init__(self, thing, delete = False, report = True): was_comment = getattr(thing, 'was_comment', False) permalink = thing.permalink # don't allow replying to self unless it's modmail valid_recipient = (thing.author_id != c.user._id or thing.sr_id) can_reply = (c.user_is_loggedin and getattr(thing, "repliable", True) and valid_recipient) can_block = True can_mute = False is_admin_message = False del_on_recipient = (isinstance(thing, Message) and thing.del_on_recipient) if not was_comment: first_message = thing if getattr(thing, 'first_message', False): first_message = Message._byID(thing.first_message, data=True) if thing.sr_id: sr = thing.subreddit_slow is_admin_message = '/r/%s' % sr.name == g.admin_message_acct if (sr.is_muted(first_message.author_slow) or (first_message.to_id and sr.is_muted(first_message.recipient_slow))): can_reply = False can_mute = sr.can_mute(c.user, thing.author_slow) if not was_comment and thing.display_author: can_block = False if was_comment: link = thing.link_slow if link.archived or link.locked: can_reply = False # Allow comment-reply messages to have links to the full thread. if was_comment: self.full_comment_path = thing.link_permalink self.full_comment_count = thing.full_comment_count PrintableButtons.__init__(self, "messagebuttons", thing, profilepage = c.profilepage, permalink = permalink, was_comment = was_comment, unread = thing.new, user_is_recipient = thing.user_is_recipient, can_reply = can_reply, parent_id = getattr(thing, "parent_id", None), show_report = True, show_delete = False, can_block = can_block, can_mute = can_mute, is_admin_message = is_admin_message, del_on_recipient=del_on_recipient, )
def compute_message_trees(messages): from r2.models import Message roots = set() threads = {} mdict = {} messages = sorted(messages, key = lambda m: m._date, reverse = True) for m in messages: if not m._loaded: m._load() mdict[m._id] = m if m.first_message: roots.add(m.first_message) threads.setdefault(m.first_message, set()).add(m._id) else: roots.add(m._id) # load any top-level messages which are not in the original list missing = [m for m in roots if m not in mdict] if missing: mdict.update(Message._byID(tup(missing), return_dict = True, data = True)) # sort threads in chrono order for k in threads: threads[k] = list(sorted(threads[k])) tree = [(root, threads.get(root, [])) for root in roots] tree.sort(key = tree_sort_fn, reverse = True) return tree
def _add_message_nolock(key, message): from r2.models import Account, Message trees = g.permacache.get(key) if not trees: # in case an empty list got written at some point, delete it to # force a recompute if trees is not None: g.permacache.delete(key) # no point computing it now. We'll do it when they go to # their message page. return # if it is a new root message, easy enough if message.first_message is None: trees.insert(0, (message._id, [])) else: tree_dict = dict(trees) # if the tree already has the first message, update the list if message.first_message in tree_dict: if message._id not in tree_dict[message.first_message]: tree_dict[message.first_message].append(message._id) tree_dict[message.first_message].sort() # we have to regenerate the conversation :/ else: m = Message._query(Message.c.first_message == message.first_message, data = True) new_tree = compute_message_trees(m) if new_tree: trees.append(new_tree[0]) trees.sort(key = tree_sort_fn, reverse = True) # done! g.permacache.set(key, trees)
def send_system_message(user, subject, body, system_user=None, distinguished='admin', repliable=False, add_to_sent=True, author=None): from r2.lib.db import queries if system_user is None: system_user = Account.system_user() if not system_user: g.log.warning("Can't send system message " "- invalid system_user or g.system_user setting") return if not author: author = system_user item, inbox_rel = Message._new(author, user, subject, body, ip='0.0.0.0') item.distinguished = distinguished item.repliable = repliable item.display_author = system_user._id item._commit() try: queries.new_message(item, inbox_rel, add_to_sent=add_to_sent) except MemcachedError: raise MessageError('reddit_inbox')
def get_reply_to_address(message): """Construct a reply-to address that encodes the message id. The address is of the form: zendeskreply+{message_id36}-{email_mac} where the mac is generated from {message_id36} using the `modmail_email_secret` The reply address should be configured with the inbound email service so that replies to our messages are routed back to the app somehow. For mailgun this involves adding a Routes filter for messages sent to "zendeskreply\+*@". to be forwarded to POST /api/zendeskreply. """ # all email replies are treated as replies to the first message in the # conversation. this is to get around some peculiarities of zendesk if message.first_message: first_message = Message._byID(message.first_message, data=True) else: first_message = message email_id = first_message._id36 email_mac = hmac.new( g.secrets['modmail_email_secret'], email_id, hashlib.sha256).hexdigest() reply_id = "zendeskreply+{email_id}-{email_mac}".format( email_id=email_id, email_mac=email_mac) sr = Subreddit._byID(message.sr_id, data=True) return "r/{subreddit} mail <{reply_id}@{domain}>".format( subreddit=sr.name, reply_id=reply_id, domain=g.modmail_email_domain)
def POST_traffic_viewer(self, form, jquery, user, thing): """ Adds a user to the list of users allowed to view a promoted link's traffic page. """ if not form.has_errors("name", errors.USER_DOESNT_EXIST, errors.NO_USER): form.set_inputs(name="") form.set_html(".status:first", _("added")) if promote.add_traffic_viewer(thing, user): user_row = TrafficViewerList(thing).user_row('traffic_viewer', user) jquery(".traffic_viewer-table").show( ).find("table").insert_table_rows(user_row) # send the user a message msg = user_added_messages['traffic']['pm']['msg'] subj = user_added_messages['traffic']['pm']['subject'] if msg and subj: d = dict(url=thing.make_permalink_slow(), traffic_url=promote.promo_traffic_url(thing), title=thing.title) msg = msg % d item, inbox_rel = Message._new(c.user, user, subj, msg, request.ip) queries.new_message(item, inbox_rel)
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/r/" + subreddit.name if days: subject = "you've been temporarily banned from %(subreddit)s" message = ("you have been temporarily banned from posting to " "%(subreddit)s. this ban will last for %(duration)s days.") else: subject = "you've been banned from %(subreddit)s" message = "you have been banned from posting to %(subreddit)s." if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ("you can contact the moderators regarding your ban " "by replying to this message. **warning**: using other accounts to " "circumvent a subreddit ban is considered a violation of reddit's " "[site rules](/help/contentpolicy#section_prohibited_behavior) " "and can result in the " "[suspension](https://reddit.zendesk.com/hc/articles/205687686) " "of your reddit account.") item, inbox_rel = Message._new(mod, user, subject, message, request.ip, sr=subreddit, from_sr=True) queries.new_message(item, inbox_rel, update_modmail=False)
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/r/" + subreddit.name if days: subject = "you've been temporarily banned from %(subreddit)s" message = ("you have been temporarily banned from posting to " "%(subreddit)s. this ban will last for %(duration)s days.") else: subject = "you've been banned from %(subreddit)s" message = "you have been banned from posting to %(subreddit)s." if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ("you can contact the moderators regarding your ban " "by replying to this message. **warning**: using other accounts to " "circumvent a subreddit ban is considered a violation of reddit's " "[site rules](/rules) and can result in being banned from reddit " "entirely.") item, inbox_rel = Message._new( mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False)
def _mock_message(id=1, author_id=1, **kwargs): kwargs['id'] = id kwargs['author_id'] = author_id message = Message(**kwargs) VByName.run = MagicMock(return_value=message) return message
def _load_messages(mlist): from r2.models import Message m = {} ids = [x for x in mlist if not isinstance(x, Message)] if ids: m = Message._by_fullname(ids, return_dict = True, data = True) messages = [m.get(x, x) for x in mlist] return messages
def _load_messages(mlist): from r2.models import Message m = {} ids = [x for x in mlist if not isinstance(x, Message)] if ids: m = Message._by_fullname(ids, return_dict=True, data=True) messages = [m.get(x, x) for x in mlist] return messages
def __init__(self, thing, delete=False, report=True): was_comment = getattr(thing, 'was_comment', False) permalink = thing.permalink # don't allow replying to self unless it's modmail valid_recipient = (thing.author_id != c.user._id or thing.sr_id) can_reply = (c.user_is_loggedin and getattr(thing, "repliable", True) and valid_recipient) can_block = True can_mute = False if not was_comment: first_message = thing if getattr(thing, 'first_message', False): first_message = Message._byID(thing.first_message, data=True) if thing.sr_id: sr = thing.subreddit_slow if feature.is_enabled('modmail_muting', subreddit=sr.name): if (sr.is_muted(first_message.author_slow) or (first_message.to_id and sr.is_muted(first_message.recipient_slow))): can_reply = False if (not sr.is_moderator(thing.author_slow) and sr.is_moderator_with_perms( c.user, 'access', 'mail')): can_mute = True if not was_comment and thing.display_author: can_block = False if was_comment: link = thing.link_slow if link.archived or link.locked: can_reply = False # Allow comment-reply messages to have links to the full thread. if was_comment: self.full_comment_path = thing.link_permalink self.full_comment_count = thing.full_comment_count PrintableButtons.__init__( self, "messagebuttons", thing, profilepage=c.profilepage, permalink=permalink, was_comment=was_comment, unread=thing.new, user_is_recipient=thing.user_is_recipient, can_reply=can_reply, parent_id=getattr(thing, "parent_id", None), show_report=True, show_delete=False, can_block=can_block, can_mute=can_mute, )
def notify_user_added(rel_type, author, user, target, message=None): msgs = user_added_messages.get(rel_type) if not msgs: return srname = target.path.rstrip("/") d = { "url": srname, "title": "%s: %s" % (srname, target.title), "author": "/u/" + author.name, "user": "******" + user.name, } if "pm" in msgs and author != user: subject = msgs["pm"]["subject"] % d msg = msgs["pm"]["msg"] % d if rel_type == "banned" and not user.has_interacted_with(target): return if rel_type == "banned" and message: msg += "\n\n" + N_("note from the moderators:\n\n\"%(message)s\"") msg %= {'message': message} if rel_type in ("banned", "moderator_invite"): # send the message from the subreddit item, inbox_rel = Message._new(author, user, subject, msg, request.ip, sr=target, from_sr=True) else: item, inbox_rel = Message._new(author, user, subject, msg, request.ip) queries.new_message(item, inbox_rel, update_modmail=False) if "modmail" in msgs: subject = msgs["modmail"]["subject"] % d msg = msgs["modmail"]["msg"] % d if rel_type == "moderator_invite": modmail_author = Account.system_user() else: modmail_author = author item, inbox_rel = Message._new(modmail_author, target, subject, msg, request.ip, sr=target) queries.new_message(item, inbox_rel, update_modmail=False)
def _conversation(trees, parent): from r2.models import Message if parent._id in trees: convo = trees[parent._id] if convo: m = Message._byID(convo[0], data=True) if not convo or m.first_message == m.parent_id: return [(parent._id, convo)] # if we get to this point, either we didn't find the conversation, # or the first child of the result was not the actual first child. # To the database! rules = [Message.c.first_message == parent._id] if c.user_is_admin: rules.append(Message.c._spam == (True, False)) rules.append(Message.c._deleted == (True, False)) m = Message._query(*rules, data=True) return compute_message_trees([parent] + list(m))
def _conversation(trees, parent): from r2.models import Message if parent._id in trees: convo = trees[parent._id] if convo: m = Message._byID(convo[0], data = True) if not convo or m.first_message == m.parent_id: return [(parent._id, convo)] # if we get to this point, either we didn't find the conversation, # or the first child of the result was not the actual first child. # To the database! rules = [Message.c.first_message == parent._id] if c.user_is_admin: rules.append(Message.c._spam == (True, False)) rules.append(Message.c._deleted == (True, False)) m = Message._query(*rules, data=True) return compute_message_trees([parent] + list(m))
def get_message_subject(message): sr = Subreddit._byID(message.sr_id, data=True) if message.first_message: first_message = Message._byID(message.first_message, data=True) conversation_subject = first_message.subject else: conversation_subject = message.subject return u"[r/{subreddit} mail]: {subject}".format( subreddit=sr.name, subject=_force_unicode(conversation_subject))
def get_message_subject(message): sr = Subreddit._byID(message.sr_id, data=True) if message.first_message: first_message = Message._byID(message.first_message, data=True) conversation_subject = first_message.subject else: conversation_subject = message.subject return u"[{brander_community_abbr}/{subreddit} mail]: {subject}".format( subreddit=sr.name, subject=_force_unicode(conversation_subject, brander_community_abbr=g.brander_community_abbr))
def notify_user_added(rel_type, author, user, target): msgs = user_added_messages.get(rel_type) if not msgs: return srname = target.path.rstrip("/") d = { "url": srname, "title": "%s: %s" % (srname, target.title), "author": "/u/" + author.name, "user": "******" + user.name, } if "pm" in msgs and author != user: subject = msgs["pm"]["subject"] % d msg = msgs["pm"]["msg"] % d if rel_type == "banned" and not user.has_interacted_with(target): return if rel_type in ("banned", "moderator_invite"): # send the message from the subreddit item, inbox_rel = Message._new(author, user, subject, msg, request.ip, sr=target, from_sr=True) else: item, inbox_rel = Message._new(author, user, subject, msg, request.ip) queries.new_message(item, inbox_rel) if "modmail" in msgs: subject = msgs["modmail"]["subject"] % d msg = msgs["modmail"]["msg"] % d if rel_type == "moderator_invite": modmail_author = Account.system_user() else: modmail_author = author item, inbox_rel = Message._new(modmail_author, target, subject, msg, request.ip, sr=target) queries.new_message(item, inbox_rel)
def notify_user_added(rel_type, author, user, target): msgs = user_added_messages.get(rel_type) if not msgs: return srname = target.path.rstrip("/") d = { "url": srname, "title": "%s: %s" % (srname, target.title), "author": "/u/" + author.name, "user": "******" + user.name, } if "pm" in msgs and author != user: subject = msgs["pm"]["subject"] % d msg = msgs["pm"]["msg"] % d if rel_type in ("moderator_invite", "contributor"): # send the message from the subreddit item, inbox_rel = Message._new( author, user, subject, msg, request.ip, sr=target, from_sr=True, can_send_email=False) else: item, inbox_rel = Message._new( author, user, subject, msg, request.ip, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False) if "modmail" in msgs: subject = msgs["modmail"]["subject"] % d msg = msgs["modmail"]["msg"] % d if rel_type == "moderator_invite": modmail_author = Account.system_user() else: modmail_author = author item, inbox_rel = Message._new(modmail_author, target, subject, msg, request.ip, sr=target) queries.new_message(item, inbox_rel)
def __init__(self, thing, delete = False, report = True): was_comment = getattr(thing, 'was_comment', False) permalink = thing.permalink # don't allow replying to self unless it's modmail valid_recipient = (thing.author_id != c.user._id or thing.sr_id) is_muted = False can_mute = False if not was_comment: first_message = thing if getattr(thing, 'first_message', False): first_message = Message._byID(thing.first_message, data=True) if thing.sr_id: sr = thing.subreddit_slow if feature.is_enabled('modmail_muting', subreddit=sr.name): if (sr.is_muted(first_message.author_slow) or (first_message.to_id and sr.is_muted(first_message.recipient_slow))): is_muted = True if (not sr.is_moderator(thing.author_slow) and sr.is_moderator_with_perms(c.user, 'access', 'mail')): can_mute = True can_reply = (c.user_is_loggedin and getattr(thing, "repliable", True) and valid_recipient and not is_muted) can_block = True if not thing.was_comment and thing.display_author: can_block = False # Allow comment-reply messages to have links to the full thread. if was_comment: self.full_comment_path = thing.link_permalink self.full_comment_count = thing.full_comment_count PrintableButtons.__init__(self, "messagebuttons", thing, profilepage = c.profilepage, permalink = permalink, was_comment = was_comment, unread = thing.new, user_is_recipient = thing.user_is_recipient, can_reply = can_reply, parent_id = getattr(thing, "parent_id", None), show_report = True, show_delete = False, can_block = can_block, can_mute = can_mute, )
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/" + g.brander_community_abbr + "/" + subreddit.name if days: subject = "You've been temporarily banned from participating in %(subreddit)s" message = ("You have been temporarily banned from participating in " "%(subreddit)s. This ban will last for %(duration)s days. ") else: subject = "You've been banned from participating in %(subreddit)s" message = "You have been banned from participating in %(subreddit)s. " message += ("You can still view and subscribe to %(subreddit)s, but you " "won't be able to post or comment.") if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'Note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ( "If you have a question regarding your ban, you can " "contact the moderator team for %(subreddit)s by replying to this " "message.") % { "subreddit": sr_name } message += "\n\n" + ( "**Reminder from the %(site_name)s staff**: If you use " "another account to circumvent this sub ban, that will be " "considered a violation of [the Content Policy](/s/SaidIt/comments/j1/the_saiditnet_terms_and_content_policy/) " "and can result in your account being suspended" "from the site as a whole.") % { "site_name": g.brander_site } item, inbox_rel = Message._new(mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False)
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/r/" + subreddit.name if days: subject = "You've been temporarily banned from participating in %(subreddit)s" message = ("You have been temporarily banned from participating in " "%(subreddit)s. This ban will last for %(duration)s days. ") else: subject = "You've been banned from participating in %(subreddit)s" message = "You have been banned from participating in %(subreddit)s. " message += ("You can still view and subscribe to %(subreddit)s, but you " "won't be able to post or comment.") if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'Note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ( "If you have a question regarding your ban, you can " "contact the moderator team for %(subreddit)s by replying to this " "message.") % { "subreddit": sr_name } message += "\n\n" + ( "**Reminder from the Reddit staff**: If you use " "another account to circumvent this subreddit ban, that will be " "considered a violation of [the Content Policy](/help/contentpolicy#section_prohibited_behavior) " "and can result in your account being [suspended](https://reddit.zendesk.com/hc/en-us/articles/205687686) " "from the site as a whole.") item, inbox_rel = Message._new(mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False, is_auto_modmail=True) queries.new_message(item, inbox_rel, update_modmail=False)
def send_system_message(user, subject, body, system_user=None, distinguished="admin", repliable=False): from r2.lib.db import queries if system_user is None: system_user = Account.system_user() if not system_user: g.log.warning("Can't send system message " "- invalid system_user or g.system_user setting") return item, inbox_rel = Message._new(system_user, user, subject, body, ip="0.0.0.0") item.distinguished = distinguished item.repliable = repliable item._commit() try: queries.new_message(item, inbox_rel) except MemcachedError: raise MessageError("reddit_inbox")
def send_system_message(user, subject, body): from r2.lib.db import queries system_user = Account.system_user() if not system_user: g.log.warning("g.system_user isn't set properly. Can't send system message.") return item, inbox_rel = Message._new(system_user, user, subject, body, ip='0.0.0.0') item.distinguished = 'admin' item.repliable = False item._commit() try: queries.new_message(item, inbox_rel) except MemcachedError: raise MessageError('reddit_inbox')
def send_mod_removal_message(subreddit, mod, user): sr_name = "/r/" + subreddit.name subject = "You've been removed as a moderator from %(subreddit)s" message = ( "You have been removed as a moderator from %(subreddit)s. " "If you have a question regarding your removal, you can " "contact the moderator team for %(subreddit)s by replying to this " "message." ) subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name} item, inbox_rel = Message._new( mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False, ) queries.new_message(item, inbox_rel, update_modmail=True)
def _add_message_nolock(key, message): from r2.models import Account, Message trees = g.permacache.get(key) if not trees: # in case an empty list got written at some point, delete it to # force a recompute if trees is not None: g.permacache.delete(key) # no point computing it now. We'll do it when they go to # their message page. return # if it is a new root message, easy enough if message.first_message is None: trees.insert(0, (message._id, [])) else: tree_dict = dict(trees) # if the tree already has the first message, update the list if message.first_message in tree_dict: if message._id not in tree_dict[message.first_message]: tree_dict[message.first_message].append(message._id) tree_dict[message.first_message].sort() # we have to regenerate the conversation :/ else: m = Message._query( Message.c.first_message == message.first_message, data=True) new_tree = compute_message_trees(m) if new_tree: trees.append(new_tree[0]) trees.sort(key=tree_sort_fn, reverse=True) # If we have too many messages in the tree, drop the oldest # conversation to avoid the permacache size limit tree_size = len(trees) + sum(len(convo[1]) for convo in trees) if tree_size > MESSAGE_TREE_SIZE_LIMIT: del trees[-1] # done! g.permacache.set(key, trees)
def send_system_message(user, subject, body, system_user=None, distinguished='admin', repliable=False): from r2.lib.db import queries if system_user is None: system_user = Account.system_user() if not system_user: g.log.warning("Can't send system message " "- invalid system_user or g.system_user setting") return item, inbox_rel = Message._new(system_user, user, subject, body, ip='0.0.0.0') item.distinguished = distinguished item.repliable = repliable item._commit() try: queries.new_message(item, inbox_rel) except MemcachedError: raise MessageError('reddit_inbox')
def send_mod_removal_message(subreddit, mod, user): sr_name = "/r/" + subreddit.name subject = "You've been removed as a moderator from %(subreddit)s" message = ( "You have been removed as a moderator from %(subreddit)s. " "If you have a question regarding your removal, you can " "contact the moderator team for %(subreddit)s by replying to this " "message.") subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name} item, inbox_rel = Message._new( mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False, ) queries.new_message(item, inbox_rel, update_modmail=True)
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/r/" + subreddit.name if days: subject = "You've been temporarily banned from participating in %(subreddit)s" message = ("You have been temporarily banned from participating in " "%(subreddit)s. This ban will last for %(duration)s days. ") else: subject = "You've been banned from participating in %(subreddit)s" message = "You have been banned from participating in %(subreddit)s. " message += ("You can still view and subscribe to %(subreddit)s, but you " "won't be able to post or comment.") if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'Note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ("If you have a question regarding your ban, you can " "contact the moderator team for %(subreddit)s by replying to this " "message.") % {"subreddit": sr_name} message += "\n\n" + ("**Reminder from the Reddit staff**: If you use " "another account to circumvent this subreddit ban, that will be " "considered a violation of [the Content Policy](/help/contentpolicy#section_prohibited_behavior) " "and can result in your account being [suspended](https://reddit.zendesk.com/hc/en-us/articles/205687686) " "from the site as a whole.") item, inbox_rel = Message._new( mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False, is_auto_modmail=True) queries.new_message(item, inbox_rel, update_modmail=False)
def send_ban_message(subreddit, mod, user, note=None, days=None, new=True): sr_name = "/r/" + subreddit.name if days: subject = "you've been temporarily banned from %(subreddit)s" message = ("you have been temporarily banned from posting to " "%(subreddit)s. this ban will last for %(duration)s days.") else: subject = "you've been banned from %(subreddit)s" message = "you have been banned from posting to %(subreddit)s." if not new: subject = "Your ban from %(subreddit)s has changed" subject %= {"subreddit": sr_name} message %= {"subreddit": sr_name, "duration": days} if note: message += "\n\n" + 'note from the moderators:' message += "\n\n" + blockquote_text(note) message += "\n\n" + ( "you can contact the moderators regarding your ban " "by replying to this message. **warning**: using other accounts to " "circumvent a subreddit ban is considered a violation of reddit's " "[site rules](/rules) and can result in being banned from reddit " "entirely.") item, inbox_rel = Message._new(mod, user, subject, message, request.ip, sr=subreddit, from_sr=True, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False)
def get_items(self): tree = self.get_tree() prev_item = next_item = None if not self.parent: if self.num is not None: if self.after: if self.reverse: tree = filter( self._tree_filter_reverse, tree) next_item = self.after._id if len(tree) > self.num: first = tree[-(self.num+1)] prev_item = first[1][-1] if first[1] else first[0] tree = tree[-self.num:] else: prev_item = self.after._id tree = filter( self._tree_filter, tree) if len(tree) > self.num: tree = tree[:self.num] last = tree[-1] next_item = last[1][-1] if last[1] else last[0] # generate the set of ids to look up and look them up message_ids = [] for root, thread in tree: message_ids.append(root) message_ids.extend(thread) if prev_item: message_ids.append(prev_item) messages = Message._byID(message_ids, data = True, return_dict = False) wrapped = {} for m in self.wrap_items(messages): if not self._viewable_message(m): g.log.warning("%r is not viewable by %s; path is %s" % (m, c.user.name, request.fullpath)) continue wrapped[m._id] = m if prev_item: prev_item = wrapped[prev_item] if next_item: next_item = wrapped[next_item] final = [] for parent, children in tree: if parent not in wrapped: continue parent = wrapped[parent] if children: # if no parent is specified, check if any of the messages are # uncollapsed, and truncate the thread children = [wrapped[child] for child in children if child in wrapped] parent.child = empty_listing() # if the parent is new, uncollapsed, or focal we don't # want it to become a moremessages wrapper. if (self.skip and not self.parent and not parent.new and parent.is_collapsed and not (self.focal and self.focal._id == parent._id)): for i, child in enumerate(children): if (child.new or not child.is_collapsed or (self.focal and self.focal._id == child._id)): break else: i = -1 parent = Wrapped(MoreMessages(parent, empty_listing())) children = children[i:] parent.child.parent_name = parent._fullname parent.child.things = [] for child in children: child.is_child = True if self.focal and child._id == self.focal._id: # focal message is never collapsed child.collapsed = False child.focal = True else: child.collapsed = child.is_collapsed parent.child.things.append(child) parent.is_parent = True # the parent might be the focal message on a permalink page if self.focal and parent._id == self.focal._id: parent.collapsed = False parent.focal = True else: parent.collapsed = parent.is_collapsed final.append(parent) return (final, prev_item, next_item, len(final), len(final))
def get_items(self): tree = self.get_tree() tree, prev_item, next_item = self._apply_pagination(tree) message_ids = [] for parent_id, child_ids in tree: message_ids.append(parent_id) message_ids.extend(child_ids) if prev_item: message_ids.append(prev_item) messages = Message._byID(message_ids, data=True, return_dict=False) wrapped = {m._id: m for m in self.wrap_items(messages)} if prev_item: prev_item = wrapped[prev_item] if next_item: next_item = wrapped[next_item] final = [] for parent_id, child_ids in tree: if parent_id not in wrapped: continue parent = wrapped[parent_id] if not self._viewable_message(parent): continue children = [wrapped[child_id] for child_id in child_ids if child_id in wrapped] depth = {parent_id: 0} substitute_parents = {} if ( children and self.skip and not self.threaded and not self.parent and not parent.new and parent.is_collapsed ): for i, child in enumerate(children): if child.new or not child.is_collapsed: break else: i = -1 # in flat view replace collapsed chain with MoreMessages add_child_listing(parent) parent = Wrapped(MoreMessages(parent, parent.child)) children = children[i:] for child in sorted(children, key=lambda child: child._id): # iterate from the root outwards so we can check the depth if self.threaded: try: child_parent = wrapped[child.parent_id] except KeyError: # the stored comment tree was missing this message's # parent, treat it as a top level reply child_parent = parent else: # for flat view all messages are decendants of the # parent message child_parent = parent parent_depth = depth[child_parent._id] child_depth = parent_depth + 1 depth[child._id] = child_depth if child_depth == MAX_RECURSION: # current message is at maximum depth level, all its # children will be displayed as children of its parent substitute_parents[child._id] = child_parent._id if child_depth > MAX_RECURSION: child_parent_id = substitute_parents[child.parent_id] substitute_parents[child._id] = child_parent_id child_parent = wrapped[child_parent_id] if not hasattr(child_parent, "child"): add_child_listing(child_parent) child.is_child = True child_parent.child.things.append(child) for child in children: # look over the children again to decide whether they can be # collapsed child.threaded = self.threaded child.collapsed = self.should_collapse(child) if self.threaded and children: most_recent_child_id = max(child._id for child in children) most_recent_child = wrapped[most_recent_child_id] most_recent_child.most_recent = True parent.is_parent = True parent.threaded = self.threaded parent.collapsed = self.should_collapse(parent) final.append(parent) return (final, prev_item, next_item, len(final), len(final))
def POST_zendeskreply(self): request_body = request.POST recipient = request_body["recipient"] sender_email = request_body["sender"] from_ = request_body["from"] subject = request_body["subject"] body_plain = request_body["body-plain"] stripped_text = request_body["stripped-text"] stripped_signature = request_body["stripped-signature"] timestamp = request_body["timestamp"] token = request_body["token"] signature = request_body["signature"] email_id = request_body["Message-Id"] if not validate_mailgun_webhook(timestamp, token, signature): # per Mailgun docs send a 406 so the message won't be retried abort(406, "invalid signature") message_id36 = parse_and_validate_reply_to_address(recipient) if not message_id36: # per Mailgun docs send a 406 so the message won't be retried abort(406, "invalid message") parent = Message._byID36(message_id36, data=True) to = Account._byID(parent.author_id, data=True) sr = Subreddit._byID(parent.sr_id, data=True) body = self.get_snipped_body(stripped_text, stripped_signature) try: markdown_souptest(body) except SoupError: g.log.warning("bad markdown in modmail email: %s", body) abort(406, "invalid body") if parent.get_muted_user_in_conversation(): queue_blocked_muted_email(sr, parent, sender_email, email_id) return # keep the subject consistent message_subject = parent.subject if not message_subject.startswith("re: "): message_subject = "re: " + message_subject # from_ is like '"NAME (GROUP)" <*****@*****.**>' match = re.search("\"(?P<name>\w+) [\w ()]*\"", from_) from_sr = True author = Account.system_user() if match and match.group("name") in g.live_config['modmail_account_map']: zendesk_name = match.group("name") moderator_name = g.live_config['modmail_account_map'][zendesk_name] moderator = Account._by_name(moderator_name) if sr.is_moderator_with_perms(moderator, "mail"): author = moderator from_sr = False message, inbox_rel = Message._new( author=author, to=to, subject=message_subject, body=body, ip='0.0.0.0', parent=parent, sr=sr, from_sr=from_sr, can_send_email=False, sent_via_email=True, email_id=email_id, ) message._commit() queries.new_message(message, inbox_rel) g.stats.simple_event("mailgun.incoming.success") g.stats.simple_event("modmail_email.incoming_email")
def get_sent(user): q = Message._query(Message.c.author_id == user._id, Message.c._spam == (True, False), sort=desc('_date')) return make_results(q)
def get_sent(user_id): return Message._query(Message.c.author_id == user_id, Message.c._spam == (True, False), sort = desc('_date'))
def POST_conversations(self, entity, subject, body, is_author_hidden, to): """Creates a new conversation for a particular SR This endpoint will create a ModmailConversation object as well as the first ModmailMessage within the ModmailConversation object. POST Params: srName -- the human readable name of the subreddit subject -- the subject of the first message in the conversation body -- the body of the first message in the conversation isAuthorHidden -- boolean on whether the mod name should be hidden (only mods can use this flag) to -- name of the user that a mod wants to create a convo with (only mods can use this flag) """ self._feature_enabled_check(entity) # make sure the user is not muted when creating a new conversation if entity.is_muted(c.user) and not c.user_is_admin: return self.send_error(400, errors.USER_MUTED) # validate post params if (errors.USER_BLOCKED, to) in c.errors: return self.send_error(400, errors.USER_BLOCKED, fields='to') elif (errors.USER_DOESNT_EXIST, to) in c.errors: return self.send_error(404, errors.USER_DOESNT_EXIST, fields='to') if to and not isinstance(to, Account): return self.send_error( 422, errors.NO_SR_TO_SR_MESSAGE, fields='to', ) # only mods can set a 'to' parameter if (not entity.is_moderator_with_perms(c.user, 'mail') and to): return self.send_error(403, errors.MOD_REQUIRED, fields='to') if to and entity.is_muted(to): return self.send_error( 400, errors.MUTED_FROM_SUBREDDIT, fields='to', ) try: conversation = ModmailConversation( entity, c.user, subject, body, is_author_hidden=is_author_hidden, to=to, ) except MustBeAModError: return self.send_error(403, errors.MOD_REQUIRED, fields='isAuthorHidden') except Exception as e: g.log.error('Failed to save conversation: {}'.format(e)) return self.send_error(500, errors.CONVERSATION_NOT_SAVED) # Create copy of the message in the legacy messaging system as well if to: message, inbox_rel = Message._new( c.user, to, subject, body, request.ip, sr=entity, from_sr=is_author_hidden, create_modmail=False, ) else: message, inbox_rel = Message._new( c.user, entity, subject, body, request.ip, create_modmail=False, ) queries.new_message(message, inbox_rel) conversation.set_legacy_first_message_id(message._id) # Get author associated account object for serialization # of the newly created conversation object authors = self._try_get_byID(conversation.author_ids, Account, ignore_missing=True) response.status_code = 201 serializable_convo = conversation.to_serializable(authors, entity, all_messages=True, current_user=c.user) messages = serializable_convo.pop('messages') mod_actions = serializable_convo.pop('modActions') g.events.new_modmail_event( 'ss.send_modmail_message', conversation, message=conversation.messages[0], msg_author=c.user, sr=entity, request=request, context=c, ) return simplejson.dumps({ 'conversation': serializable_convo, 'messages': messages, 'modActions': mod_actions, })
def modmail_event(self, message, request=None, context=None): """Create a 'modmail' event for event-collector. message: An r2.models.Message object request: pylons.request of the request that created the message context: pylons.tmpl_context of the request that created the message """ from r2.models import Account, Message sender = message.author_slow sr = message.subreddit_slow sender_is_moderator = sr.is_moderator_with_perms(sender, "mail") if message.first_message: first_message = Message._byID(message.first_message, data=True) else: first_message = message event = EventV2( topic="message_events", event_type="ss.send_message", time=message._date, request=request, context=context, data={ # set these manually rather than allowing them to be set from # the request context because the loggedin user might not # be the message sender "user_id": sender._id, "user_name": sender.name, }, ) if sender == Account.system_user(): sender_type = "automated" elif sender_is_moderator: sender_type = "moderator" else: sender_type = "user" event.add("sender_type", sender_type) event.add("sr_id", sr._id) event.add("sr_name", sr.name) event.add("message_id", message._id) event.add("message_fullname", message._fullname) event.add("first_message_id", first_message._id) event.add("first_message_fullname", first_message._fullname) if request and request.POST.get("source", None): source = request.POST["source"] if source in {"compose", "permalink", "modmail", "usermail"}: event.add("page", source) if message.sent_via_email: event.add("is_third_party", True) event.add("third_party_metadata", "mailgun") if not message.to_id: target = sr else: target = Account._byID(message.to_id, data=True) event.add_target_fields(target) self.save_event(event)
def __init__(self, thing, delete=False, report=True): was_comment = getattr(thing, 'was_comment', False) permalink = thing.permalink # don't allow replying to self unless it's modmail valid_recipient = (thing.author_id != c.user._id or thing.sr_id) can_reply = (c.user_is_loggedin and getattr(thing, "repliable", True) and valid_recipient) can_block = True can_mute = False is_admin_message = False show_distinguish = c.user.employee and c.user._id == thing.author_id del_on_recipient = (isinstance(thing, Message) and thing.del_on_recipient) if not was_comment: first_message = thing if getattr(thing, 'first_message', False): first_message = Message._byID(thing.first_message, data=True) if thing.sr_id: sr = thing.subreddit_slow is_admin_message = '/r/%s' % sr.name == g.admin_message_acct if (sr.is_muted(first_message.author_slow) or (first_message.to_id and sr.is_muted(first_message.recipient_slow))): can_reply = False can_mute = sr.can_mute(c.user, thing.author_slow) if not was_comment and thing.display_author: can_block = False if was_comment: link = thing.link_slow if link.archived or link.locked: can_reply = False # Allow comment-reply messages to have links to the full thread. if was_comment: self.full_comment_path = thing.link_permalink self.full_comment_count = thing.full_comment_count PrintableButtons.__init__( self, "messagebuttons", thing, profilepage=c.profilepage, permalink=permalink, was_comment=was_comment, unread=thing.new, user_is_recipient=thing.user_is_recipient, can_reply=can_reply, parent_id=getattr(thing, "parent_id", None), show_report=True, show_delete=False, can_block=can_block, can_mute=can_mute, is_admin_message=is_admin_message, del_on_recipient=del_on_recipient, show_distinguish=show_distinguish, distinguished=thing.distinguished, )
def POST_mod_messages(self, conversation, msg_body, is_author_hidden, is_internal): """Creates a new message for a particular ModmailConversation URL Params: conversation_id -- id of the conversation to post a new message to POST Params: body -- this is the message body isAuthorHidden -- boolean on whether to hide author, i.e. respond as the subreddit isInternal -- boolean to signify a moderator only message """ self._validate_vmodconversation() sr = Subreddit._by_fullname(conversation.owner_fullname) self._feature_enabled_check(sr) # make sure the user is not muted before posting a message if sr.is_muted(c.user): return self.send_error(400, errors.USER_MUTED) if conversation.is_internal and not is_internal: is_internal = True is_mod = sr.is_moderator(c.user) if not is_mod and is_author_hidden: return self.send_error( 403, errors.MOD_REQUIRED, fields='isAuthorHidden', ) elif not is_mod and is_internal: return self.send_error( 403, errors.MOD_REQUIRED, fields='isInternal', ) try: if not conversation.is_internal and not conversation.is_auto: participant = conversation.get_participant_account() if participant and sr.is_muted(participant): return self.send_error( 400, errors.MUTED_FROM_SUBREDDIT, ) except NotFound: pass try: new_message = conversation.add_message( c.user, msg_body, is_author_hidden=is_author_hidden, is_internal=is_internal, ) except: return self.send_error(500, errors.MODMAIL_MESSAGE_NOT_SAVED) # Add the message to the legacy messaging system as well (unless it's # an internal message on a non-internal conversation, since we have no # way to hide specific messages from the external participant) legacy_incompatible = is_internal and not conversation.is_internal if (conversation.legacy_first_message_id and not legacy_incompatible): first_message = Message._byID(conversation.legacy_first_message_id) subject = conversation.subject if not subject.startswith('re: '): subject = 're: ' + subject # Retrieve the participant to decide whether to send the message # to the sr or to the participant. If the currently logged in user # is the same as the participant then address the message to the # sr. recipient = sr if not is_internal: try: participant = ( ModmailConversationParticipant.get_participant( conversation.id)) is_participant = ( (c.user._id == participant.account_id) and not sr.is_moderator_with_perms(c.user, 'mail')) if not is_participant: recipient = Account._byID(participant.account_id) except NotFound: pass message, inbox_rel = Message._new( c.user, recipient, subject, msg_body, request.ip, parent=first_message, from_sr=is_author_hidden, create_modmail=False, ) queries.new_message(message, inbox_rel) serializable_convo = conversation.to_serializable( entity=sr, all_messages=True, current_user=c.user, ) messages = serializable_convo.pop('messages') g.events.new_modmail_event( 'ss.send_modmail_message', conversation, message=new_message, msg_author=c.user, sr=sr, request=request, context=c, ) response.status_code = 201 return simplejson.dumps({ 'conversation': serializable_convo, 'messages': messages, })
def message_event(self, message, request=None, context=None): """Create a 'message' event for event-collector. message: An r2.models.Message object request: pylons.request of the request that created the message context: pylons.tmpl_context of the request that created the message """ from r2.models import Account, Message sender = message.author_slow if message.first_message: first_message = Message._byID(message.first_message, data=True) else: first_message = message event = Event( topic="message_events", event_type="ss.send_message", time=message._date, request=request, context=context, data={ # set these manually rather than allowing them to be set from # the request context because the loggedin user might not # be the message sender "user_id": sender._id, "user_name": sender.name, }, ) if sender == Account.system_user(): sender_type = "automated" else: sender_type = "user" event.add("sender_type", sender_type) event.add("message_kind", "message") event.add("message_id", message._id) event.add("message_fullname", message._fullname) event.add_text("message_body", message.body) event.add_text("message_subject", message.subject) event.add("first_message_id", first_message._id) event.add("first_message_fullname", first_message._fullname) if request and request.POST.get("source", None): source = request.POST["source"] if source in {"compose", "permalink", "usermail"}: event.add("page", source) if message.sent_via_email: event.add("is_third_party", True) event.add("third_party_metadata", "mailgun") target = Account._byID(message.to_id, data=True) event.add_target_fields(target) self.save_event(event)
def POST_zendeskreply(self): request_body = request.POST recipient = request_body["recipient"] sender_email = request_body["sender"] from_ = request_body["from"] subject = request_body["subject"] body_plain = request_body["body-plain"] stripped_text = request_body["stripped-text"] stripped_signature = request_body["stripped-signature"] timestamp = request_body["timestamp"] token = request_body["token"] signature = request_body["signature"] email_id = request_body["Message-Id"] if not validate_mailgun_webhook(timestamp, token, signature): # per Mailgun docs send a 406 so the message won't be retried abort(406, "invalid signature") message_id36 = parse_and_validate_reply_to_address(recipient) if not message_id36: # per Mailgun docs send a 406 so the message won't be retried abort(406, "invalid message") parent = Message._byID36(message_id36, data=True) to = Account._byID(parent.author_id, data=True) sr = Subreddit._byID(parent.sr_id, data=True) body = self.get_snipped_body(stripped_text, stripped_signature) try: markdown_souptest(body) except SoupError: g.log.warning("bad markdown in modmail email: %s", body) abort(406, "invalid body") if parent.get_muted_user_in_conversation(): queue_blocked_muted_email(sr, parent, sender_email, email_id) return # keep the subject consistent message_subject = parent.subject if not message_subject.startswith("re: "): message_subject = "re: " + message_subject # from_ is like '"NAME (GROUP)" <*****@*****.**>' match = re.search("\"(?P<name>\w+) [\w ()]*\"", from_) from_sr = True author = Account.system_user() if match and match.group( "name") in g.live_config['modmail_account_map']: zendesk_name = match.group("name") moderator_name = g.live_config['modmail_account_map'][zendesk_name] moderator = Account._by_name(moderator_name) if sr.is_moderator_with_perms(moderator, "mail"): author = moderator from_sr = False message, inbox_rel = Message._new( author=author, to=to, subject=message_subject, body=body, ip='0.0.0.0', parent=parent, sr=sr, from_sr=from_sr, can_send_email=False, sent_via_email=True, email_id=email_id, ) message._commit() queries.new_message(message, inbox_rel) g.stats.simple_event("mailgun.incoming.success") g.stats.simple_event("modmail_email.incoming_email")
return True resume_id = long(sys.argv[1]) if len(sys.argv) > 1 else None msg_accounts = Account._query(sort=desc("_date"), data=True) if resume_id: msg_accounts._filter(Account.c._id < resume_id) for account in progress(fetch_things2(msg_accounts), estimate=resume_id): current_inbox_count = account.inbox_count unread_messages = list(queries.get_unread_inbox(account)) if account._id % 100000 == 0: g.reset_caches() if not len(unread_messages): if current_inbox_count: account._incr('inbox_count', -current_inbox_count) else: msgs = Message._by_fullname( unread_messages, data=True, return_dict=False, ignore_missing=True, ) kept_msgs = sum(1 for msg in msgs if _keep(msg, account)) if kept_msgs or current_inbox_count: account._incr('inbox_count', kept_msgs - current_inbox_count)
resume_id = long(sys.argv[1]) if len(sys.argv) > 1 else None msg_accounts = Account._query(sort=desc("_date"), data=True) if resume_id: msg_accounts._filter(Account.c._id < resume_id) for account in progress(fetch_things2(msg_accounts), estimate=resume_id): current_inbox_count = account.inbox_count unread_messages = list(queries.get_unread_inbox(account)) if account._id % 100000 == 0: g.reset_caches() if not len(unread_messages): if current_inbox_count: account._incr('inbox_count', -current_inbox_count) else: msgs = Message._by_fullname( unread_messages, data=True, return_dict=False, ignore_missing=True, ) kept_msgs = sum(1 for msg in msgs if _keep(msg, account)) if kept_msgs or current_inbox_count: account._incr('inbox_count', kept_msgs - current_inbox_count)
def notify_user_added(rel_type, author, user, target): msgs = user_added_messages.get(rel_type) if not msgs: return srname = target.path.rstrip("/") d = { "url": srname, "title": "%s: %s" % (srname, target.title), "author": "/u/" + author.name, "user": "******" + user.name, } if "pm" in msgs and author != user: subject = msgs["pm"]["subject"] % d msg = msgs["pm"]["msg"] % d if rel_type in ("moderator_invite", "contributor"): # send the message from the subreddit item, inbox_rel = Message._new(author, user, subject, msg, request.ip, sr=target, from_sr=True, can_send_email=False, is_auto_modmail=True) else: item, inbox_rel = Message._new(author, user, subject, msg, request.ip, can_send_email=False) queries.new_message(item, inbox_rel, update_modmail=False) if "modmail" in msgs: subject = msgs["modmail"]["subject"] % d msg = msgs["modmail"]["msg"] % d if rel_type == "moderator_invite": # Don't send the separate moderator invite message from the # system user to new modmail, since the one sent to the invitee # will already show up in there. # TODO: when new modmail is fully deployed, the "modmail" dict # should be completely removed from the moderator_invite section # of user_added_messages, and this check removed. if feature.is_enabled('new_modmail', subreddit=target.name): return modmail_author = Account.system_user() else: modmail_author = author item, inbox_rel = Message._new(modmail_author, target, subject, msg, request.ip, sr=target, is_auto_modmail=True) queries.new_message(item, inbox_rel)
def get_sent(user): q = Message._query(Message.c.author_id == user._id, Message.c._spam == (True, False), sort = desc('_date')) return make_results(q)
def get_items(self): tree = self.get_tree() prev_item = next_item = None if not self.parent: if self.num is not None: if self.after: if self.reverse: tree = filter(self._tree_filter_reverse, tree) next_item = self.after._id if len(tree) > self.num: first = tree[-(self.num + 1)] prev_item = first[1][-1] if first[1] else first[0] tree = tree[-self.num:] else: prev_item = self.after._id tree = filter(self._tree_filter, tree) if len(tree) > self.num: tree = tree[:self.num] last = tree[-1] next_item = last[1][-1] if last[1] else last[0] # generate the set of ids to look up and look them up message_ids = [] for root, thread in tree: message_ids.append(root) message_ids.extend(thread) if prev_item: message_ids.append(prev_item) messages = Message._byID(message_ids, data=True, return_dict=False) wrapped = {m._id: m for m in self.wrap_items(messages)} if prev_item: prev_item = wrapped[prev_item] if next_item: next_item = wrapped[next_item] final = [] for parent, children in tree: if parent not in wrapped: continue parent = wrapped[parent] if not self._viewable_message(parent): continue if children: # if no parent is specified, check if any of the messages are # uncollapsed, and truncate the thread children = [ wrapped[child] for child in children if child in wrapped ] add_child_listing(parent) # if the parent is new, uncollapsed, or focal we don't # want it to become a moremessages wrapper. if (self.skip and not self.parent and not parent.new and parent.is_collapsed and not (self.focal and self.focal._id == parent._id)): for i, child in enumerate(children): if (child.new or not child.is_collapsed or (self.focal and self.focal._id == child._id)): break else: i = -1 parent = Wrapped(MoreMessages(parent, parent.child)) children = children[i:] parent.child.parent_name = parent._fullname parent.child.things = [] for child in children: child.is_child = True if self.focal and child._id == self.focal._id: # focal message is never collapsed child.collapsed = False child.focal = True else: child.collapsed = child.is_collapsed parent.child.things.append(child) parent.is_parent = True # the parent might be the focal message on a permalink page if self.focal and parent._id == self.focal._id: parent.collapsed = False parent.focal = True else: parent.collapsed = parent.is_collapsed final.append(parent) return (final, prev_item, next_item, len(final), len(final))