Esempio n. 1
0
def password_email(user):
    """
    For resetting a user's password.
    """
    from r2.lib.pages import PasswordReset

    user_reset_ratelimit = SimpleRateLimit(
        name="email_reset_count_%s" % user._id36,
        seconds=int(datetime.timedelta(hours=12).total_seconds()),
        limit=3,
    )
    if not user_reset_ratelimit.record_and_check():
        return False

    global_reset_ratelimit = SimpleRateLimit(
        name="email_reset_count_global",
        seconds=int(datetime.timedelta(hours=1).total_seconds()),
        limit=1000,
    )
    if not global_reset_ratelimit.record_and_check():
        raise ValueError("password reset ratelimit exceeded")

    token = PasswordResetToken._new(user)
    base = g.https_endpoint or g.origin
    passlink = base + '/resetpassword/' + token._id
    g.log.info("Generated password reset link: " + passlink)
    _system_email(user.email,
                  PasswordReset(user=user,
                                passlink=passlink).render(style='email'),
                  Email.Kind.RESET_PASSWORD,
                  user=user,
                  )
    return True
Esempio n. 2
0
    def POST_mute_participant(self, conversation):

        if conversation.is_internal or conversation.is_auto:
            return self.send_error(400, errors.CANT_RESTRICT_MODERATOR)

        sr = Subreddit._by_fullname(conversation.owner_fullname)

        try:
            participant = conversation.get_participant_account()
        except NotFound:
            return self.send_error(404, errors.USER_DOESNT_EXIST)

        if not sr.can_mute(c.user, participant):
            return self.send_error(400, errors.CANT_RESTRICT_MODERATOR)

        if not c.user_is_admin:
            if not sr.is_moderator_with_perms(c.user, 'access', 'mail'):
                return self.send_error(403, errors.INVALID_MOD_PERMISSIONS)

            if sr.use_quotas:
                sr_ratelimit = SimpleRateLimit(
                    name="sr_muted_%s" % sr._id36,
                    seconds=g.sr_quota_time,
                    limit=g.sr_muted_quota,
                )
                if not sr_ratelimit.record_and_check():
                    return self.send_error(403, errors.SUBREDDIT_RATELIMIT)

        # Add the mute record but only if successful create the
        # appropriate notifications, this prevents duplicate
        # notifications from being sent
        added = sr.add_muted(participant)

        if not added:
            return simplejson.dumps(
                self._convo_to_serializable(conversation, all_messages=True))

        MutedAccountsBySubreddit.mute(sr, participant, c.user)
        permalink = conversation.make_permalink()

        # Create the appropriate objects to be displayed on the
        # mute moderation log, use the permalink to the new modmail
        # system
        ModAction.create(sr,
                         c.user,
                         'muteuser',
                         target=participant,
                         description=permalink)
        sr.add_rel_note('muted', participant, permalink)

        # Add the muted mod action to the conversation
        conversation.add_action(c.user, 'muted', commit=True)

        result = self._get_updated_convo(conversation.id, c.user)
        result['user'] = self._get_modmail_userinfo(conversation, sr=sr)

        return simplejson.dumps(result)
Esempio n. 3
0
def password_email(user):
    """
    For resetting a user's password.
    """
    from r2.lib.pages import PasswordReset

    user_reset_ratelimit = SimpleRateLimit(
        name="email_reset_count_%s" % user._id36,
        seconds=int(datetime.timedelta(hours=12).total_seconds()),
        limit=3,
    )
    if not user_reset_ratelimit.record_and_check():
        return False

    global_reset_ratelimit = SimpleRateLimit(
        name="email_reset_count_global",
        seconds=int(datetime.timedelta(hours=1).total_seconds()),
        limit=1000,
    )
    if not global_reset_ratelimit.record_and_check():
        raise ValueError("password reset ratelimit exceeded")

    token = PasswordResetToken._new(user)
    base = g.https_endpoint or g.origin
    passlink = base + '/resetpassword/' + token._id
    g.log.info("Generated password reset link: " + passlink)
    _system_email(
        user.email,
        PasswordReset(user=user, passlink=passlink).render(style='email'),
        Email.Kind.RESET_PASSWORD,
        user=user,
    )
    return True
Esempio n. 4
0
def message_notification_email(data):
    """Queues a system email for a new message notification."""
    from r2.lib.pages import MessageNotificationEmail

    timer_start = time.time()

    MAX_EMAILS_PER_USER = 30
    MAX_MESSAGES_PER_BATCH = 5
    total_messages_sent = 0
    inbox_item_lookup_count = 0

    unique_user_list = make_message_dict_unique(data)
    g.log.info("there are %s users for this batch of emails" %
               len(unique_user_list))

    for datum in unique_user_list.itervalues():
        user = Account._byID36(datum['to'], data=True)
        g.log.info('user fullname: %s' % user._fullname)

        # In case a user has enabled the preference while it was enabled for
        # them, but we've since turned it off.  We need to explicitly state the
        # user because we're not in the context of an HTTP request from them.
        if not feature.is_enabled('orangereds_as_emails', user=user):
            g.log.info('feature not enabled for user: %s' % user._fullname)
            continue

        # Don't send more than MAX_EMAILS_PER_USER per user per day
        user_notification_ratelimit = SimpleRateLimit(
            name="email_message_notification_%s" % user._id36,
            seconds=int(datetime.timedelta(days=1).total_seconds()),
            limit=MAX_EMAILS_PER_USER,
        )
        if not user_notification_ratelimit.check():
            g.log.info('message blocked at user_notification_ratelimit: %s' %
                       user_notification_ratelimit)
            continue

        # Get all new messages that haven't been emailed
        inbox_items = get_unread_and_unemailed(user)
        inbox_item_lookup_count += 1

        if not inbox_items:
            g.log.info('no inbox items found for %s' % user._fullname)
            continue

        newest_inbox_rel = inbox_items[-1][0]
        oldest_inbox_rel = inbox_items[0][0]

        now = datetime.datetime.now(g.tz)
        start_date = datetime.datetime.strptime(
            datum['start_date'], "%Y-%m-%d %H:%M:%S").replace(tzinfo=g.tz)

        # If messages are still being queued within the cooling period or
        # messages have been queued past the max delay, then keep waiting
        # a little longer to batch all of the messages up
        if (start_date != newest_inbox_rel._date and now <
                newest_inbox_rel._date + NOTIFICATION_EMAIL_COOLING_PERIOD and
                now < oldest_inbox_rel._date + NOTIFICATION_EMAIL_MAX_DELAY):
            g.log.info('messages still being batched for: %s' % user._fullname)
            continue

        messages = []
        message_count = 0
        more_unread_messages = False
        non_preview_usernames = set()

        # Batch messages to email starting with older messages
        for inbox_rel, message in inbox_items:
            # Get sender_name, replacing with display_author if it exists
            g.log.info('user fullname: %s, message fullname: %s' %
                       (user._fullname, message._fullname))

            sender_name = get_sender_name(message)

            if message_count >= MAX_MESSAGES_PER_BATCH:
                # prevent duplicate usernames for template display
                non_preview_usernames.add(sender_name)
                more_unread_messages = True
            else:
                link = None
                parent = None
                if isinstance(message, Comment):
                    permalink = message.make_permalink_slow(context=1,
                                                            force_domain=True)
                    if message.parent_id:
                        parent = Comment._byID(message.parent_id, data=True)
                    else:
                        link = Link._byID(message.link_id, data=True)
                else:
                    permalink = message.make_permalink(force_domain=True)

                message_type = get_message_type(message, parent, user, link)

                messages.append({
                    "author_name": sender_name,
                    "message_type": message_type,
                    "body": message.body,
                    "date": long_datetime(message._date),
                    "permalink": permalink,
                    "id": message._id,
                    "fullname": message._fullname,
                    "subject": getattr(message, 'subject', ''),
                })

            inbox_rel.emailed = True
            inbox_rel._commit()
            message_count += 1

        mac = generate_notification_email_unsubscribe_token(
            datum['to'],
            user_email=user.email,
            user_password_hash=user.password)
        base = g.https_endpoint or g.origin
        unsubscribe_link = base + '/mail/unsubscribe/%s/%s' % (datum['to'],
                                                               mac)
        inbox_url = base + '/message/inbox'

        # unique email_hash for emails, to be used in utm tags
        id_str = ''.join(str(message['id'] for message in messages))
        email_hash = hashlib.sha1(id_str).hexdigest()

        base_utm_query = {
            'utm_name': email_hash,
            'utm_source': 'email',
            'utm_medium': 'message_notification',
        }

        non_preview_usernames_str = generate_non_preview_usernames_str(
            non_preview_usernames)

        templateData = {
            'messages': messages,
            'unsubscribe_link': unsubscribe_link,
            'more_unread_messages': more_unread_messages,
            'message_count': message_count,
            'max_message_display_count': MAX_MESSAGES_PER_BATCH,
            'non_preview_usernames_str': non_preview_usernames_str,
            'base_url': base,
            'base_utm_query': base_utm_query,
            'inbox_url': inbox_url,
        }
        custom_headers = {'List-Unsubscribe': "<%s>" % unsubscribe_link}
        g.log.info('sending message for user: %s' % user._fullname)
        g.email_provider.send_email(
            to_address=user.email,
            from_address="Reddit <%s>" % g.notification_email,
            subject=Email.subjects[Email.Kind.MESSAGE_NOTIFICATION],
            text=MessageNotificationEmail(**templateData).render(
                style='email'),
            html=MessageNotificationEmail(**templateData).render(style='html'),
            custom_headers=custom_headers,
            email_type='message_notification_email',
        )

        total_messages_sent += 1

        # report the email event to data pipeline
        g.events.orangered_email_event(
            request=request,
            context=c,
            user=user,
            messages=messages,
            email_hash=email_hash,
            reply_count=message_count,
            newest_reply_age=newest_inbox_rel._date,
            oldest_reply_age=oldest_inbox_rel._date,
        )

        g.stats.simple_event('email.message_notification.queued')
        user_notification_ratelimit.record_usage()

    timer_end = time.time()
    g.log.info("Took %s seconds to send orangered emails" %
               (timer_end - timer_start))

    g.log.info("Total number of messages sent: %s" % total_messages_sent)
    g.log.info("Total count of inbox lookups: %s" % inbox_item_lookup_count)
Esempio n. 5
0
def message_notification_email(data):
    """Queues a system email for a new message notification."""
    from r2.lib.pages import MessageNotificationEmail

    timer_start = time.time()

    MAX_EMAILS_PER_USER = 30
    MAX_MESSAGES_PER_BATCH = 5
    total_messages_sent = 0
    inbox_item_lookup_count = 0

    unique_user_list = make_message_dict_unique(data)
    g.log.info(
        "there are %s users for this batch of emails" % len(unique_user_list))

    for datum in unique_user_list.itervalues():
        user = Account._byID36(datum['to'], data=True)
        g.log.info('user fullname: %s' % user._fullname)

        # In case a user has enabled the preference while it was enabled for
        # them, but we've since turned it off.  We need to explicitly state the
        # user because we're not in the context of an HTTP request from them.
        if not feature.is_enabled('orangereds_as_emails', user=user):
            g.log.info('feature not enabled for user: %s' % user._fullname)
            continue

        # Don't send more than MAX_EMAILS_PER_USER per user per day
        user_notification_ratelimit = SimpleRateLimit(
            name="email_message_notification_%s" % user._id36,
            seconds=int(datetime.timedelta(days=1).total_seconds()),
            limit=MAX_EMAILS_PER_USER,
        )
        if not user_notification_ratelimit.check():
            g.log.info('message blocked at user_notification_ratelimit: %s' %
                       user_notification_ratelimit)
            continue

        # Get all new messages that haven't been emailed
        inbox_items = get_unread_and_unemailed(user)
        inbox_item_lookup_count += 1

        if not inbox_items:
            g.log.info('no inbox items found for %s' % user._fullname)
            continue

        newest_inbox_rel = inbox_items[-1][0]
        oldest_inbox_rel = inbox_items[0][0]

        now = datetime.datetime.now(g.tz)
        start_date = datetime.datetime.strptime(datum['start_date'],
                     "%Y-%m-%d %H:%M:%S").replace(tzinfo=g.tz)

        # If messages are still being queued within the cooling period or
        # messages have been queued past the max delay, then keep waiting
        # a little longer to batch all of the messages up
        if (start_date != newest_inbox_rel._date and
                now < newest_inbox_rel._date + NOTIFICATION_EMAIL_COOLING_PERIOD and
                now < oldest_inbox_rel._date + NOTIFICATION_EMAIL_MAX_DELAY):
            g.log.info('messages still being batched for: %s' % user._fullname)
            continue

        messages = []
        message_count = 0
        more_unread_messages = False
        non_preview_usernames = set()

        # Batch messages to email starting with older messages
        for inbox_rel, message in inbox_items:
            # Get sender_name, replacing with display_author if it exists
            g.log.info('user fullname: %s, message fullname: %s' % (
                user._fullname, message._fullname))

            sender_name = get_sender_name(message)

            if message_count >= MAX_MESSAGES_PER_BATCH:
                # prevent duplicate usernames for template display
                non_preview_usernames.add(sender_name)
                more_unread_messages = True
            else:
                link = None
                parent = None
                if isinstance(message, Comment):
                    permalink = message.make_permalink_slow(context=1,
                        force_domain=True)
                    if message.parent_id:
                        parent = Comment._byID(message.parent_id, data=True)
                    else:
                        link = Link._byID(message.link_id, data=True)
                else:
                    permalink = message.make_permalink(force_domain=True)

                message_type = get_message_type(message, parent, user, link)

                messages.append({
                    "author_name": sender_name,
                    "message_type": message_type,
                    "body": message.body,
                    "date": long_datetime(message._date),
                    "permalink": permalink,
                    "id": message._id,
                    "fullname": message._fullname,
                    "subject": getattr(message, 'subject', ''),
                })

            inbox_rel.emailed = True
            inbox_rel._commit()
            message_count += 1

        mac = generate_notification_email_unsubscribe_token(
                datum['to'], user_email=user.email,
                user_password_hash=user.password)
        base = g.https_endpoint or g.origin
        unsubscribe_link = base + '/mail/unsubscribe/%s/%s' % (datum['to'], mac)
        inbox_url = base + '/message/inbox'

        # unique email_hash for emails, to be used in utm tags
        id_str = ''.join(str(message['id'] for message in messages))
        email_hash = hashlib.sha1(id_str).hexdigest()

        base_utm_query = {
            'utm_name': email_hash,
            'utm_source': 'email',
            'utm_medium':'message_notification',
        }

        non_preview_usernames_str = generate_non_preview_usernames_str(
                                        non_preview_usernames)

        templateData = {
            'messages': messages,
            'unsubscribe_link': unsubscribe_link,
            'more_unread_messages': more_unread_messages,
            'message_count': message_count,
            'max_message_display_count': MAX_MESSAGES_PER_BATCH,
            'non_preview_usernames_str': non_preview_usernames_str,
            'base_url': base,
            'base_utm_query': base_utm_query,
            'inbox_url': inbox_url,
        }
        custom_headers = {
            'List-Unsubscribe': "<%s>" % unsubscribe_link
        }
        g.log.info('sending message for user: %s' % user._fullname)
        g.email_provider.send_email(
            to_address=user.email,
            from_address="Reddit <%s>" % g.notification_email,
            subject=Email.subjects[Email.Kind.MESSAGE_NOTIFICATION],
            text=MessageNotificationEmail(**templateData).render(style='email'),
            html=MessageNotificationEmail(**templateData).render(style='html'),
            custom_headers=custom_headers,
            email_type='message_notification_email',
        )

        total_messages_sent += 1

        # report the email event to data pipeline
        g.events.orangered_email_event(
            request=request,
            context=c,
            user=user,
            messages=messages,
            email_hash=email_hash,
            reply_count=message_count,
            newest_reply_age=newest_inbox_rel._date,
            oldest_reply_age=oldest_inbox_rel._date,
        )

        g.stats.simple_event('email.message_notification.queued')
        user_notification_ratelimit.record_usage()

    timer_end = time.time()
    g.log.info(
        "Took %s seconds to send orangered emails" % (timer_end - timer_start))

    g.log.info("Total number of messages sent: %s" % total_messages_sent)
    g.log.info("Total count of inbox lookups: %s" % inbox_item_lookup_count)