diff --git a/discord_rss_bot/custom_message.py b/discord_rss_bot/custom_message.py index 2ec3a17..a9411fc 100644 --- a/discord_rss_bot/custom_message.py +++ b/discord_rss_bot/custom_message.py @@ -41,6 +41,8 @@ class CustomEmbed: footer_text: str = "" footer_icon_url: str = "" show_steam_game_icon_in_thumbnail: bool = True + avatar_url: str = "" + username: str = "" def try_to_replace(custom_message: str, template: str, replace_with: str) -> str: @@ -313,7 +315,6 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb embed.thumbnail_url = embed.thumbnail_url.replace("\\n", "\n") embed.footer_text = embed.footer_text.replace("\\n", "\n") embed.footer_icon_url = embed.footer_icon_url.replace("\\n", "\n") - return embed @@ -334,6 +335,8 @@ def _replace_embed_tags(embed: CustomEmbed, template: str, replace_with: str) -> embed.thumbnail_url = try_to_replace(embed.thumbnail_url, template, replace_with) embed.footer_text = try_to_replace(embed.footer_text, template, replace_with) embed.footer_icon_url = try_to_replace(embed.footer_icon_url, template, replace_with) + embed.avatar_url = try_to_replace(embed.avatar_url, template, replace_with) + embed.username = try_to_replace(embed.username, template, replace_with) def get_custom_message(reader: Reader, feed: Feed) -> str: @@ -461,6 +464,8 @@ def save_embed(reader: Reader, feed: Feed, embed: CustomEmbed) -> None: "footer_text": embed.footer_text, "footer_icon_url": embed.footer_icon_url, "show_steam_game_icon_in_thumbnail": embed.show_steam_game_icon_in_thumbnail, + "avatar_url": embed.avatar_url, + "username": embed.username, } reader.set_tag(feed, "embed", json.dumps(embed_dict)) # pyright: ignore[reportArgumentType] @@ -495,6 +500,8 @@ def get_embed(reader: Reader, feed: Feed) -> CustomEmbed: footer_text="", footer_icon_url="", show_steam_game_icon_in_thumbnail=True, + avatar_url="", + username="", ) @@ -521,6 +528,8 @@ def get_embed_data(embed_data: dict[str, str | int | bool]) -> CustomEmbed: "show_steam_game_icon_in_thumbnail", True, ) + avatar_url: str = str(embed_data.get("avatar_url", "")) + username: str = str(embed_data.get("username", "")) return CustomEmbed( title=title, @@ -534,4 +543,6 @@ def get_embed_data(embed_data: dict[str, str | int | bool]) -> CustomEmbed: footer_text=footer_text, footer_icon_url=footer_icon_url, show_steam_game_icon_in_thumbnail=show_steam_game_icon_in_thumbnail, + avatar_url=avatar_url, + username=username, ) diff --git a/discord_rss_bot/extensions/hoyolab.py b/discord_rss_bot/extensions/hoyolab.py index 9cbc832..4078cc0 100644 --- a/discord_rss_bot/extensions/hoyolab.py +++ b/discord_rss_bot/extensions/hoyolab.py @@ -454,13 +454,19 @@ class HoyolabExtension(FeedExtension): webhook.add_file(file=video_response.content, filename=f"{entry.id}.mp4") def _apply_author_from_post(self, webhook: DiscordWebhook, post_data: JsonObject) -> None: - """Set webhook author (username and avatar) from post user data.""" + """Set webhook author (username and avatar) from post user data. + + Only sets values that haven't been explicitly configured via + the feed's embed or message template settings. + """ user: JsonObject = _as_json_object(post_data.get("user")) author_name: str = str(user.get("nickname", "")) avatar_url: str = str(user.get("avatar_url", "")) if author_name: - webhook.avatar_url = avatar_url - webhook.username = author_name + if not webhook.username: + webhook.username = author_name + if not webhook.avatar_url: + webhook.avatar_url = avatar_url def _apply_structured_content(self, webhook: DiscordWebhook, post_data: JsonObject) -> None: """Parse structured content for YouTube embeds and add them to the webhook.""" diff --git a/discord_rss_bot/feeds.py b/discord_rss_bot/feeds.py index 6de7b0a..5d6da2f 100644 --- a/discord_rss_bot/feeds.py +++ b/discord_rss_bot/feeds.py @@ -52,6 +52,7 @@ from discord_rss_bot.custom_message import get_custom_message from discord_rss_bot.custom_message import get_image_urls from discord_rss_bot.custom_message import get_validated_message_avatar_url from discord_rss_bot.custom_message import get_validated_message_username +from discord_rss_bot.custom_message import normalize_message_username from discord_rss_bot.custom_message import replace_tags_in_embed from discord_rss_bot.custom_message import replace_tags_in_text_message from discord_rss_bot.extensions import auto_enable_extensions_for_feed @@ -1754,12 +1755,14 @@ def create_components_v2_webhook( }, create_media_gallery_component(media_items), ] - return DiscordWebhook( + webhook: DiscordWebhook = DiscordWebhook( url=webhook_url, flags=1 << 15, components=components, rate_limit_retry=True, ) + _apply_embed_identity(webhook, custom_embed) + return webhook def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches] @@ -1851,10 +1854,25 @@ def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches] if custom_embed.footer_icon_url and not custom_embed.footer_text: discord_embed.set_footer(text="-", icon_url=custom_embed.footer_icon_url) + _apply_embed_identity(webhook, custom_embed) webhook.add_embed(discord_embed) return webhook +def _apply_embed_identity(webhook: DiscordWebhook, custom_embed: CustomEmbed) -> None: + """Apply the embed's avatar_url and username to the webhook. + + Sets the webhook-level avatar and display name from the custom embed + configuration, if valid. These can be overridden later by per-feed + ``message_avatar_url`` / ``message_username`` tags. + """ + if custom_embed.avatar_url and is_url_valid(custom_embed.avatar_url): + webhook.avatar_url = custom_embed.avatar_url + embed_username: str = normalize_message_username(custom_embed.username) + if embed_username: + webhook.username = embed_username + + def get_webhook_url(reader: Reader, entry: Entry) -> str: """Get the webhook URL for the entry. diff --git a/discord_rss_bot/main.py b/discord_rss_bot/main.py index 4197c1c..c053f76 100644 --- a/discord_rss_bot/main.py +++ b/discord_rss_bot/main.py @@ -1506,6 +1506,8 @@ async def get_embed_page( "thumbnail_url": embed.thumbnail_url, "footer_text": embed.footer_text, "footer_icon_url": embed.footer_icon_url, + "avatar_url": embed.avatar_url, + "username": embed.username, "show_steam_game_icon_in_thumbnail": embed.show_steam_game_icon_in_thumbnail, "is_steam_feed": is_steam_feed_url(feed.url or ""), "extension_variables": extension_variables, @@ -1520,7 +1522,7 @@ async def get_embed_page( @app.post("/embed", response_class=HTMLResponse) -async def post_embed( # ruff:ignore[complex-structure] +async def post_embed( # ruff:ignore[complex-structure, too-many-branches] feed_url: Annotated[str, Form()], reader: Annotated[Reader, Depends(get_reader_dependency)], title: Annotated[str, Form()] = "", @@ -1534,6 +1536,8 @@ async def post_embed( # ruff:ignore[complex-structure] footer_text: Annotated[str, Form()] = "", footer_icon_url: Annotated[str, Form()] = "", show_steam_game_icon_in_thumbnail: Annotated[str, Form()] = "", + avatar_url: Annotated[str, Form()] = "", + username: Annotated[str, Form()] = "", ) -> RedirectResponse: """Set the embed settings. @@ -1550,6 +1554,8 @@ async def post_embed( # ruff:ignore[complex-structure] footer_text: The footer text of the embed. footer_icon_url: The footer icon url of the embed. show_steam_game_icon_in_thumbnail: Whether to use Steam game icon as thumbnail. + avatar_url: Optional webhook avatar URL override for embed messages. + username: Optional webhook display name override for embed messages. reader: The Reader instance. Returns: @@ -1580,6 +1586,10 @@ async def post_embed( # ruff:ignore[complex-structure] custom_embed.footer_text = footer_text if footer_icon_url != custom_embed.footer_icon_url: custom_embed.footer_icon_url = footer_icon_url + if avatar_url != custom_embed.avatar_url: + custom_embed.avatar_url = avatar_url + if username != custom_embed.username: + custom_embed.username = username # Handle the steam thumbnail toggle (checkbox: "true" when checked, "" when unchecked). new_steam_icon_toggle: bool = show_steam_game_icon_in_thumbnail.strip().lower() in {"true", "on", "1"} diff --git a/discord_rss_bot/templates/embed.html b/discord_rss_bot/templates/embed.html index e5ce5da..ff2726d 100644 --- a/discord_rss_bot/templates/embed.html +++ b/discord_rss_bot/templates/embed.html @@ -238,6 +238,8 @@
@, #, :, or `; cannot contain clyde or discord.http:// or https:// image URL.