diff --git a/scripts/lib/upgrade-zulip-stage-2 b/scripts/lib/upgrade-zulip-stage-2 index 21560e65e3..b39e771f6a 100755 --- a/scripts/lib/upgrade-zulip-stage-2 +++ b/scripts/lib/upgrade-zulip-stage-2 @@ -134,6 +134,8 @@ if migrations_needed: logging.info("Applying database migrations...") subprocess.check_call(["./manage.py", "migrate", "--noinput"], preexec_fn=su_to_zulip) +subprocess.check_call(["./manage.py", "create_realm_internal_bots"], preexec_fn=su_to_zulip) + logging.info("Restarting Zulip...") subprocess.check_output(["./scripts/restart-server"], preexec_fn=su_to_zulip) diff --git a/tools/lib/provision.py b/tools/lib/provision.py index 47025aac29..0bff444e68 100755 --- a/tools/lib/provision.py +++ b/tools/lib/provision.py @@ -374,6 +374,8 @@ def main(options): else: print("No need to run `manage.py compilemessages`.") + run(["./manage.py", "create_realm_internal_bots"]) # Creates realm internal bots if required. + run(["scripts/lib/clean-unused-caches"]) version_file = os.path.join(UUID_VAR_PATH, 'provision_version') diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 00d43847a3..267519b065 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -88,6 +88,7 @@ from confirmation.models import Confirmation, create_confirmation_link from confirmation import settings as confirmation_settings from six import unichr +from zerver.lib.bulk_create import bulk_create_users from zerver.lib.create_user import random_api_key from zerver.lib.timestamp import timestamp_to_datetime, datetime_to_timestamp from zerver.lib.queue import queue_json_publish @@ -426,6 +427,13 @@ def notify_created_bot(user_profile: UserProfile) -> None: event = dict(type="realm_bot", op="add", bot=bot) send_event(event, bot_owner_user_ids(user_profile)) +def create_users(realm: Realm, name_list: Iterable[Tuple[Text, Text]], bot_type: int=None) -> None: + user_set = set() + for full_name, email in name_list: + short_name = email_to_username(email) + user_set.add((email, full_name, short_name, True)) + bulk_create_users(realm, user_set, bot_type) + def do_create_user(email: Text, password: Optional[Text], realm: Realm, full_name: Text, short_name: Text, is_realm_admin: bool=False, bot_type: Optional[int]=None, bot_owner: Optional[UserProfile]=None, tos_version: Optional[Text]=None, diff --git a/zerver/lib/onboarding.py b/zerver/lib/onboarding.py index 7f500208c7..d380d8d7bd 100644 --- a/zerver/lib/onboarding.py +++ b/zerver/lib/onboarding.py @@ -4,11 +4,24 @@ from django.conf import settings from zerver.lib.actions import set_default_streams, bulk_add_subscriptions, \ internal_prep_stream_message, internal_send_private_message, \ create_stream_if_needed, create_streams_if_needed, do_send_messages, \ - do_add_reaction_legacy + do_add_reaction_legacy, create_users from zerver.models import Realm, UserProfile, Message, Reaction, get_system_bot from typing import Any, Dict, List, Mapping, Text +def setup_realm_internal_bots(realm: Realm) -> None: + internal_bots = [(bot['name'], bot['email_template'] % (settings.INTERNAL_BOT_DOMAIN,)) + for bot in settings.REALM_INTERNAL_BOTS] + create_users(realm, internal_bots, bot_type=UserProfile.DEFAULT_BOT) + bots = UserProfile.objects.filter( + realm=realm, + email__in=[bot_info[1] for bot_info in internal_bots], + bot_owner__isnull=True + ) + for bot in bots: + bot.bot_owner = bot + bot.save() + def send_initial_pms(user: UserProfile) -> None: organization_setup_text = "" if user.is_realm_admin: diff --git a/zerver/management/commands/create_realm_internal_bots.py b/zerver/management/commands/create_realm_internal_bots.py new file mode 100644 index 0000000000..b0e1dc55ae --- /dev/null +++ b/zerver/management/commands/create_realm_internal_bots.py @@ -0,0 +1,40 @@ + +from typing import Any, Iterable, Text, Tuple + +from django.conf import settings +from django.core.management.base import BaseCommand + +from zerver.lib.actions import create_users +from zerver.models import Realm, UserProfile + +class Command(BaseCommand): + help = "Create Realm internal bots. These bots provide various services like doing reminders." + + def handle(self, *args: Any, **options: Any) -> None: + internal_bots = set([(bot['name'], bot['email_template'] % (settings.INTERNAL_BOT_DOMAIN,)) + for bot in settings.REALM_INTERNAL_BOTS]) + + existing_bots = list(UserProfile.objects.select_related( + 'realm').filter(email__in=[bot[1] for bot in internal_bots])) + + all_realms = list(Realm.objects.all()) + + for realm in all_realms: + this_realm_bots = set() + for bot in existing_bots: + if bot.realm.string_id == realm.string_id: + this_realm_bots.update([bot]) + bots_to_create = list(internal_bots - this_realm_bots) + if bots_to_create: + create_users(realm, bots_to_create, bot_type=UserProfile.DEFAULT_BOT) + + # Set the owners for these bots to the bots themselves + bots = UserProfile.objects.filter( + email__in=[bot_info[1] for bot_info in internal_bots], + bot_owner__isnull=True + ) + for bot in bots: + bot.bot_owner = bot + bot.save() + + self.stdout.write("Successfully created realm default bots.\n") diff --git a/zerver/views/registration.py b/zerver/views/registration.py index 8040ad5d45..d1cfbe07f2 100644 --- a/zerver/views/registration.py +++ b/zerver/views/registration.py @@ -27,7 +27,7 @@ from django_auth_ldap.backend import LDAPBackend, _LDAPUser from zerver.decorator import require_post, has_request_variables, \ JsonableError, REQ, do_login from zerver.lib.onboarding import setup_initial_streams, \ - send_initial_realm_messages + send_initial_realm_messages, setup_realm_internal_bots from zerver.lib.response import json_success from zerver.lib.subdomains import get_subdomain, is_root_domain_available from zerver.lib.timezone import get_all_timezones @@ -176,6 +176,7 @@ def accounts_register(request: HttpRequest) -> HttpResponse: realm_name = form.cleaned_data['realm_name'] realm = do_create_realm(string_id, realm_name) setup_initial_streams(realm) + setup_realm_internal_bots(realm) assert(realm is not None) full_name = form.cleaned_data['full_name'] diff --git a/zproject/settings.py b/zproject/settings.py index a4bed7dc04..59ddc558cc 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -698,6 +698,10 @@ INTERNAL_BOTS = [{'var_name': 'NOTIFICATION_BOT', 'email_template': 'welcome-bot@%s', 'name': 'Welcome Bot'}] +REALM_INTERNAL_BOTS = [{'var_name': 'REMINDER_BOT', + 'email_template': 'reminder-bot@%s', + 'name': 'Reminder Bot'}] + if PRODUCTION: INTERNAL_BOTS += [ {'var_name': 'NAGIOS_STAGING_SEND_BOT', @@ -711,7 +715,7 @@ if PRODUCTION: INTERNAL_BOT_DOMAIN = "zulip.com" # Set the realm-specific bot names -for bot in INTERNAL_BOTS: +for bot in INTERNAL_BOTS + REALM_INTERNAL_BOTS: if vars().get(bot['var_name']) is None: bot_email = bot['email_template'] % (INTERNAL_BOT_DOMAIN,) vars()[bot['var_name']] = bot_email