Allow newlines in embeds

This commit is contained in:
Joakim Hellsén 2026-07-18 06:04:29 +02:00
commit 35ede5aa33
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
3 changed files with 60 additions and 2 deletions

View file

@ -318,7 +318,7 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb
entry_updated: str = entry.updated.strftime("%Y-%m-%d %H:%M:%S") if entry.updated else "Never" entry_updated: str = entry.updated.strftime("%Y-%m-%d %H:%M:%S") if entry.updated else "Never"
if embed.title and not embed.author_name and embed.author_url: if embed.title and not embed.author_name and embed.author_url:
msg = "You are using author_url without author_name, but has title set. We will use author_name instead of title when sending the embed to Discord." # noqa: E501 msg = "You are using author_url without author_name, but has title set. We will use author_name instead of title when sending the embed to Discord." # ruff:ignore[line-too-long]
logger.info(msg) logger.info(msg)
embed.author_name = embed.title embed.author_name = embed.title
embed.title = "" embed.title = ""
@ -356,6 +356,17 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb
for replacement in list_of_replacements: for replacement in list_of_replacements:
for template, replace_with in replacement.items(): for template, replace_with in replacement.items():
_replace_embed_tags(embed, template, replace_with) _replace_embed_tags(embed, template, replace_with)
embed.title = embed.title.replace("\\n", "\n")
embed.description = embed.description.replace("\\n", "\n")
embed.author_name = embed.author_name.replace("\\n", "\n")
embed.author_url = embed.author_url.replace("\\n", "\n")
embed.author_icon_url = embed.author_icon_url.replace("\\n", "\n")
embed.image_url = embed.image_url.replace("\\n", "\n")
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 return embed
@ -541,7 +552,11 @@ def get_embed(reader: Reader, feed: Feed) -> CustomEmbed:
def coerce_embed_bool(value: object) -> bool: def coerce_embed_bool(value: object) -> bool:
"""Normalize stored embed booleans from JSON or form-like values.""" """Normalize stored embed booleans from JSON or form-like values.
Returns:
The coerced boolean value.
"""
if isinstance(value, bool): if isinstance(value, bool):
return value return value
if isinstance(value, int): if isinstance(value, int):

View file

@ -218,6 +218,12 @@
message, please contact the developer. message, please contact the developer.
{% endif %} {% endif %}
</div> </div>
<div class="form-text">
<ul class="list-inline">
<li>You can use \n to create a new line.</li>
<li>You can remove the embed from links by adding &lt; and &gt; around the link. (For example &lt;{% raw %}{{entry_link}}{% endraw %}&gt;)</li>
</ul>
</div>
<label for="title" class="col-sm-6 col-form-label">Title</label> <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" <input name="title" type="text" class="form-control bg-dark border-dark text-muted" id="title"
{% if title %} value="{{- title -}}" {% endif %} /> {% if title %} value="{{- title -}}" {% endif %} />

View file

@ -330,6 +330,43 @@ def test_replace_tags_in_embed_uses_last_content_item(
assert "New content" in embed.description assert "New content" in embed.description
@patch("discord_rss_bot.custom_message.get_embed")
def test_replace_tags_in_embed_converts_escaped_newlines(
mock_get_embed: MagicMock,
) -> None:
r"""Verify \\n in embed fields becomes actual newlines, and fields without \\n are unchanged."""
mock_reader = MagicMock()
mock_get_embed.return_value = CustomEmbed(
title="Line 1\\nLine 2",
description="{{entry_text}}\\ntest\\test2",
author_name="Author\\nSubtitle",
footer_text="Footer\\n\\nMore",
)
entry_ns: SimpleNamespace = make_entry("<p>Summary</p>")
entry: Entry = typing.cast("Entry", entry_ns)
embed: CustomEmbed = replace_tags_in_embed(entry_ns.feed, entry, reader=mock_reader)
# Fields with \\n should have actual newlines
assert "\n" in embed.title
assert embed.title == "Line 1\nLine 2"
assert "\n" in embed.author_name
assert embed.author_name == "Author\nSubtitle"
assert "\n" in embed.footer_text
assert embed.footer_text == "Footer\n\nMore"
# Description combines tag replacement with \\n conversion.
# "{{entry_text}}\\ntest\\\\test2" becomes "Summary text\ntest\\test2".
assert "\n" in embed.description
assert embed.description == "Summary\ntest\\test2"
# Fields without \\n should be unchanged (backward compat) - none here but
# cover other fields that had no \\n originally
assert "\\n" not in embed.title
assert "\\n" not in embed.author_name
assert "\\n" not in embed.footer_text
def test_get_custom_message_returns_empty_string_on_value_error() -> None: def test_get_custom_message_returns_empty_string_on_value_error() -> None:
reader = MagicMock() reader = MagicMock()
feed = make_feed() feed = make_feed()