def send_message(self, message: Dict[str, Any]) -> None: if not self._rate_limit.is_legal(): self._rate_limit.show_error_and_exit() if message['type'] == 'stream': internal_send_stream_message_by_name( self.user_profile.realm, self.user_profile, message['to'], message['topic'], message['content'] ) return 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) internal_send_private_message(self.user_profile.realm, self.user_profile, recipient_user, message['content']) else: internal_send_huddle_message(self.user_profile.realm, self.user_profile, recipients, message['content'])
def send_initial_pms(user): # type: (UserProfile) -> None content = """Welcome to Zulip! This is a great place to test formatting, sending, and editing messages. Click anywhere on this message to reply. A compose box will open at the bottom of the screen.""" internal_send_private_message(user.realm, get_system_bot(settings.WELCOME_BOT), user.email, content)
def send_initial_pms(user): # type: (UserProfile) -> None content = ( "Hello, and welcome to Zulip!\n\nThis is a private message from me, Welcome Bot. " "Here are some tips to get you started:\n" "* Download our [Desktop and mobile apps](/apps)\n" "* Customize your account and notifications on your [Settings page](#settings).\n" "* Check out our !modal_link(#keyboard-shortcuts, Keyboard shortcuts)\n\n" "The most important shortcut is `r` or `Enter` to reply.\n\n" "Practice sending a few messages by replying to this conversation. If you're not into " "keyboards, that's okay too; clicking anywhere on this message will also do the trick!") internal_send_private_message(user.realm, get_system_bot(settings.WELCOME_BOT), user.email, content)
def test_cross_realm_file_access(self): # type: () -> None def create_user(email, realm_id): # type: (Text, Text) -> UserProfile self.register(email, 'test', subdomain=realm_id) return get_user(email, get_realm(realm_id)) test_subdomain = "uploadtest.example.com" user1_email = '*****@*****.**' user2_email = '*****@*****.**' user3_email = '*****@*****.**' r1 = Realm.objects.create(string_id=test_subdomain, invite_required=False) RealmDomain.objects.create(realm=r1, domain=test_subdomain) create_user(user1_email, test_subdomain) create_user(user2_email, 'zulip') create_user(user3_email, test_subdomain) # Send a message from @zulip.com -> @uploadtest.example.com self.login(user2_email, 'test') fp = StringIO("zulip!") fp.name = "zulip.txt" result = self.client_post("/json/user_uploads", {'file': fp}) uri = result.json()['uri'] fp_path_id = re.sub('/user_uploads/', '', uri) body = "First message ...[zulip.txt](http://localhost:9991/user_uploads/" + fp_path_id + ")" with self.settings(CROSS_REALM_BOT_EMAILS = set((user2_email, user3_email))): internal_send_private_message( realm=r1, sender=get_system_bot(user2_email), recipient_user=get_user(user1_email, r1), content=body, ) self.login(user1_email, 'test') response = self.client_get(uri, subdomain=test_subdomain) self.assertEqual(response.status_code, 200) data = b"".join(response.streaming_content) self.assertEqual(b"zulip!", data) self.logout() # Confirm other cross-realm users can't read it. self.login(user3_email, 'test') response = self.client_get(uri, subdomain=test_subdomain) self.assertEqual(response.status_code, 403) self.assert_in_response("You are not authorized to view this file.", response)
def approve_sponsorship(realm: Realm) -> None: from zerver.lib.actions import do_change_plan_type, internal_send_private_message do_change_plan_type(realm, Realm.STANDARD_FREE) 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"]) notification_bot = get_system_bot(settings.NOTIFICATION_BOT) for billing_admin in realm.get_human_billing_admin_users(): with override_language(billing_admin.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(billing_admin.realm, notification_bot, billing_admin, message)
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) 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, )
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, error_message )
def send_to_missed_message_address(address: str, message: message.Message) -> None: token = get_missed_message_token_from_address(address) key = missed_message_redis_key(token) result = redis_client.hmget(key, 'user_profile_id', 'recipient_id', 'subject') if not all(val is not None for val in result): raise ZulipEmailForwardError('Missing missed message address data') user_profile_id, recipient_id, subject_b = result # type: (bytes, bytes, bytes) user_profile = get_user_profile_by_id(user_profile_id) if not is_user_active(user_profile): logger.warning("Sending user is not active. Ignoring this missed message email.") return recipient = Recipient.objects.get(id=recipient_id) body = construct_zulip_body(message, user_profile.realm) if recipient.type == Recipient.STREAM: stream = get_stream_by_id_in_realm(recipient.type_id, user_profile.realm) internal_send_stream_message( user_profile.realm, user_profile, stream, subject_b.decode('utf-8'), 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.realm, 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))
def consume(self, event: Mapping[str, Any]) -> None: if event['type'] == 'mark_stream_messages_as_read': user_profile = get_user_profile_by_id(event['user_profile_id']) client = Client.objects.get(id=event['client_id']) for stream_id in event['stream_ids']: # Since the user just unsubscribed, we don't require # an active Subscription object (otherwise, private # streams would never be accessible) (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id, require_active=False) do_mark_stream_messages_as_read(user_profile, client, stream) elif event['type'] == 'realm_exported': realm = Realm.objects.get(id=event['realm_id']) output_dir = tempfile.mkdtemp(prefix="zulip-export-") public_url = export_realm_wrapper(realm=realm, output_dir=output_dir, threads=6, upload=True, public_only=True, delete_after_upload=True) assert public_url is not None # TODO: This enables support for delete after access, and needs to be tested. export_event = RealmAuditLog.objects.get(id=event['id']) export_event.extra_data = public_url export_event.save(update_fields=['extra_data']) # Send a private message notification letting the user who # triggered the export know the export finished. user_profile = get_user_profile_by_id(event['user_profile_id']) content = "Your data export is complete and has been uploaded here:\n\n%s" % ( public_url,) internal_send_private_message( realm=user_profile.realm, sender=get_system_bot(settings.NOTIFICATION_BOT), recipient_user=user_profile, content=content ) # For future frontend use, also notify administrator # clients that the export happened, including sending the # url. notify_export_completed(user_profile, public_url)
def _send_cross_realm_personal_message(self) -> int: # Send message from bot to users from different realm. bot_email = "*****@*****.**" zulip_user = self.example_user("hamlet") msg_id = internal_send_private_message( sender=get_system_bot(bot_email), recipient_user=zulip_user, content="test message", ) assert msg_id is not None return msg_id
def send_message(self, message: Dict[str, Any]) -> None: if not self._rate_limit.is_legal(): self._rate_limit.show_error_and_exit() if message['type'] == 'stream': internal_send_stream_message(self.user_profile.realm, self.user_profile, message['to'], message['subject'], message['content']) return 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']) == 1: recipient_user = get_user(recipients[0], self.user_profile.realm) internal_send_private_message(self.user_profile.realm, self.user_profile, recipient_user, message['content']) else: internal_send_huddle_message(self.user_profile.realm, self.user_profile, recipients, message['content'])
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: help_url = user.realm.uri + "/help/getting-your-organization-started-with-zulip" organization_setup_text = ("* " + _( "[Read the guide]({help_url}) for getting your organization started with Zulip" ) + "\n").format(help_url=help_url) welcome_msg = _("Hello, and welcome to Zulip!") if user.realm.demo_organization_scheduled_deletion_date is not None: welcome_msg += " " + _( "Note that this is a [demo organization]({demo_org_help_url}) and will be automatically deleted in 30 days." ) content = "".join([ welcome_msg + "\n\n", _("This is a private message from me, Welcome Bot.") + " ", _("Here are some tips to get you started:") + "\n", "* " + _("Download our [Desktop and mobile apps]({apps_url})") + "\n", "* " + _("Customize your account and notifications on your [Settings page]({settings_url})" ) + "\n", "* " + _("Type `?` to check out Zulip's keyboard shortcuts") + "\n {organization_setup_text}\n", _("The most important shortcut is `r` to reply.") + "\n\n", _("Practice sending a few messages by replying to this conversation.") + " ", _("If you're not into keyboards, that's okay too; " "clicking anywhere on this message will also do the trick!"), ]) content = content.format( apps_url="/apps", settings_url="#settings", organization_setup_text=organization_setup_text, demo_org_help_url="/help/demo-organizations", ) internal_send_private_message( get_system_bot(settings.WELCOME_BOT, user.realm_id), user, content)
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: help_url = user.realm.uri + "/help/getting-your-organization-started-with-axxess" organization_setup_text = ("* [Read the guide](%s) for getting your organization " "started with Axxess\n" % (help_url,)) content = ( "Hello, and welcome to Axxess!\n\nYour Privacy and Data are now Protected.\n" "\nGuaranteed\n" "Here are some tips to get you started:\n" "* Download our [Desktop and mobile apps](https://axxess.one/apps) \n" "* Customize your account and notifications on your [Settings page](#settings)\n" "* Type `?` to check out the Axxess keyboard shortcuts\n" "%s" "\n" "The most important shortcut is `r` to reply.\n\n" "Sign Up for our support\n") \ % (organization_setup_text,) internal_send_private_message(user.realm, get_system_bot(settings.WELCOME_BOT), user, content)
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: help_url = user.realm.uri + "/help/getting-your-organization-started-with-zulip" organization_setup_text = ("* [Read the guide](%s) for getting your organization " "started with Zulip\n" % (help_url,)) content = ( "Hello, and welcome to Zulip!\n\nThis is a private message from me, Welcome Bot. " "Here are some tips to get you started:\n" "* Download our [Desktop and mobile apps](/apps)\n" "* Customize your account and notifications on your [Settings page](#settings)\n" "* Type `?` to check out Zulip's keyboard shortcuts\n" "%s" "\n" "The most important shortcut is `r` to reply.\n\n" "Practice sending a few messages by replying to this conversation. If you're not into " "keyboards, that's okay too; clicking anywhere on this message will also do the trick!") \ % (organization_setup_text,) internal_send_private_message(user.realm, get_system_bot(settings.WELCOME_BOT), user, content)
def _send_cross_realm_message(self) -> int: # Send message from bot to users from different realm. bot_email = '*****@*****.**' get_user_profile_by_email(bot_email) mit_user = UserProfile.objects.filter(realm=self.mit_realm).first() msg_id = internal_send_private_message( realm=mit_user.realm, sender=get_system_bot(bot_email), recipient_user=mit_user, content='test message', ) assert msg_id is not None return msg_id
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: help_url = user.realm.uri + "/help/getting-your-organization-started-with-zulip" organization_setup_text = ( "* [Read the guide](%s) for getting your organization " "started with Zulip\n" % (help_url, )) content = ( "Hello, and welcome to Zineya!\n\nThis is a private message from me, Zineya Bot. " "Here are some tips to get you started:\n" "* Customize your account and notifications on your [Settings page](#settings)\n" "* Type `?` to check out Zineya's keyboard shortcuts\n" "%s" "\n" "The most important shortcut is `r` to reply.\n\n" "Practice sending a few messages by replying to this conversation. If you're not into " "keyboards, that's okay too; clicking anywhere on this message will also do the trick!") \ % (organization_setup_text,) print( "////////////////////////HEY THERE THIS IS WELCOME BOT FUNCTION UPPPER //////////////////////" ) print( "//////////// ///////////////////////////////////////////////////////////////////////////////" ) print( "//////////// ///////////////////////////////////////////////////////////////////////////////" ) welcome_bot = get_system_bot(settings.WELCOME_BOT) #welcome_bots = welcome_bot.replace('*****@*****.**', 'test-bot@localhost') print(welcome_bot.email) welcome_bot.email = "test-bot@localhost" welcome_bot.full_name = "Zineya" welcome_bot.short_name = "Zineya" welcome_bot.api_key = "cTVr9MrmB5OZQqOlx3vc7B5R6Ac3Hafd" welcome_bot.delivery_email = "test-bot@localhost" welcome_bot.id = 19 print(welcome_bot.email) internal_send_private_message(user.realm, welcome_bot, user, content)
def send_to_missed_message_address(address: str, message: message.Message) -> None: token = get_missed_message_token_from_address(address) key = missed_message_redis_key(token) result = redis_client.hmget(key, 'user_profile_id', 'recipient_id', 'subject') if not all(val is not None for val in result): raise ZulipEmailForwardError('Missing missed message address data') user_profile_id, recipient_id, subject_b = result # type: (bytes, bytes, bytes) user_profile = get_user_profile_by_id(user_profile_id) recipient = Recipient.objects.get(id=recipient_id) body = construct_zulip_body(message, user_profile.realm) if recipient.type == Recipient.STREAM: stream = get_stream_by_id_in_realm(recipient.type_id, user_profile.realm) internal_send_stream_message( user_profile.realm, user_profile, stream, subject_b.decode('utf-8'), 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.realm, 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 %s to %s" % ( user_profile.email, recipient_str))
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: help_url = user.realm.uri + "/help/getting-your-organization-started-with-zulip" organization_setup_text = ("* [Read the guide](%s) for getting your organization " "started with Zulip\n" % (help_url,)) content = ( "Hello, and welcome to Zulip!\n\nThis is a private message from me, Welcome Bot. " "Here are some tips to get you started:\n" "* Download our [Desktop and mobile apps](/apps)\n" "* Customize your account and notifications on your [Settings page](#settings)\n" "* Type `?` to check out Zulip's keyboard shortcuts\n" "%s" "\n" "The most important shortcut is `r` to reply.\n\n" "Practice sending a few messages by replying to this conversation. If you're not into " "keyboards, that's okay too; clicking anywhere on this message will also do the trick!") \ % (organization_setup_text,) internal_send_private_message(user.realm, get_system_bot(settings.WELCOME_BOT), user, content)
def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" 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(is_initial_pm=True), ]) 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)
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, delete_after_upload=True, ) except Exception: export_event.extra_data = orjson.dumps( dict(failed_timestamp=timezone_now().timestamp(), )).decode() export_event.save(update_fields=["extra_data"]) logging.error( "Data export for %s failed after %s", user_profile.realm.string_id, time.time() - start, ) 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, ) end = time.time() logger.info("deferred_work processed %s event (%dms)", event["type"], (end - start) * 1000)
def consume(self, event: Dict[str, Any]) -> None: if event['type'] == 'mark_stream_messages_as_read': user_profile = get_user_profile_by_id(event['user_profile_id']) client = Client.objects.get(id=event['client_id']) for stream_id in event['stream_ids']: # Since the user just unsubscribed, we don't require # an active Subscription object (otherwise, private # streams would never be accessible) (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id, require_active=False) do_mark_stream_messages_as_read(user_profile, client, stream) 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 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': start = time.time() 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, delete_after_upload=True) except Exception: export_event.extra_data = orjson.dumps(dict( failed_timestamp=timezone_now().timestamp(), )).decode() export_event.save(update_fields=['extra_data']) logging.error( "Data export for %s failed after %s", user_profile.realm.string_id, time.time() - start, ) 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( realm=user_profile.realm, sender=get_system_bot(settings.NOTIFICATION_BOT), 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, )
def consume(self, event: Dict[str, Any]) -> None: if event['type'] == 'mark_stream_messages_as_read': user_profile = get_user_profile_by_id(event['user_profile_id']) client = Client.objects.get(id=event['client_id']) for stream_id in event['stream_ids']: # Since the user just unsubscribed, we don't require # an active Subscription object (otherwise, private # streams would never be accessible) (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id, require_active=False) do_mark_stream_messages_as_read(user_profile, client, stream) 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': start = time.time() 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, delete_after_upload=True) except Exception: export_event.extra_data = ujson.dumps( dict(failed_timestamp=timezone_now().timestamp())) export_event.save(update_fields=['extra_data']) logging.error( "Data export for %s failed after %s", user_profile.realm.string_id, time.time() - start, ) 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 = ujson.dumps( dict(export_path=urllib.parse.urlparse(public_url).path, )) export_event.save(update_fields=['extra_data']) # Send a private message notification letting the user who # triggered the export know the export finished. content = "Your data export is complete and has been uploaded here:\n\n%s" % ( public_url, ) internal_send_private_message(realm=user_profile.realm, sender=get_system_bot( settings.NOTIFICATION_BOT), 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, )