Add webhook avatar and username to embed settings
All checks were successful
Test and build Docker image / docker (push) Successful in 1m40s
All checks were successful
Test and build Docker image / docker (push) Successful in 1m40s
Allow configuring the webhook identity (name and avatar) directly from the embed settings page. The embed-level values are applied before per-feed tag overrides and extension overrides, so they act as a baseline that can still be overridden by the custom message page or extensions.
This commit is contained in:
parent
347aa3fdcc
commit
e2c120d99f
5 changed files with 96 additions and 21 deletions
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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."""
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,8 @@
|
|||
<li>You can remove the embed from links by adding < and > around the link. (For example <{% raw %}{{entry_link}}{% endraw %}>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr class="border-secondary my-3" />
|
||||
<h5>Embed content</h5>
|
||||
<label for="title" class="col-sm-6 col-form-label">Title</label>
|
||||
<input name="title" type="text" class="form-control bg-dark border-dark text-muted" id="title"
|
||||
{% if title %} value="{{- title -}}" {% endif %} />
|
||||
|
|
@ -247,15 +249,6 @@
|
|||
<label for="color" class="col-sm-6 col-form-label">Embed color</label>
|
||||
<input name="color" type="color" class="form-control form-control-color bg-dark border-dark text-muted"
|
||||
id="color" {% if color %} value="{{- color -}}" {% endif %} />
|
||||
<label for="author_name" class="col-sm-6 col-form-label">Author name</label>
|
||||
<input name="author_name" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="author_name" {% if author_name %} value="{{- author_name -}}" {% endif %} />
|
||||
<label for="author_url" class="col-sm-6 col-form-label">Author URL</label>
|
||||
<input name="author_url" type="text" class="form-control bg-dark border-dark text-muted" id="author_url"
|
||||
{% if author_url %} value="{{- author_url -}}" {% endif %} />
|
||||
<label for="author_icon_url" class="col-sm-6 col-form-label">Author icon URL</label>
|
||||
<input name="author_icon_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="author_icon_url" {% if author_icon_url %} value="{{- author_icon_url -}}" {% endif %} />
|
||||
<label for="image_url" class="col-sm-6 col-form-label">Image URL</label>
|
||||
<input name="image_url" type="text" class="form-control bg-dark border-dark text-muted" id="image_url"
|
||||
{% if image_url %} value="{{- image_url -}}" {% endif %} />
|
||||
|
|
@ -265,7 +258,31 @@
|
|||
<label for="thumbnail_url" class="col-sm-6 col-form-label">Thumbnail</label>
|
||||
<input name="thumbnail_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="thumbnail_url" {% if thumbnail_url %} value="{{- thumbnail_url -}}" {% endif %} />
|
||||
|
||||
<hr class="border-secondary my-3" />
|
||||
<h5>Author</h5>
|
||||
<label for="author_name" class="col-sm-6 col-form-label">Author name</label>
|
||||
<input name="author_name" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="author_name" {% if author_name %} value="{{- author_name -}}" {% endif %} />
|
||||
<label for="author_url" class="col-sm-6 col-form-label">Author URL</label>
|
||||
<input name="author_url" type="text" class="form-control bg-dark border-dark text-muted" id="author_url"
|
||||
{% if author_url %} value="{{- author_url -}}" {% endif %} />
|
||||
<label for="author_icon_url" class="col-sm-6 col-form-label">Author icon URL</label>
|
||||
<input name="author_icon_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="author_icon_url" {% if author_icon_url %} value="{{- author_icon_url -}}" {% endif %} />
|
||||
|
||||
<hr class="border-secondary my-3" />
|
||||
<h5>Footer</h5>
|
||||
<label for="footer_text" class="col-sm-6 col-form-label">Footer text</label>
|
||||
<input name="footer_text" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="footer_text" {% if footer_text %} value="{{- footer_text -}}" {% endif %} />
|
||||
<label for="footer_icon_url" class="col-sm-6 col-form-label">Footer icon</label>
|
||||
<input name="footer_icon_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="footer_icon_url" {% if footer_icon_url %} value="{{- footer_icon_url -}}" {% endif %} />
|
||||
|
||||
{% if is_steam_feed %}
|
||||
<hr class="border-secondary my-3" />
|
||||
<h5>Options</h5>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="show_steam_game_icon_in_thumbnail"
|
||||
id="show_steam_game_icon_in_thumbnail" value="true"
|
||||
|
|
@ -275,12 +292,25 @@
|
|||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<label for="footer_text" class="col-sm-6 col-form-label">Footer text</label>
|
||||
<input name="footer_text" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="footer_text" {% if footer_text %} value="{{- footer_text -}}" {% endif %} />
|
||||
<label for="footer_icon_url" class="col-sm-6 col-form-label">Footer icon</label>
|
||||
<input name="footer_icon_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="footer_icon_url" {% if footer_icon_url %} value="{{- footer_icon_url -}}" {% endif %} />
|
||||
|
||||
<hr class="border-secondary my-3" />
|
||||
<h5>Webhook identity</h5>
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>These override the webhook's display name and avatar (profile picture) for messages from this feed.</li>
|
||||
<li>Leave blank to use the webhook's default name and avatar.</li>
|
||||
<li>Username rules: 1–80 characters; cannot contain <code>@</code>, <code>#</code>, <code>:</code>, or <code>`</code>; cannot contain <code>clyde</code> or <code>discord</code>.</li>
|
||||
<li>Avatar must be a full <code>http://</code> or <code>https://</code> image URL.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<label for="username" class="col-sm-6 col-form-label">Webhook name</label>
|
||||
<input name="username" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="username" maxlength="80" placeholder="e.g. My Feed Bot"
|
||||
{% if username %} value="{{- username -}}" {% endif %} />
|
||||
<label for="avatar_url" class="col-sm-6 col-form-label">Webhook avatar</label>
|
||||
<input name="avatar_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="avatar_url" placeholder="e.g. https://example.com/avatar.png or {% raw %}{{hoyolab_author_avatar_url}}{% endraw %}"
|
||||
{% if avatar_url %} value="{{- avatar_url -}}" {% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add a hidden feed_url field to the form -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue