We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
User avatars don't show in emails
At least for comment notifications, the avatar of the user who commented is just a blue box with a question mark regardless of whether they have a custom avatar or the default gravatar. We should check if this is happening for other notifications or if it's just the comment workflow email.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/sentry/notifications/utils/avatar.py`
Content:
```
1 from __future__ import annotations
2 
3 from django.urls import reverse
4 from django.utils.html import format_html
5 from django.utils.safestring import SafeString
6 
7 from sentry.models.avatars.user_avatar import UserAvatar
8 from sentry.models.user import User
9 from sentry.services.hybrid_cloud.user import RpcUser
10 from sentry.utils.assets import get_asset_url
11 from sentry.utils.avatar import get_email_avatar
12 from sentry.utils.http import absolute_uri
13 
14 
15 def get_user_avatar_url(user: User | RpcUser, size: int = 20) -> str:
16     ident: str
17     if isinstance(user, User):
18         try:
19             avatar = UserAvatar.objects.get(user=user)
20             ident = avatar.ident
21         except UserAvatar.DoesNotExist:
22             return ""
23     elif user.avatar:
24         if user.avatar is None:
25             return ""
26         ident = user.avatar.ident
27     else:
28         return ""
29 
30     url = reverse("sentry-user-avatar-url", args=[ident])
31     if size:
32         url = f"{url}?s={int(size)}"
33     return str(absolute_uri(url))
34 
35 
36 def get_sentry_avatar_url() -> str:
37     url = "/images/sentry-email-avatar.png"
38     return str(absolute_uri(get_asset_url("sentry", url)))
39 
40 
41 def avatar_as_html(user: User | RpcUser) -> SafeString:
42     if not user:
43         return format_html(
44             '<img class="avatar" src="{}" width="20px" height="20px" />', get_sentry_avatar_url()
45         )
46     avatar_type = user.get_avatar_type()
47     if avatar_type == "upload":
48         return format_html('<img class="avatar" src="{}" />', get_user_avatar_url(user))
49     elif avatar_type == "letter_avatar":
50         return get_email_avatar(user.get_display_name(), user.get_label(), 20, False)
51     else:
52         return get_email_avatar(user.get_display_name(), user.get_label(), 20, True)
53
```

Path: `src/sentry/notifications/notifications/activity/note.py`
Content:
```
1 from __future__ import annotations
2 
3 from typing import Any, Mapping, Optional
4 
5 from sentry.services.hybrid_cloud.actor import RpcActor
6 from sentry.types.integrations import ExternalProviders
7 
8 from .base import GroupActivityNotification
9 
10 
11 class NoteActivityNotification(GroupActivityNotification):
12     message_builder = "SlackNotificationsMessageBuilder"
13     metrics_key = "note_activity"
14     template_path = "sentry/emails/activity/note"
15 
16     def get_description(self) -> tuple[str, Optional[str], Mapping[str, Any]]:
17         # Notes may contain {} characters so we should escape them.
18         text = str(self.activity.data["text"]).replace("{", "{{").replace("}", "}}")
19         return text, None, {}
20 
21     @property
22     def title(self) -> str:
23         if self.user:
24             author = self.user.get_display_name()
25         else:
26             author = "Unknown"
27         return f"New comment by {author}"
28 
29     def get_notification_title(
30         self, provider: ExternalProviders, context: Mapping[str, Any] | None = None
31     ) -> str:
32         return self.title
33 
34     def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:
35         return self.get_context()["text_description"]
36
```
--- 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/src/sentry/notifications/notifications/activity/note.py b/src/sentry/notifications/notifications/activity/note.py
--- a/src/sentry/notifications/notifications/activity/note.py
+++ b/src/sentry/notifications/notifications/activity/note.py
@@ -2,6 +2,10 @@
 
 from typing import Any, Mapping, Optional
 
+from django.utils.html import format_html
+from django.utils.safestring import SafeString
+
+from sentry.notifications.utils.avatar import avatar_as_html
 from sentry.services.hybrid_cloud.actor import RpcActor
 from sentry.types.integrations import ExternalProviders
 
@@ -33,3 +37,15 @@
 
     def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:
         return self.get_context()["text_description"]
+
+    def description_as_html(self, description: str, params: Mapping[str, Any]) -> SafeString:
+        """Note emails are formatted differently from almost all other activity emails.
+        Rather than passing the `description` as a string to be formatted into HTML with
+        `author` and `an_issue` (see base definition and resolved.py's `get_description`
+        as an example) we are simply passed the comment as a string that needs no formatting,
+        and want the avatar on it's own rather than bundled with the author's display name
+        because the display name is already shown in the notification title."""
+        fmt = '<span class="avatar-container">{}</span>'
+        if self.user:
+            return format_html(fmt, avatar_as_html(self.user, 48))
+        return format_html(description)
diff --git a/src/sentry/notifications/utils/avatar.py b/src/sentry/notifications/utils/avatar.py
--- a/src/sentry/notifications/utils/avatar.py
+++ b/src/sentry/notifications/utils/avatar.py
@@ -38,15 +38,18 @@
     return str(absolute_uri(get_asset_url("sentry", url)))
 
 
-def avatar_as_html(user: User | RpcUser) -> SafeString:
+def avatar_as_html(user: User | RpcUser, size: int = 20) -> SafeString:
     if not user:
         return format_html(
-            '<img class="avatar" src="{}" width="20px" height="20px" />', get_sentry_avatar_url()
+            '<img class="avatar" src="{}" width="{}px" height="{}px" />',
+            get_sentry_avatar_url(),
+            size,
+            size,
         )
     avatar_type = user.get_avatar_type()
     if avatar_type == "upload":
         return format_html('<img class="avatar" src="{}" />', get_user_avatar_url(user))
     elif avatar_type == "letter_avatar":
-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, False)
+        return get_email_avatar(user.get_display_name(), user.get_label(), size, False)
     else:
-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, True)
+        return get_email_avatar(user.get_display_name(), user.get_label(), size, True)

{"golden_diff": "diff --git a/src/sentry/notifications/notifications/activity/note.py b/src/sentry/notifications/notifications/activity/note.py\n--- a/src/sentry/notifications/notifications/activity/note.py\n+++ b/src/sentry/notifications/notifications/activity/note.py\n@@ -2,6 +2,10 @@\n \n from typing import Any, Mapping, Optional\n \n+from django.utils.html import format_html\n+from django.utils.safestring import SafeString\n+\n+from sentry.notifications.utils.avatar import avatar_as_html\n from sentry.services.hybrid_cloud.actor import RpcActor\n from sentry.types.integrations import ExternalProviders\n \n@@ -33,3 +37,15 @@\n \n     def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:\n         return self.get_context()[\"text_description\"]\n+\n+    def description_as_html(self, description: str, params: Mapping[str, Any]) -> SafeString:\n+        \"\"\"Note emails are formatted differently from almost all other activity emails.\n+        Rather than passing the `description` as a string to be formatted into HTML with\n+        `author` and `an_issue` (see base definition and resolved.py's `get_description`\n+        as an example) we are simply passed the comment as a string that needs no formatting,\n+        and want the avatar on it's own rather than bundled with the author's display name\n+        because the display name is already shown in the notification title.\"\"\"\n+        fmt = '<span class=\"avatar-container\">{}</span>'\n+        if self.user:\n+            return format_html(fmt, avatar_as_html(self.user, 48))\n+        return format_html(description)\ndiff --git a/src/sentry/notifications/utils/avatar.py b/src/sentry/notifications/utils/avatar.py\n--- a/src/sentry/notifications/utils/avatar.py\n+++ b/src/sentry/notifications/utils/avatar.py\n@@ -38,15 +38,18 @@\n     return str(absolute_uri(get_asset_url(\"sentry\", url)))\n \n \n-def avatar_as_html(user: User | RpcUser) -> SafeString:\n+def avatar_as_html(user: User | RpcUser, size: int = 20) -> SafeString:\n     if not user:\n         return format_html(\n-            '<img class=\"avatar\" src=\"{}\" width=\"20px\" height=\"20px\" />', get_sentry_avatar_url()\n+            '<img class=\"avatar\" src=\"{}\" width=\"{}px\" height=\"{}px\" />',\n+            get_sentry_avatar_url(),\n+            size,\n+            size,\n         )\n     avatar_type = user.get_avatar_type()\n     if avatar_type == \"upload\":\n         return format_html('<img class=\"avatar\" src=\"{}\" />', get_user_avatar_url(user))\n     elif avatar_type == \"letter_avatar\":\n-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, False)\n+        return get_email_avatar(user.get_display_name(), user.get_label(), size, False)\n     else:\n-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, True)\n+        return get_email_avatar(user.get_display_name(), user.get_label(), size, True)\n", "issue": "User avatars don't show in emails\nAt least for comment notifications, the avatar of the user who commented is just a blue box with a question mark regardless of whether they have a custom avatar or the default gravatar. We should check if this is happening for other notifications or if it's just the comment workflow email.\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.safestring import SafeString\n\nfrom sentry.models.avatars.user_avatar import UserAvatar\nfrom sentry.models.user import User\nfrom sentry.services.hybrid_cloud.user import RpcUser\nfrom sentry.utils.assets import get_asset_url\nfrom sentry.utils.avatar import get_email_avatar\nfrom sentry.utils.http import absolute_uri\n\n\ndef get_user_avatar_url(user: User | RpcUser, size: int = 20) -> str:\n    ident: str\n    if isinstance(user, User):\n        try:\n            avatar = UserAvatar.objects.get(user=user)\n            ident = avatar.ident\n        except UserAvatar.DoesNotExist:\n            return \"\"\n    elif user.avatar:\n        if user.avatar is None:\n            return \"\"\n        ident = user.avatar.ident\n    else:\n        return \"\"\n\n    url = reverse(\"sentry-user-avatar-url\", args=[ident])\n    if size:\n        url = f\"{url}?s={int(size)}\"\n    return str(absolute_uri(url))\n\n\ndef get_sentry_avatar_url() -> str:\n    url = \"/images/sentry-email-avatar.png\"\n    return str(absolute_uri(get_asset_url(\"sentry\", url)))\n\n\ndef avatar_as_html(user: User | RpcUser) -> SafeString:\n    if not user:\n        return format_html(\n            '<img class=\"avatar\" src=\"{}\" width=\"20px\" height=\"20px\" />', get_sentry_avatar_url()\n        )\n    avatar_type = user.get_avatar_type()\n    if avatar_type == \"upload\":\n        return format_html('<img class=\"avatar\" src=\"{}\" />', get_user_avatar_url(user))\n    elif avatar_type == \"letter_avatar\":\n        return get_email_avatar(user.get_display_name(), user.get_label(), 20, False)\n    else:\n        return get_email_avatar(user.get_display_name(), user.get_label(), 20, True)\n", "path": "src/sentry/notifications/utils/avatar.py"}, {"content": "from __future__ import annotations\n\nfrom typing import Any, Mapping, Optional\n\nfrom sentry.services.hybrid_cloud.actor import RpcActor\nfrom sentry.types.integrations import ExternalProviders\n\nfrom .base import GroupActivityNotification\n\n\nclass NoteActivityNotification(GroupActivityNotification):\n    message_builder = \"SlackNotificationsMessageBuilder\"\n    metrics_key = \"note_activity\"\n    template_path = \"sentry/emails/activity/note\"\n\n    def get_description(self) -> tuple[str, Optional[str], Mapping[str, Any]]:\n        # Notes may contain {} characters so we should escape them.\n        text = str(self.activity.data[\"text\"]).replace(\"{\", \"{{\").replace(\"}\", \"}}\")\n        return text, None, {}\n\n    @property\n    def title(self) -> str:\n        if self.user:\n            author = self.user.get_display_name()\n        else:\n            author = \"Unknown\"\n        return f\"New comment by {author}\"\n\n    def get_notification_title(\n        self, provider: ExternalProviders, context: Mapping[str, Any] | None = None\n    ) -> str:\n        return self.title\n\n    def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:\n        return self.get_context()[\"text_description\"]\n", "path": "src/sentry/notifications/notifications/activity/note.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.safestring import SafeString\n\nfrom sentry.models.avatars.user_avatar import UserAvatar\nfrom sentry.models.user import User\nfrom sentry.services.hybrid_cloud.user import RpcUser\nfrom sentry.utils.assets import get_asset_url\nfrom sentry.utils.avatar import get_email_avatar\nfrom sentry.utils.http import absolute_uri\n\n\ndef get_user_avatar_url(user: User | RpcUser, size: int = 20) -> str:\n    ident: str\n    if isinstance(user, User):\n        try:\n            avatar = UserAvatar.objects.get(user=user)\n            ident = avatar.ident\n        except UserAvatar.DoesNotExist:\n            return \"\"\n    elif user.avatar:\n        if user.avatar is None:\n            return \"\"\n        ident = user.avatar.ident\n    else:\n        return \"\"\n\n    url = reverse(\"sentry-user-avatar-url\", args=[ident])\n    if size:\n        url = f\"{url}?s={int(size)}\"\n    return str(absolute_uri(url))\n\n\ndef get_sentry_avatar_url() -> str:\n    url = \"/images/sentry-email-avatar.png\"\n    return str(absolute_uri(get_asset_url(\"sentry\", url)))\n\n\ndef avatar_as_html(user: User | RpcUser, size: int = 20) -> SafeString:\n    if not user:\n        return format_html(\n            '<img class=\"avatar\" src=\"{}\" width=\"{}px\" height=\"{}px\" />',\n            get_sentry_avatar_url(),\n            size,\n            size,\n        )\n    avatar_type = user.get_avatar_type()\n    if avatar_type == \"upload\":\n        return format_html('<img class=\"avatar\" src=\"{}\" />', get_user_avatar_url(user))\n    elif avatar_type == \"letter_avatar\":\n        return get_email_avatar(user.get_display_name(), user.get_label(), size, False)\n    else:\n        return get_email_avatar(user.get_display_name(), user.get_label(), size, True)\n", "path": "src/sentry/notifications/utils/avatar.py"}, {"content": "from __future__ import annotations\n\nfrom typing import Any, Mapping, Optional\n\nfrom django.utils.html import format_html\nfrom django.utils.safestring import SafeString\n\nfrom sentry.notifications.utils.avatar import avatar_as_html\nfrom sentry.services.hybrid_cloud.actor import RpcActor\nfrom sentry.types.integrations import ExternalProviders\n\nfrom .base import GroupActivityNotification\n\n\nclass NoteActivityNotification(GroupActivityNotification):\n    message_builder = \"SlackNotificationsMessageBuilder\"\n    metrics_key = \"note_activity\"\n    template_path = \"sentry/emails/activity/note\"\n\n    def get_description(self) -> tuple[str, Optional[str], Mapping[str, Any]]:\n        # Notes may contain {} characters so we should escape them.\n        text = str(self.activity.data[\"text\"]).replace(\"{\", \"{{\").replace(\"}\", \"}}\")\n        return text, None, {}\n\n    @property\n    def title(self) -> str:\n        if self.user:\n            author = self.user.get_display_name()\n        else:\n            author = \"Unknown\"\n        return f\"New comment by {author}\"\n\n    def get_notification_title(\n        self, provider: ExternalProviders, context: Mapping[str, Any] | None = None\n    ) -> str:\n        return self.title\n\n    def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:\n        return self.get_context()[\"text_description\"]\n\n    def description_as_html(self, description: str, params: Mapping[str, Any]) -> SafeString:\n        \"\"\"Note emails are formatted differently from almost all other activity emails.\n        Rather than passing the `description` as a string to be formatted into HTML with\n        `author` and `an_issue` (see base definition and resolved.py's `get_description`\n        as an example) we are simply passed the comment as a string that needs no formatting,\n        and want the avatar on it's own rather than bundled with the author's display name\n        because the display name is already shown in the notification title.\"\"\"\n        fmt = '<span class=\"avatar-container\">{}</span>'\n        if self.user:\n            return format_html(fmt, avatar_as_html(self.user, 48))\n        return format_html(description)\n", "path": "src/sentry/notifications/notifications/activity/note.py"}]}