Repository: zulip/zulip

Issue:
Customize Welcome Bot for education organizations
Welcome Bot should take into account organization type in order to provide a tailored onboarding experience. In particular, it should link to specialized onboarding help center content for education organizations.

Organization types:
- Education (non-profit), Education (for-profit)

Line in Welcome Bot message to replace:
> If you are new to Zulip, check out our [Getting started guide](/help/getting-started-with-zulip)! We also have a guide for [Setting up your organization](http://zulipdev.com:9991/help/getting-your-organization-started-with-zulip).

Instead, it should be:
> If you are new to Zulip, check out our [Using Zulip for a class guide](/help/using-zulip-for-a-class)! We also have a guide for [Setting up Zulip for a class](/help/setting-up-zulip-for-a-class).

The line should be modified for non-admin users as well, in the analogous way.



Discussion:
Hello @zulip/server-onboarding members, this issue was labeled with the "area: onboarding" label, so you may want to check it out!

<!-- areaLabelAddition -->

@zulipbot claim
Hello @raghupalash, you have been unassigned from this issue because you have not updated this issue or any referenced pull requests for over 14 days.

You can reclaim this issue or claim any other issue by commenting `@zulipbot claim` on that issue.

Thanks for your contributions, and hope to see you again soon!


Resolving patch:
diff --git a/zerver/actions/create_user.py b/zerver/actions/create_user.py
--- a/zerver/actions/create_user.py
+++ b/zerver/actions/create_user.py
@@ -252,9 +252,9 @@ def process_new_human_user(
 
     # 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
+    from zerver.lib.onboarding import send_initial_direct_message
 
-    send_initial_pms(user_profile)
+    send_initial_direct_message(user_profile)
 
 
 def notify_created_user(user_profile: UserProfile) -> None:
diff --git a/zerver/lib/onboarding.py b/zerver/lib/onboarding.py
--- a/zerver/lib/onboarding.py
+++ b/zerver/lib/onboarding.py
@@ -39,40 +39,75 @@ def create_if_missing_realm_internal_bots() -> None:
             setup_realm_internal_bots(realm)
 
 
-def send_initial_pms(user: UserProfile) -> None:
-    organization_setup_text = ""
+def send_initial_direct_message(user: UserProfile) -> None:
+    # We adjust the initial Welcome Bot direct message for education organizations.
+    education_organization = False
+    if (
+        user.realm.org_type == Realm.ORG_TYPES["education_nonprofit"]["id"]
+        or user.realm.org_type == Realm.ORG_TYPES["education"]["id"]
+    ):
+        education_organization = True
 
     # 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 education_organization:
+            getting_started_help = user.realm.uri + "/help/using-zulip-for-a-class"
+            getting_started_string = (
+                _(
+                    "If you are new to Zulip, check out our [Using Zulip for a class guide]({getting_started_url})!"
+                )
+            ).format(getting_started_url=getting_started_help)
+        else:
+            getting_started_help = user.realm.uri + "/help/getting-started-with-zulip"
+            getting_started_string = (
+                _(
+                    "If you are new to Zulip, check out our [Getting started guide]({getting_started_url})!"
+                )
+            ).format(getting_started_url=getting_started_help)
+
+        organization_setup_string = ""
+        # Add extra content on setting up a new organization for administrators.
         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)
+            if education_organization:
+                organization_setup_help = user.realm.uri + "/help/setting-up-zulip-for-a-class"
+                organization_setup_string = (
+                    " "
+                    + _(
+                        "We also have a guide for [Setting up Zulip for a class]({organization_setup_url})."
+                    )
+                ).format(organization_setup_url=organization_setup_help)
+            else:
+                organization_setup_help = (
+                    user.realm.uri + "/help/getting-your-organization-started-with-zulip"
+                )
+                organization_setup_string = (
+                    " "
+                    + _(
+                        "We also have a guide for [Setting up your organization]({organization_setup_url})."
+                    )
+                ).format(organization_setup_url=organization_setup_help)
 
-        welcome_msg = _("Hello, and welcome to Zulip!") + "👋"
-        demo_org_warning = ""
-        if user.realm.demo_organization_scheduled_deletion_date is not None:
-            demo_org_help_url = user.realm.uri + "/help/demo-organizations"
-            demo_org_warning = (
+        demo_organization_warning_string = ""
+        # Add extra content about automatic deletion for demo organization owners.
+        if user.is_realm_owner and user.realm.demo_organization_scheduled_deletion_date is not None:
+            demo_organization_help = user.realm.uri + "/help/demo-organizations"
+            demo_organization_warning_string = (
                 _(
-                    "Note that this is a [demo organization]({demo_org_help_url}) and will be "
+                    "Note that this is a [demo organization]({demo_organization_help_url}) and will be "
                     "**automatically deleted** in 30 days."
                 )
                 + "\n\n"
-            ).format(demo_org_help_url=demo_org_help_url)
+            ).format(demo_organization_help_url=demo_organization_help)
 
         content = "".join(
             [
-                welcome_msg + " ",
+                _("Hello, and welcome to Zulip!") + "👋" + " ",
                 _("This is a direct message from me, Welcome Bot.") + "\n\n",
-                _(
-                    "If you are new to Zulip, check out our [Getting started guide]({getting_started_url})!"
-                ),
+                "{getting_started_text}",
                 "{organization_setup_text}\n\n",
-                "{demo_org_warning}",
+                "{demo_organization_text}",
                 _(
                     "I can also help you get set up! Just click anywhere on this message or press `r` to reply."
                 )
@@ -83,9 +118,9 @@ def send_initial_pms(user: UserProfile) -> None:
         )
 
     content = content.format(
-        organization_setup_text=organization_setup_text,
-        demo_org_warning=demo_org_warning,
-        getting_started_url="/help/getting-started-with-zulip",
+        getting_started_text=getting_started_string,
+        organization_setup_text=organization_setup_string,
+        demo_organization_text=demo_organization_warning_string,
     )
 
     internal_send_private_message(
diff --git a/zerver/management/commands/send_welcome_bot_message.py b/zerver/management/commands/send_welcome_bot_message.py
--- a/zerver/management/commands/send_welcome_bot_message.py
+++ b/zerver/management/commands/send_welcome_bot_message.py
@@ -2,7 +2,7 @@
 from typing import Any
 
 from zerver.lib.management import ZulipBaseCommand
-from zerver.lib.onboarding import send_initial_pms
+from zerver.lib.onboarding import send_initial_direct_message
 
 
 class Command(ZulipBaseCommand):
@@ -18,4 +18,4 @@ def add_arguments(self, parser: ArgumentParser) -> None:
 
     def handle(self, *args: Any, **options: str) -> None:
         for user_profile in self.get_users(options, self.get_realm(options), is_bot=False):
-            send_initial_pms(user_profile)
+            send_initial_direct_message(user_profile)


Test patch:
diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py
--- a/zerver/tests/test_signup.py
+++ b/zerver/tests/test_signup.py
@@ -1590,13 +1590,13 @@ def test_create_realm_with_marketing_emails_disabled(self) -> None:
         self.assertFalse(user.enable_marketing_emails)
 
     @override_settings(OPEN_REALM_CREATION=True)
-    def test_create_regular_realm_welcome_bot_pm(self) -> None:
+    def test_create_regular_realm_welcome_bot_direct_message(self) -> None:
         password = "test"
         string_id = "zuliptest"
         email = "user1@test.com"
         realm_name = "Test"
 
-        # Create new realm with the email
+        # Create new realm with the email.
         result = self.submit_realm_creation_form(
             email, realm_subdomain=string_id, realm_name=realm_name
         )
@@ -1623,28 +1623,36 @@ def test_create_regular_realm_welcome_bot_pm(self) -> None:
         )
         self.assertEqual(result.status_code, 302)
 
-        # Make sure the correct Welcome Bot PM is sent
+        # Make sure the correct Welcome Bot direct message is sent.
         welcome_msg = Message.objects.filter(
             sender__email="welcome-bot@zulip.com", recipient__type=Recipient.PERSONAL
         ).latest("id")
         self.assertTrue(welcome_msg.content.startswith("Hello, and welcome to Zulip!"))
+
+        # Organization type is not education or education_nonprofit,
+        # and organization is not a demo organization.
+        self.assertIn("Getting started guide", welcome_msg.content)
+        self.assertNotIn("Using Zulip for a class guide", welcome_msg.content)
         self.assertNotIn("demo organization", welcome_msg.content)
 
     @override_settings(OPEN_REALM_CREATION=True)
-    def test_create_demo_realm_welcome_bot_pm(self) -> None:
+    def test_create_education_demo_organiztion_welcome_bot_direct_message(self) -> None:
         password = "test"
         string_id = "zuliptest"
         email = "user1@test.com"
         realm_name = "Test"
 
-        # Create new realm with the email
+        # Create new realm with the email.
         result = self.submit_realm_creation_form(
-            email, realm_subdomain=string_id, realm_name=realm_name
+            email,
+            realm_subdomain=string_id,
+            realm_name=realm_name,
+            realm_type=Realm.ORG_TYPES["education"]["id"],
         )
         self.assertEqual(result.status_code, 302)
         self.assertTrue(
             result["Location"].endswith(
-                f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}"
+                f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=35&realm_subdomain={string_id}"
             )
         )
         result = self.client_get(result["Location"])
@@ -1661,15 +1669,20 @@ def test_create_demo_realm_welcome_bot_pm(self) -> None:
             realm_subdomain=string_id,
             realm_name=realm_name,
             enable_marketing_emails=False,
+            realm_type=Realm.ORG_TYPES["education"]["id"],
             is_demo_organization=True,
         )
         self.assertEqual(result.status_code, 302)
 
-        # Make sure the correct Welcome Bot PM is sent
+        # Make sure the correct Welcome Bot direct message is sent.
         welcome_msg = Message.objects.filter(
             sender__email="welcome-bot@zulip.com", recipient__type=Recipient.PERSONAL
         ).latest("id")
         self.assertTrue(welcome_msg.content.startswith("Hello, and welcome to Zulip!"))
+
+        # Organization type is education, and organization is a demo organization.
+        self.assertNotIn("Getting started guide", welcome_msg.content)
+        self.assertIn("Using Zulip for a class guide", welcome_msg.content)
         self.assertIn("demo organization", welcome_msg.content)
 
     @override_settings(OPEN_REALM_CREATION=True, FREE_TRIAL_DAYS=30)