diff --git a/static/images/email-gateway-bot.png b/static/images/email-gateway-bot.png new file mode 100644 index 0000000000..ce10f48fc2 Binary files /dev/null and b/static/images/email-gateway-bot.png differ diff --git a/static/images/welcome-bot.png b/static/images/welcome-bot.png new file mode 100644 index 0000000000..4a65e65d97 Binary files /dev/null and b/static/images/welcome-bot.png differ diff --git a/zerver/lib/avatar.py b/zerver/lib/avatar.py index a53b163e5a..69c12cf77d 100644 --- a/zerver/lib/avatar.py +++ b/zerver/lib/avatar.py @@ -13,6 +13,12 @@ from zerver.lib.upload import get_avatar_url from zerver.lib.url_encoding import append_url_query_string from zerver.models import UserProfile +SYSTEM_BOTS_AVATAR_FILES = { + settings.WELCOME_BOT: "images/welcome-bot.png", + settings.NOTIFICATION_BOT: "images/logo/zulip-icon-square.svg", + settings.EMAIL_GATEWAY_BOT: "images/email-gateway-bot.png", +} + def avatar_url( user_profile: UserProfile, medium: bool = False, client_gravatar: bool = False @@ -54,6 +60,11 @@ def get_avatar_field( computing them on the server (mostly to save bandwidth). """ + # System bots have hardcoded avatars + system_bot_avatar = SYSTEM_BOTS_AVATAR_FILES.get(email) + if system_bot_avatar: + return staticfiles_storage.url(system_bot_avatar) + """ If our client knows how to calculate gravatar hashes, we will return None and let the client compute the gravatar diff --git a/zerver/lib/server_initialization.py b/zerver/lib/server_initialization.py index a3100f2f8d..1ee249453b 100644 --- a/zerver/lib/server_initialization.py +++ b/zerver/lib/server_initialization.py @@ -70,6 +70,9 @@ def create_internal_realm() -> None: bots = UserProfile.objects.filter(email__in=[bot_info[1] for bot_info in internal_bots]) for bot in bots: bot.bot_owner = bot + # Avatars for system bots are hardcoded, so make sure gravatar + # won't be used.. + bot.avatar_source = "U" bot.save() # Initialize the email gateway bot as able to forge senders. diff --git a/zerver/migrations/0615_system_bot_avatars.py b/zerver/migrations/0615_system_bot_avatars.py new file mode 100644 index 0000000000..04fd6e7773 --- /dev/null +++ b/zerver/migrations/0615_system_bot_avatars.py @@ -0,0 +1,46 @@ +# Generated by Django 5.0.9 on 2024-10-14 23:22 + +from django.conf import settings +from django.db import migrations +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.migrations.state import StateApps + + +def set_system_bot_avatar_source_user( + apps: StateApps, schema_editor: BaseDatabaseSchemaEditor +) -> None: + UserProfile = apps.get_model("zerver", "UserProfile") + UserProfile.objects.filter( + email__in=[ + settings.EMAIL_GATEWAY_BOT, + settings.NOTIFICATION_BOT, + settings.WELCOME_BOT, + ] + ).update(avatar_source="U") + + +def set_system_bot_avatar_source_gravatar( + apps: StateApps, schema_editor: BaseDatabaseSchemaEditor +) -> None: + UserProfile = apps.get_model("zerver", "UserProfile") + UserProfile.objects.filter( + email__in=[ + settings.EMAIL_GATEWAY_BOT, + settings.NOTIFICATION_BOT, + settings.WELCOME_BOT, + ] + ).update(avatar_source="G") + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0614_remove_realm_move_messages_between_streams_policy"), + ] + + operations = [ + migrations.RunPython( + set_system_bot_avatar_source_user, + elidable=True, + reverse_code=set_system_bot_avatar_source_gravatar, + ), + ] diff --git a/zerver/tests/test_upload.py b/zerver/tests/test_upload.py index 264ee46174..a6acaa5021 100644 --- a/zerver/tests/test_upload.py +++ b/zerver/tests/test_upload.py @@ -1256,12 +1256,12 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase): # Test cross_realm_bot avatar access using email. response = self.api_get(hamlet, "/avatar/welcome-bot@zulip.com", {"foo": "bar"}) redirect_url = response["Location"] - self.assertTrue(redirect_url.endswith(str(avatar_url(cross_realm_bot)) + "&foo=bar")) + self.assertTrue(redirect_url.endswith(str(avatar_url(cross_realm_bot)) + "?foo=bar")) # Test cross_realm_bot avatar access using id. response = self.api_get(hamlet, f"/avatar/{cross_realm_bot.id}", {"foo": "bar"}) redirect_url = response["Location"] - self.assertTrue(redirect_url.endswith(str(avatar_url(cross_realm_bot)) + "&foo=bar")) + self.assertTrue(redirect_url.endswith(str(avatar_url(cross_realm_bot)) + "?foo=bar")) # Without spectators enabled, no unauthenticated access. response = self.client_get("/avatar/cordelia@zulip.com", {"foo": "bar"})