We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Go to newly created stream (with first-time modal)
Even after #29154, users find it hard to navigate to a newly created stream. To address this, we should:

1. Take the user directly to the stream they just created. To avoid a potentially confusing interleaved view, we should go to the most recent topic in the stream (currently "stream events", but might be "general chat" in the future).
2. The first time that a user creates a stream, show an explanatory modal (wording to be finalized when working on the PR):

----

## Stream **#{stream name}** created!

You will now see the stream you created. If you'd like to go back to stream settings, click on the name of the stream at the top of your Zulip window, or use the **back** button in your browser or desktop app.

[Continue]

---

Since we are changing the behavior, it's fine to show this once to existing users.

[CZO thread](https://chat.zulip.org/#narrow/stream/137-feedback/topic/user.20research.3A.20going.20to.20a.20new.20stream/near/1744305)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zerver/lib/hotspots.py`
Content:
```
1 # See https://zulip.readthedocs.io/en/latest/subsystems/hotspots.html
2 # for documentation on this subsystem.
3 from dataclasses import dataclass
4 from typing import Any, Dict, List, Optional, Union
5 
6 from django.conf import settings
7 from django.utils.translation import gettext_lazy
8 from django_stubs_ext import StrPromise
9 
10 from zerver.models import OnboardingStep, UserProfile
11 
12 
13 @dataclass
14 class Hotspot:
15     name: str
16     title: Optional[StrPromise]
17     description: Optional[StrPromise]
18     has_trigger: bool = False
19 
20     def to_dict(self, delay: float = 0) -> Dict[str, Union[str, float, bool]]:
21         return {
22             "type": "hotspot",
23             "name": self.name,
24             "title": str(self.title),
25             "description": str(self.description),
26             "delay": delay,
27             "has_trigger": self.has_trigger,
28         }
29 
30 
31 INTRO_HOTSPOTS: List[Hotspot] = [
32     Hotspot(
33         name="intro_streams",
34         title=gettext_lazy("Catch up on a stream"),
35         description=gettext_lazy(
36             "Messages sent to a stream are seen by everyone subscribed "
37             "to that stream. Try clicking on one of the stream links below."
38         ),
39     ),
40     Hotspot(
41         name="intro_topics",
42         title=gettext_lazy("Topics"),
43         description=gettext_lazy(
44             "Every message has a topic. Topics keep conversations "
45             "easy to follow, and make it easy to reply to conversations that start "
46             "while you are offline."
47         ),
48     ),
49     Hotspot(
50         # In theory, this should be renamed to intro_personal, since
51         # it's no longer attached to the gear menu, but renaming these
52         # requires a migration that is not worth doing at this time.
53         name="intro_gear",
54         title=gettext_lazy("Settings"),
55         description=gettext_lazy("Go to Settings to configure your notifications and preferences."),
56     ),
57     Hotspot(
58         name="intro_compose",
59         title=gettext_lazy("Compose"),
60         description=gettext_lazy(
61             "Click here to start a new conversation. Pick a topic "
62             "(2-3 words is best), and give it a go!"
63         ),
64     ),
65 ]
66 
67 
68 NON_INTRO_HOTSPOTS: List[Hotspot] = []
69 
70 
71 @dataclass
72 class OneTimeNotice:
73     name: str
74 
75     def to_dict(self) -> Dict[str, str]:
76         return {
77             "type": "one_time_notice",
78             "name": self.name,
79         }
80 
81 
82 ONE_TIME_NOTICES: List[OneTimeNotice] = [
83     OneTimeNotice(
84         name="visibility_policy_banner",
85     ),
86     OneTimeNotice(
87         name="intro_inbox_view_modal",
88     ),
89     OneTimeNotice(
90         name="intro_recent_view_modal",
91     ),
92 ]
93 
94 # We would most likely implement new hotspots in the future that aren't
95 # a part of the initial tutorial. To that end, classifying them into
96 # categories which are aggregated in ALL_HOTSPOTS, seems like a good start.
97 ALL_HOTSPOTS = [*INTRO_HOTSPOTS, *NON_INTRO_HOTSPOTS]
98 ALL_ONBOARDING_STEPS: List[Union[Hotspot, OneTimeNotice]] = [*ALL_HOTSPOTS, *ONE_TIME_NOTICES]
99 
100 
101 def get_next_onboarding_steps(user: UserProfile) -> List[Dict[str, Any]]:
102     # For manual testing, it can be convenient to set
103     # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to
104     # make it easy to click on all of the hotspots.
105     #
106     # Since this is just for development purposes, it's convenient for us to send
107     # all the hotspots rather than any specific category.
108     if settings.ALWAYS_SEND_ALL_HOTSPOTS:
109         return [hotspot.to_dict() for hotspot in ALL_HOTSPOTS]
110 
111     # If a Zulip server has disabled the tutorial, never send hotspots.
112     if not settings.TUTORIAL_ENABLED:
113         return []
114 
115     seen_onboarding_steps = frozenset(
116         OnboardingStep.objects.filter(user=user).values_list("onboarding_step", flat=True)
117     )
118 
119     onboarding_steps: List[Dict[str, Any]] = [hotspot.to_dict() for hotspot in NON_INTRO_HOTSPOTS]
120 
121     for one_time_notice in ONE_TIME_NOTICES:
122         if one_time_notice.name in seen_onboarding_steps:
123             continue
124         onboarding_steps.append(one_time_notice.to_dict())
125 
126     if user.tutorial_status == UserProfile.TUTORIAL_FINISHED:
127         return onboarding_steps
128 
129     for hotspot in INTRO_HOTSPOTS:
130         if hotspot.name in seen_onboarding_steps:
131             continue
132 
133         onboarding_steps.append(hotspot.to_dict(delay=0.5))
134         return onboarding_steps
135 
136     user.tutorial_status = UserProfile.TUTORIAL_FINISHED
137     user.save(update_fields=["tutorial_status"])
138     return onboarding_steps
139 
140 
141 def copy_hotspots(source_profile: UserProfile, target_profile: UserProfile) -> None:
142     for userhotspot in frozenset(OnboardingStep.objects.filter(user=source_profile)):
143         OnboardingStep.objects.create(
144             user=target_profile,
145             onboarding_step=userhotspot.onboarding_step,
146             timestamp=userhotspot.timestamp,
147         )
148 
149     target_profile.tutorial_status = source_profile.tutorial_status
150     target_profile.onboarding_steps = source_profile.onboarding_steps
151     target_profile.save(update_fields=["tutorial_status", "onboarding_steps"])
152
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zerver/lib/hotspots.py b/zerver/lib/hotspots.py
--- a/zerver/lib/hotspots.py
+++ b/zerver/lib/hotspots.py
@@ -89,6 +89,9 @@
     OneTimeNotice(
         name="intro_recent_view_modal",
     ),
+    OneTimeNotice(
+        name="first_stream_created_banner",
+    ),
 ]
 
 # We would most likely implement new hotspots in the future that aren't

{"golden_diff": "diff --git a/zerver/lib/hotspots.py b/zerver/lib/hotspots.py\n--- a/zerver/lib/hotspots.py\n+++ b/zerver/lib/hotspots.py\n@@ -89,6 +89,9 @@\n     OneTimeNotice(\n         name=\"intro_recent_view_modal\",\n     ),\n+    OneTimeNotice(\n+        name=\"first_stream_created_banner\",\n+    ),\n ]\n \n # We would most likely implement new hotspots in the future that aren't\n", "issue": "Go to newly created stream (with first-time modal)\nEven after #29154, users find it hard to navigate to a newly created stream. To address this, we should:\r\n\r\n1. Take the user directly to the stream they just created. To avoid a potentially confusing interleaved view, we should go to the most recent topic in the stream (currently \"stream events\", but might be \"general chat\" in the future).\r\n2. The first time that a user creates a stream, show an explanatory modal (wording to be finalized when working on the PR):\r\n\r\n----\r\n\r\n## Stream **#{stream name}** created!\r\n\r\nYou will now see the stream you created. If you'd like to go back to stream settings, click on the name of the stream at the top of your Zulip window, or use the **back** button in your browser or desktop app.\r\n\r\n[Continue]\r\n\r\n---\r\n\r\nSince we are changing the behavior, it's fine to show this once to existing users.\r\n\r\n[CZO thread](https://chat.zulip.org/#narrow/stream/137-feedback/topic/user.20research.3A.20going.20to.20a.20new.20stream/near/1744305)\n", "before_files": [{"content": "# See https://zulip.readthedocs.io/en/latest/subsystems/hotspots.html\n# for documentation on this subsystem.\nfrom dataclasses import dataclass\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom django.conf import settings\nfrom django.utils.translation import gettext_lazy\nfrom django_stubs_ext import StrPromise\n\nfrom zerver.models import OnboardingStep, UserProfile\n\n\n@dataclass\nclass Hotspot:\n    name: str\n    title: Optional[StrPromise]\n    description: Optional[StrPromise]\n    has_trigger: bool = False\n\n    def to_dict(self, delay: float = 0) -> Dict[str, Union[str, float, bool]]:\n        return {\n            \"type\": \"hotspot\",\n            \"name\": self.name,\n            \"title\": str(self.title),\n            \"description\": str(self.description),\n            \"delay\": delay,\n            \"has_trigger\": self.has_trigger,\n        }\n\n\nINTRO_HOTSPOTS: List[Hotspot] = [\n    Hotspot(\n        name=\"intro_streams\",\n        title=gettext_lazy(\"Catch up on a stream\"),\n        description=gettext_lazy(\n            \"Messages sent to a stream are seen by everyone subscribed \"\n            \"to that stream. Try clicking on one of the stream links below.\"\n        ),\n    ),\n    Hotspot(\n        name=\"intro_topics\",\n        title=gettext_lazy(\"Topics\"),\n        description=gettext_lazy(\n            \"Every message has a topic. Topics keep conversations \"\n            \"easy to follow, and make it easy to reply to conversations that start \"\n            \"while you are offline.\"\n        ),\n    ),\n    Hotspot(\n        # In theory, this should be renamed to intro_personal, since\n        # it's no longer attached to the gear menu, but renaming these\n        # requires a migration that is not worth doing at this time.\n        name=\"intro_gear\",\n        title=gettext_lazy(\"Settings\"),\n        description=gettext_lazy(\"Go to Settings to configure your notifications and preferences.\"),\n    ),\n    Hotspot(\n        name=\"intro_compose\",\n        title=gettext_lazy(\"Compose\"),\n        description=gettext_lazy(\n            \"Click here to start a new conversation. Pick a topic \"\n            \"(2-3 words is best), and give it a go!\"\n        ),\n    ),\n]\n\n\nNON_INTRO_HOTSPOTS: List[Hotspot] = []\n\n\n@dataclass\nclass OneTimeNotice:\n    name: str\n\n    def to_dict(self) -> Dict[str, str]:\n        return {\n            \"type\": \"one_time_notice\",\n            \"name\": self.name,\n        }\n\n\nONE_TIME_NOTICES: List[OneTimeNotice] = [\n    OneTimeNotice(\n        name=\"visibility_policy_banner\",\n    ),\n    OneTimeNotice(\n        name=\"intro_inbox_view_modal\",\n    ),\n    OneTimeNotice(\n        name=\"intro_recent_view_modal\",\n    ),\n]\n\n# We would most likely implement new hotspots in the future that aren't\n# a part of the initial tutorial. To that end, classifying them into\n# categories which are aggregated in ALL_HOTSPOTS, seems like a good start.\nALL_HOTSPOTS = [*INTRO_HOTSPOTS, *NON_INTRO_HOTSPOTS]\nALL_ONBOARDING_STEPS: List[Union[Hotspot, OneTimeNotice]] = [*ALL_HOTSPOTS, *ONE_TIME_NOTICES]\n\n\ndef get_next_onboarding_steps(user: UserProfile) -> List[Dict[str, Any]]:\n    # For manual testing, it can be convenient to set\n    # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to\n    # make it easy to click on all of the hotspots.\n    #\n    # Since this is just for development purposes, it's convenient for us to send\n    # all the hotspots rather than any specific category.\n    if settings.ALWAYS_SEND_ALL_HOTSPOTS:\n        return [hotspot.to_dict() for hotspot in ALL_HOTSPOTS]\n\n    # If a Zulip server has disabled the tutorial, never send hotspots.\n    if not settings.TUTORIAL_ENABLED:\n        return []\n\n    seen_onboarding_steps = frozenset(\n        OnboardingStep.objects.filter(user=user).values_list(\"onboarding_step\", flat=True)\n    )\n\n    onboarding_steps: List[Dict[str, Any]] = [hotspot.to_dict() for hotspot in NON_INTRO_HOTSPOTS]\n\n    for one_time_notice in ONE_TIME_NOTICES:\n        if one_time_notice.name in seen_onboarding_steps:\n            continue\n        onboarding_steps.append(one_time_notice.to_dict())\n\n    if user.tutorial_status == UserProfile.TUTORIAL_FINISHED:\n        return onboarding_steps\n\n    for hotspot in INTRO_HOTSPOTS:\n        if hotspot.name in seen_onboarding_steps:\n            continue\n\n        onboarding_steps.append(hotspot.to_dict(delay=0.5))\n        return onboarding_steps\n\n    user.tutorial_status = UserProfile.TUTORIAL_FINISHED\n    user.save(update_fields=[\"tutorial_status\"])\n    return onboarding_steps\n\n\ndef copy_hotspots(source_profile: UserProfile, target_profile: UserProfile) -> None:\n    for userhotspot in frozenset(OnboardingStep.objects.filter(user=source_profile)):\n        OnboardingStep.objects.create(\n            user=target_profile,\n            onboarding_step=userhotspot.onboarding_step,\n            timestamp=userhotspot.timestamp,\n        )\n\n    target_profile.tutorial_status = source_profile.tutorial_status\n    target_profile.onboarding_steps = source_profile.onboarding_steps\n    target_profile.save(update_fields=[\"tutorial_status\", \"onboarding_steps\"])\n", "path": "zerver/lib/hotspots.py"}], "after_files": [{"content": "# See https://zulip.readthedocs.io/en/latest/subsystems/hotspots.html\n# for documentation on this subsystem.\nfrom dataclasses import dataclass\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom django.conf import settings\nfrom django.utils.translation import gettext_lazy\nfrom django_stubs_ext import StrPromise\n\nfrom zerver.models import OnboardingStep, UserProfile\n\n\n@dataclass\nclass Hotspot:\n    name: str\n    title: Optional[StrPromise]\n    description: Optional[StrPromise]\n    has_trigger: bool = False\n\n    def to_dict(self, delay: float = 0) -> Dict[str, Union[str, float, bool]]:\n        return {\n            \"type\": \"hotspot\",\n            \"name\": self.name,\n            \"title\": str(self.title),\n            \"description\": str(self.description),\n            \"delay\": delay,\n            \"has_trigger\": self.has_trigger,\n        }\n\n\nINTRO_HOTSPOTS: List[Hotspot] = [\n    Hotspot(\n        name=\"intro_streams\",\n        title=gettext_lazy(\"Catch up on a stream\"),\n        description=gettext_lazy(\n            \"Messages sent to a stream are seen by everyone subscribed \"\n            \"to that stream. Try clicking on one of the stream links below.\"\n        ),\n    ),\n    Hotspot(\n        name=\"intro_topics\",\n        title=gettext_lazy(\"Topics\"),\n        description=gettext_lazy(\n            \"Every message has a topic. Topics keep conversations \"\n            \"easy to follow, and make it easy to reply to conversations that start \"\n            \"while you are offline.\"\n        ),\n    ),\n    Hotspot(\n        # In theory, this should be renamed to intro_personal, since\n        # it's no longer attached to the gear menu, but renaming these\n        # requires a migration that is not worth doing at this time.\n        name=\"intro_gear\",\n        title=gettext_lazy(\"Settings\"),\n        description=gettext_lazy(\"Go to Settings to configure your notifications and preferences.\"),\n    ),\n    Hotspot(\n        name=\"intro_compose\",\n        title=gettext_lazy(\"Compose\"),\n        description=gettext_lazy(\n            \"Click here to start a new conversation. Pick a topic \"\n            \"(2-3 words is best), and give it a go!\"\n        ),\n    ),\n]\n\n\nNON_INTRO_HOTSPOTS: List[Hotspot] = []\n\n\n@dataclass\nclass OneTimeNotice:\n    name: str\n\n    def to_dict(self) -> Dict[str, str]:\n        return {\n            \"type\": \"one_time_notice\",\n            \"name\": self.name,\n        }\n\n\nONE_TIME_NOTICES: List[OneTimeNotice] = [\n    OneTimeNotice(\n        name=\"visibility_policy_banner\",\n    ),\n    OneTimeNotice(\n        name=\"intro_inbox_view_modal\",\n    ),\n    OneTimeNotice(\n        name=\"intro_recent_view_modal\",\n    ),\n    OneTimeNotice(\n        name=\"first_stream_created_banner\",\n    ),\n]\n\n# We would most likely implement new hotspots in the future that aren't\n# a part of the initial tutorial. To that end, classifying them into\n# categories which are aggregated in ALL_HOTSPOTS, seems like a good start.\nALL_HOTSPOTS = [*INTRO_HOTSPOTS, *NON_INTRO_HOTSPOTS]\nALL_ONBOARDING_STEPS: List[Union[Hotspot, OneTimeNotice]] = [*ALL_HOTSPOTS, *ONE_TIME_NOTICES]\n\n\ndef get_next_onboarding_steps(user: UserProfile) -> List[Dict[str, Any]]:\n    # For manual testing, it can be convenient to set\n    # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to\n    # make it easy to click on all of the hotspots.\n    #\n    # Since this is just for development purposes, it's convenient for us to send\n    # all the hotspots rather than any specific category.\n    if settings.ALWAYS_SEND_ALL_HOTSPOTS:\n        return [hotspot.to_dict() for hotspot in ALL_HOTSPOTS]\n\n    # If a Zulip server has disabled the tutorial, never send hotspots.\n    if not settings.TUTORIAL_ENABLED:\n        return []\n\n    seen_onboarding_steps = frozenset(\n        OnboardingStep.objects.filter(user=user).values_list(\"onboarding_step\", flat=True)\n    )\n\n    onboarding_steps: List[Dict[str, Any]] = [hotspot.to_dict() for hotspot in NON_INTRO_HOTSPOTS]\n\n    for one_time_notice in ONE_TIME_NOTICES:\n        if one_time_notice.name in seen_onboarding_steps:\n            continue\n        onboarding_steps.append(one_time_notice.to_dict())\n\n    if user.tutorial_status == UserProfile.TUTORIAL_FINISHED:\n        return onboarding_steps\n\n    for hotspot in INTRO_HOTSPOTS:\n        if hotspot.name in seen_onboarding_steps:\n            continue\n\n        onboarding_steps.append(hotspot.to_dict(delay=0.5))\n        return onboarding_steps\n\n    user.tutorial_status = UserProfile.TUTORIAL_FINISHED\n    user.save(update_fields=[\"tutorial_status\"])\n    return onboarding_steps\n\n\ndef copy_hotspots(source_profile: UserProfile, target_profile: UserProfile) -> None:\n    for userhotspot in frozenset(OnboardingStep.objects.filter(user=source_profile)):\n        OnboardingStep.objects.create(\n            user=target_profile,\n            onboarding_step=userhotspot.onboarding_step,\n            timestamp=userhotspot.timestamp,\n        )\n\n    target_profile.tutorial_status = source_profile.tutorial_status\n    target_profile.onboarding_steps = source_profile.onboarding_steps\n    target_profile.save(update_fields=[\"tutorial_status\", \"onboarding_steps\"])\n", "path": "zerver/lib/hotspots.py"}]}