diff --git a/discord_rss_bot/custom_message.py b/discord_rss_bot/custom_message.py index ca7d988..4a9b9e4 100644 --- a/discord_rss_bot/custom_message.py +++ b/discord_rss_bot/custom_message.py @@ -1,5 +1,6 @@ from __future__ import annotations +import html import json import logging from dataclasses import dataclass @@ -7,13 +8,16 @@ from typing import TYPE_CHECKING from bs4 import BeautifulSoup from bs4 import Tag +from markupsafe import Markup from discord_rss_bot.extensions import run_extensions from discord_rss_bot.extensions.steam import fix_steam_image_url +from discord_rss_bot.extensions.steam import get_feed_display_title from discord_rss_bot.html_format import format_entry_html_for_discord from discord_rss_bot.is_url_valid import is_url_valid if TYPE_CHECKING: + from collections.abc import Mapping from collections.abc import Sequence from reader import Content @@ -105,7 +109,7 @@ def replace_tags_in_text_message(entry: Entry, reader: Reader) -> str: {"{{feed_last_updated}}": feed_last_updated}, {"{{feed_link}}": feed.link or ""}, {"{{feed_subtitle}}": feed.subtitle or ""}, - {"{{feed_title}}": feed.title or ""}, + {"{{feed_title}}": get_feed_display_title(feed, reader)}, {"{{feed_updated}}": feed_updated}, {"{{feed_updates_enabled}}": str(feed.updates_enabled) or ""}, {"{{feed_url}}": feed.url or ""}, @@ -278,7 +282,7 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb {"{{feed_last_updated}}": feed_last_updated or ""}, {"{{feed_link}}": feed.link or ""}, {"{{feed_subtitle}}": feed.subtitle or ""}, - {"{{feed_title}}": feed.title or ""}, + {"{{feed_title}}": get_feed_display_title(feed, reader)}, {"{{feed_updated}}": feed_updated or ""}, {"{{feed_updates_enabled}}": "True" if feed.updates_enabled else "False"}, {"{{feed_url}}": feed.url or ""}, @@ -344,6 +348,143 @@ def _replace_embed_tags(embed: CustomEmbed, template: str, replace_with: str) -> embed.username = try_to_replace(embed.username, template, replace_with) +def _tag_reference_item(tag: str, value: object) -> str: + """Return one reference row for the template-variable preview list. + + Args: + tag: The template variable name without braces, e.g. ``feed_title``. + value: The preview value to display next to the tag. + + Returns: + An HTML ``
  • `` with the tag in ```` and the escaped value. + """ + escaped_value: str = html.escape(str(value), quote=True) + return f"
  • {{{{{tag}}}}} {escaped_value}
  • " + + +def _tag_reference_value(obj: object, name: str) -> object: + """Read ``obj.name``, returning an empty string when the attribute is missing. + + Mirrors Jinja's lenient attribute lookup so the reference list also works + with the minimal stubs used in tests. + + Args: + obj: The object to read the attribute from. + name: The attribute name. + + Returns: + The attribute value, or an empty string when it is missing. + """ + return getattr(obj, name, "") + + +def render_tag_reference_html( + *, + feed: Feed, + entry: Entry | None, + feed_title: str, + first_image: str, + extension_variables: Sequence[str] = (), + extension_values: Mapping[str, str] | None = None, +) -> Markup: + """Render the template-variable reference list for the custom/embed pages. + + The list used to be hardcoded in ``custom.html`` and ``embed.html`` with + many ``{% raw %}`` blocks, which broke whenever a formatter touched the + templates. Building the markup here keeps the ``{{ ... }}`` tags intact + because Jinja does not re-parse the returned string. + + Args: + feed: The feed whose values are previewed. + entry: The first entry to preview, or None when the feed has no entries. + feed_title: The resolved display title for the feed (``{{feed_title}}``). + first_image: The first image URL found in the entry (``{{image_1}}``). + extension_variables: Names of the enabled extension variables. + extension_values: Values produced by the enabled extensions. + + Returns: + A ``