示例#1
0
def approve_sponsorship(realm: Realm, *, acting_user: Optional[UserProfile]) -> None:
    from zerver.actions.message_send import internal_send_private_message
    from zerver.actions.realm_settings import do_change_realm_plan_type

    do_change_realm_plan_type(realm, Realm.PLAN_TYPE_STANDARD_FREE, acting_user=acting_user)
    customer = get_customer_by_realm(realm)
    if customer is not None and customer.sponsorship_pending:
        customer.sponsorship_pending = False
        customer.save(update_fields=["sponsorship_pending"])
        RealmAuditLog.objects.create(
            realm=realm,
            acting_user=acting_user,
            event_type=RealmAuditLog.REALM_SPONSORSHIP_APPROVED,
            event_time=timezone_now(),
        )
    notification_bot = get_system_bot(settings.NOTIFICATION_BOT, realm.id)
    for user in realm.get_human_billing_admin_and_realm_owner_users():
        with override_language(user.default_language):
            # Using variable to make life easier for translators if these details change.
            plan_name = "Zulip Cloud Standard"
            emoji = ":tada:"
            message = _(
                f"Your organization's request for sponsored hosting has been approved! {emoji}.\n"
                f"You have been upgraded to {plan_name}, free of charge."
            )
            internal_send_private_message(notification_bot, user, message)
示例#2
0
def send_welcome_bot_response(send_request: SendMessageRequest) -> None:
    """Given the send_request object for a private message from the user
    to welcome-bot, trigger the welcome-bot reply."""
    welcome_bot = get_system_bot(settings.WELCOME_BOT,
                                 send_request.message.sender.realm_id)
    human_response_lower = send_request.message.content.lower()
    content = select_welcome_bot_response(human_response_lower)
    internal_send_private_message(welcome_bot, send_request.message.sender,
                                  content)
示例#3
0
 def setUp(self) -> None:
     super().setUp()
     # This emulates the welcome message sent by the welcome bot to [email protected]
     # This is only a quick fix - ideally, we would have this message sent by the initialization
     # code in populate_db.py
     user = self.example_user("hamlet")
     welcome_bot = get_system_bot(settings.WELCOME_BOT, user.realm_id)
     content = "Shortened welcome message."
     internal_send_private_message(welcome_bot, user, content)
示例#4
0
def process_missed_message(to: str, message: EmailMessage) -> None:
    mm_address = get_usable_missed_message_address(to)
    mm_address.increment_times_used()

    user_profile = mm_address.user_profile
    topic = mm_address.message.topic_name()

    if mm_address.message.recipient.type == Recipient.PERSONAL:
        # We need to reply to the sender so look up their personal recipient_id
        recipient = mm_address.message.sender.recipient
    else:
        recipient = mm_address.message.recipient

    if not is_user_active(user_profile):
        logger.warning(
            "Sending user is not active. Ignoring this message notification email."
        )
        return

    body = construct_zulip_body(message, user_profile.realm)

    assert recipient is not None
    if recipient.type == Recipient.STREAM:
        stream = get_stream_by_id_in_realm(recipient.type_id,
                                           user_profile.realm)
        send_mm_reply_to_stream(user_profile, stream, topic, body)
        recipient_str = stream.name
    elif recipient.type == Recipient.PERSONAL:
        display_recipient = get_display_recipient(recipient)
        assert not isinstance(display_recipient, str)
        recipient_str = display_recipient[0]["email"]
        recipient_user = get_user(recipient_str, user_profile.realm)
        internal_send_private_message(user_profile, recipient_user, body)
    elif recipient.type == Recipient.HUDDLE:
        display_recipient = get_display_recipient(recipient)
        assert not isinstance(display_recipient, str)
        emails = [user_dict["email"] for user_dict in display_recipient]
        recipient_str = ", ".join(emails)
        internal_send_huddle_message(user_profile.realm, user_profile, emails,
                                     body)
    else:
        raise AssertionError("Invalid recipient type!")

    logger.info(
        "Successfully processed email from user %s to %s",
        user_profile.id,
        recipient_str,
    )
示例#5
0
    def send_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
        if not self._rate_limit.is_legal():
            self._rate_limit.show_error_and_exit()

        if message["type"] == "stream":
            message_id = internal_send_stream_message_by_name(
                self.user_profile.realm,
                self.user_profile,
                message["to"],
                message["topic"],
                message["content"],
            )
            return {"id": message_id}

        assert message["type"] == "private"
        # Ensure that it's a comma-separated list, even though the
        # usual 'to' field could be either a List[str] or a str.
        recipients = ",".join(message["to"]).split(",")

        if len(message["to"]) == 0:
            raise EmbeddedBotEmptyRecipientsList(_("Message must have recipients!"))
        elif len(message["to"]) == 1:
            recipient_user = get_active_user(recipients[0], self.user_profile.realm)
            message_id = internal_send_private_message(
                self.user_profile, recipient_user, message["content"]
            )
        else:
            message_id = internal_send_huddle_message(
                self.user_profile.realm, self.user_profile, recipients, message["content"]
            )
        return {"id": message_id}
示例#6
0
def send_mm_reply_to_stream(user_profile: UserProfile, stream: Stream,
                            topic: str, body: str) -> None:
    try:
        check_send_message(
            sender=user_profile,
            client=get_client("Internal"),
            message_type_name="stream",
            message_to=[stream.id],
            topic_name=topic,
            message_content=body,
        )
    except JsonableError as error:
        error_message = "Error sending message to stream {stream} via message notification email reply:\n{error}".format(
            stream=stream.name, error=error.msg)
        internal_send_private_message(
            get_system_bot(settings.NOTIFICATION_BOT, user_profile.realm_id),
            user_profile,
            error_message,
        )
示例#7
0
def send_initial_pms(user: UserProfile) -> None:
    organization_setup_text = ""

    # We need to override the language in this code path, because it's
    # called from account registration, which is a pre-account API
    # request and thus may not have the user's language context yet.
    with override_language(user.default_language):
        if user.is_realm_admin:
            help_url = user.realm.uri + "/help/getting-your-organization-started-with-zulip"
            organization_setup_text = (" " + _(
                "We also have a guide for [Setting up your organization]({help_url})."
            )).format(help_url=help_url)

        welcome_msg = _("Hello, and welcome to Zulip!") + "👋"
        demo_org_warning = ""
        if user.realm.demo_organization_scheduled_deletion_date is not None:
            demo_org_warning = (_(
                "Note that this is a [demo organization]({demo_org_help_url}) and will be "
                "**automatically deleted** in 30 days.") + "\n\n")

        content = "".join([
            welcome_msg + " ",
            _("This is a private message from me, Welcome Bot.") + "\n\n",
            _("If you are new to Zulip, check out our [Getting started guide]({getting_started_url})!"
              ),
            "{organization_setup_text}" + "\n\n",
            "{demo_org_warning}",
            _("I can also help you get set up! Just click anywhere on this message or press `r` to reply."
              ) + "\n\n",
            _("Here are a few messages I understand:") + " ",
            bot_commands(),
        ])

    content = content.format(
        organization_setup_text=organization_setup_text,
        demo_org_warning=demo_org_warning,
        demo_org_help_url="/help/demo-organizations",
        getting_started_url="/help/getting-started-with-zulip",
    )

    internal_send_private_message(
        get_system_bot(settings.WELCOME_BOT, user.realm_id), user, content)
示例#8
0
 def _send_cross_realm_personal_message(self) -> int:
     # Send message from bot to users from different realm.
     bot_email = "*****@*****.**"
     internal_realm = get_realm(settings.SYSTEM_BOT_REALM)
     zulip_user = self.example_user("hamlet")
     msg_id = internal_send_private_message(
         sender=get_system_bot(bot_email, internal_realm.id),
         recipient_user=zulip_user,
         content="test message",
     )
     assert msg_id is not None
     return msg_id
示例#9
0
def process_new_human_user(
    user_profile: UserProfile,
    prereg_user: Optional[PreregistrationUser] = None,
    default_stream_groups: Sequence[DefaultStreamGroup] = [],
    realm_creation: bool = False,
) -> None:
    realm = user_profile.realm

    mit_beta_user = realm.is_zephyr_mirror_realm
    if prereg_user is not None:
        streams: List[Stream] = list(prereg_user.streams.all())
        acting_user: Optional[UserProfile] = prereg_user.referred_by

        # A PregistrationUser should not be used for another UserProfile
        assert prereg_user.created_user is None, "PregistrationUser should not be reused"
    else:
        streams = []
        acting_user = None

    # If the user's invitation didn't explicitly list some streams, we
    # add the default streams
    if len(streams) == 0:
        streams = get_default_subs(user_profile)

    for default_stream_group in default_stream_groups:
        default_stream_group_streams = default_stream_group.streams.all()
        for stream in default_stream_group_streams:
            if stream not in streams:
                streams.append(stream)

    bulk_add_subscriptions(
        realm,
        streams,
        [user_profile],
        from_user_creation=True,
        acting_user=acting_user,
    )

    add_new_user_history(user_profile, streams)

    # mit_beta_users don't have a referred_by field
    if (not mit_beta_user and prereg_user is not None
            and prereg_user.referred_by is not None
            and prereg_user.referred_by.is_active):
        # This is a cross-realm private message.
        with override_language(prereg_user.referred_by.default_language):
            internal_send_private_message(
                get_system_bot(settings.NOTIFICATION_BOT,
                               prereg_user.referred_by.realm_id),
                prereg_user.referred_by,
                _("{user} accepted your invitation to join Zulip!").format(
                    user=f"{user_profile.full_name} <`{user_profile.email}`>"),
            )

    # Revoke all preregistration users except prereg_user, and link prereg_user to
    # the created user
    if prereg_user is None:
        assert not realm_creation, "realm_creation should only happen with a PreregistrationUser"

    if prereg_user is not None:
        prereg_user.status = confirmation_settings.STATUS_ACTIVE
        prereg_user.created_user = user_profile
        prereg_user.save(update_fields=["status", "created_user"])

    # In the special case of realm creation, there can be no additional PreregistrationUser
    # for us to want to modify - because other realm_creation PreregistrationUsers should be
    # left usable for creating different realms.
    if not realm_creation:
        # Mark any other PreregistrationUsers in the realm that are STATUS_ACTIVE as
        # inactive so we can keep track of the PreregistrationUser we
        # actually used for analytics.
        if prereg_user is not None:
            PreregistrationUser.objects.filter(
                email__iexact=user_profile.delivery_email,
                realm=user_profile.realm).exclude(id=prereg_user.id).update(
                    status=confirmation_settings.STATUS_REVOKED)
        else:
            PreregistrationUser.objects.filter(
                email__iexact=user_profile.delivery_email,
                realm=user_profile.realm).update(
                    status=confirmation_settings.STATUS_REVOKED)

        if prereg_user is not None and prereg_user.referred_by is not None:
            notify_invites_changed(user_profile.realm)

    notify_new_user(user_profile)
    # Clear any scheduled invitation emails to prevent them
    # from being sent after the user is created.
    clear_scheduled_invitation_emails(user_profile.delivery_email)
    if realm.send_welcome_emails:
        enqueue_welcome_emails(user_profile, realm_creation)

    # We have an import loop here; it's intentional, because we want
    # to keep all the onboarding code in zerver/lib/onboarding.py.
    from zerver.lib.onboarding import send_initial_pms

    send_initial_pms(user_profile)
示例#10
0
    def consume(self, event: Dict[str, Any]) -> None:
        start = time.time()
        if event["type"] == "mark_stream_messages_as_read":
            user_profile = get_user_profile_by_id(event["user_profile_id"])

            for recipient_id in event["stream_recipient_ids"]:
                count = do_mark_stream_messages_as_read(
                    user_profile, recipient_id)
                logger.info(
                    "Marked %s messages as read for user %s, stream_recipient_id %s",
                    count,
                    user_profile.id,
                    recipient_id,
                )
        elif event["type"] == "mark_stream_messages_as_read_for_everyone":
            # This event is generated by the stream deactivation code path.
            batch_size = 100
            offset = 0
            while True:
                messages = Message.objects.filter(
                    recipient_id=event["stream_recipient_id"]).order_by(
                        "id")[offset:offset + batch_size]
                UserMessage.objects.filter(message__in=messages).extra(
                    where=[UserMessage.where_unread()]).update(
                        flags=F("flags").bitor(UserMessage.flags.read))
                offset += len(messages)
                if len(messages) < batch_size:
                    break
            logger.info(
                "Marked %s messages as read for all users, stream_recipient_id %s",
                offset,
                event["stream_recipient_id"],
            )
        elif event["type"] == "clear_push_device_tokens":
            try:
                clear_push_device_tokens(event["user_profile_id"])
            except PushNotificationBouncerRetryLaterError:

                def failure_processor(event: Dict[str, Any]) -> None:
                    logger.warning(
                        "Maximum retries exceeded for trigger:%s event:clear_push_device_tokens",
                        event["user_profile_id"],
                    )

                retry_event(self.queue_name, event, failure_processor)
        elif event["type"] == "realm_export":
            realm = Realm.objects.get(id=event["realm_id"])
            output_dir = tempfile.mkdtemp(prefix="zulip-export-")
            export_event = RealmAuditLog.objects.get(id=event["id"])
            user_profile = get_user_profile_by_id(event["user_profile_id"])

            try:
                public_url = export_realm_wrapper(
                    realm=realm,
                    output_dir=output_dir,
                    threads=6,
                    upload=True,
                    public_only=True,
                )
            except Exception:
                export_event.extra_data = orjson.dumps(
                    dict(failed_timestamp=timezone_now().timestamp(),
                         )).decode()
                export_event.save(update_fields=["extra_data"])
                logging.exception(
                    "Data export for %s failed after %s",
                    user_profile.realm.string_id,
                    time.time() - start,
                    stack_info=True,
                )
                notify_realm_export(user_profile)
                return

            assert public_url is not None

            # Update the extra_data field now that the export is complete.
            export_event.extra_data = orjson.dumps(
                dict(export_path=urllib.parse.urlparse(public_url).path,
                     )).decode()
            export_event.save(update_fields=["extra_data"])

            # Send a private message notification letting the user who
            # triggered the export know the export finished.
            with override_language(user_profile.default_language):
                content = _(
                    "Your data export is complete and has been uploaded here:\n\n{public_url}"
                ).format(public_url=public_url)
            internal_send_private_message(
                sender=get_system_bot(settings.NOTIFICATION_BOT, realm.id),
                recipient_user=user_profile,
                content=content,
            )

            # For future frontend use, also notify administrator
            # clients that the export happened.
            notify_realm_export(user_profile)
            logging.info(
                "Completed data export for %s in %s",
                user_profile.realm.string_id,
                time.time() - start,
            )
        elif event["type"] == "reupload_realm_emoji":
            # This is a special event queued by the migration for reuploading emojis.
            # We don't want to run the necessary code in the actual migration, so it simply
            # queues the necessary event, and the actual work is done here in the queue worker.
            realm = Realm.objects.get(id=event["realm_id"])
            logger.info("Processing reupload_realm_emoji event for realm %s",
                        realm.id)
            handle_reupload_emojis_event(realm, logger)
        elif event["type"] == "soft_reactivate":
            user_profile = get_user_profile_by_id(event["user_profile_id"])
            reactivate_user_if_soft_deactivated(user_profile)

        end = time.time()
        logger.info("deferred_work processed %s event (%dms)", event["type"],
                    (end - start) * 1000)