Use Steam game names as feed display titles; custom.html are now partly rendered from Python
All checks were successful
Test and build Docker image / docker (push) Successful in 30s
All checks were successful
Test and build Docker image / docker (push) Successful in 30s
This commit is contained in:
parent
cd9201d4ef
commit
6c0aea053b
15 changed files with 976 additions and 615 deletions
|
|
@ -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 ``<li>`` with the tag in ``<code>`` and the escaped value.
|
||||
"""
|
||||
escaped_value: str = html.escape(str(value), quote=True)
|
||||
return f"<li><code>{{{{{tag}}}}}</code> {escaped_value}</li>"
|
||||
|
||||
|
||||
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 ``<ul>`` list of reference rows, safe to embed directly in HTML.
|
||||
"""
|
||||
if extension_values is None:
|
||||
extension_values = {}
|
||||
|
||||
rows: list[str] = ['<ul class="list-inline">', "<br />"]
|
||||
rows.extend(
|
||||
[
|
||||
_tag_reference_item("feed_author", _tag_reference_value(feed, "authors_str")),
|
||||
_tag_reference_item("feed_added", _tag_reference_value(feed, "added")),
|
||||
_tag_reference_item("feed_last_exception", _tag_reference_value(feed, "last_exception")),
|
||||
_tag_reference_item("feed_last_updated", _tag_reference_value(feed, "last_updated")),
|
||||
_tag_reference_item("feed_link", _tag_reference_value(feed, "link")),
|
||||
_tag_reference_item("feed_subtitle", _tag_reference_value(feed, "subtitle")),
|
||||
_tag_reference_item("feed_title", feed_title),
|
||||
_tag_reference_item("feed_updated", _tag_reference_value(feed, "updated")),
|
||||
_tag_reference_item("feed_updates_enabled", _tag_reference_value(feed, "updates_enabled")),
|
||||
_tag_reference_item("feed_url", _tag_reference_value(feed, "url")),
|
||||
_tag_reference_item("feed_user_title", _tag_reference_value(feed, "user_title")),
|
||||
_tag_reference_item("feed_version", _tag_reference_value(feed, "version")),
|
||||
],
|
||||
)
|
||||
rows.append("<br />")
|
||||
|
||||
if entry is not None:
|
||||
content_html: str = entry.content[0].value if entry.content else ""
|
||||
summary: str = entry.summary or ""
|
||||
entry_content: str = format_entry_html_for_discord(content_html) if entry.content else ""
|
||||
entry_summary: str = format_entry_html_for_discord(summary) if entry.summary else ""
|
||||
entry_text: str = entry_content if entry.content else entry_summary
|
||||
|
||||
rows.extend(
|
||||
[
|
||||
_tag_reference_item("entry_added", entry.added),
|
||||
_tag_reference_item("entry_author", entry.authors_str or ""),
|
||||
],
|
||||
)
|
||||
if entry.content:
|
||||
rows.extend((
|
||||
_tag_reference_item("entry_content", entry_content),
|
||||
_tag_reference_item("entry_content_raw", content_html),
|
||||
))
|
||||
rows.extend(
|
||||
[
|
||||
_tag_reference_item("entry_id", entry.id),
|
||||
_tag_reference_item("entry_important", entry.important),
|
||||
_tag_reference_item("entry_link", entry.link or ""),
|
||||
_tag_reference_item("entry_published", entry.published),
|
||||
_tag_reference_item("entry_read", entry.read),
|
||||
_tag_reference_item("entry_read_modified", entry.read_modified),
|
||||
],
|
||||
)
|
||||
if entry.summary:
|
||||
rows.extend((
|
||||
_tag_reference_item("entry_summary", entry_summary),
|
||||
_tag_reference_item("entry_summary_raw", summary),
|
||||
))
|
||||
rows.extend(
|
||||
[
|
||||
_tag_reference_item("entry_title", entry.title or ""),
|
||||
_tag_reference_item("entry_text", entry_text),
|
||||
_tag_reference_item("entry_updated", entry.updated),
|
||||
],
|
||||
)
|
||||
rows.extend(("<br />", _tag_reference_item("image_1", first_image)))
|
||||
|
||||
if extension_variables:
|
||||
rows.extend(("<br />", "<li>Extension variables (from enabled extensions):</li>"))
|
||||
for var_name in extension_variables:
|
||||
var_value: str = extension_values.get(var_name, "") or "(empty)"
|
||||
rows.append(_tag_reference_item(var_name, var_value))
|
||||
else:
|
||||
rows.append(
|
||||
"Something went wrong, there was no entry found. If this feed has entries and you still see this "
|
||||
"message, please contact the developer.",
|
||||
)
|
||||
|
||||
rows.append("</ul>")
|
||||
# Safe: every dynamic value is HTML-escaped by _tag_reference_item.
|
||||
return Markup("\n".join(rows)) # ruff: ignore[unsafe-markup-use]
|
||||
|
||||
|
||||
def get_custom_message(reader: Reader, feed: Feed) -> str:
|
||||
"""Get custom_message tag from feed.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ It also rewrites broken Steam CDN image URLs: some news feeds reference
|
|||
localized image names such as ``.../english.png`` that return a 404, and
|
||||
the working URL omits the language segment (``.../<hash>.png``).
|
||||
|
||||
Steam feeds' ``{{feed_title}}`` (and web UI display names) use the game
|
||||
name (e.g. "Dota") instead of the raw feed title (e.g. "570 RSS Feed").
|
||||
The name is fetched once from the Steam Store API and cached in the reader
|
||||
DB per feed, so no repeated network requests are needed.
|
||||
|
||||
Usage:
|
||||
1. Enable ``steam`` on the feed's Extensions page.
|
||||
2. In embed settings, set Thumbnail URL to ``{{steam_thumbnail_url}}``.
|
||||
|
|
@ -19,6 +24,7 @@ import hashlib
|
|||
import json
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import ClassVar
|
||||
|
|
@ -28,6 +34,9 @@ from urllib.parse import parse_qs
|
|||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlunsplit
|
||||
|
||||
import httpx2
|
||||
from httpx2 import HTTPError
|
||||
from httpx2 import Response
|
||||
from reader import ReaderError
|
||||
|
||||
from discord_rss_bot.extensions.base import FeedExtension
|
||||
|
|
@ -258,6 +267,172 @@ def fix_steam_image_urls_in_payload(value: JsonValue) -> JsonValue:
|
|||
return value
|
||||
|
||||
|
||||
#: Reader tag under which the Steam game name is cached per feed. Populated
|
||||
#: once per feed so title rendering never needs a second network request.
|
||||
STEAM_GAME_NAME_TAG: str = "steam_game_name"
|
||||
|
||||
#: Steam Store API endpoint used to resolve app IDs to game names.
|
||||
_STEAM_APPDETAILS_URL: str = "https://store.steampowered.com/api/appdetails"
|
||||
|
||||
#: How long to wait before retrying a failed game-name lookup for the same app.
|
||||
_STEAM_GAME_NAME_RETRY_COOLDOWN: float = 60.0 * 60.0
|
||||
|
||||
#: Last lookup attempt time per app ID. Used so a repeatedly failing feed
|
||||
#: does not trigger a network request on every single message.
|
||||
_steam_game_name_attempts: dict[str, float] = {}
|
||||
|
||||
|
||||
def get_steam_game_name(app_id: str) -> str | None: # ruff:ignore[too-many-return-statements]
|
||||
"""Fetch the game name for *app_id* from the Steam Store API.
|
||||
|
||||
Args:
|
||||
app_id: The Steam application ID.
|
||||
|
||||
Returns:
|
||||
The game name, or ``None`` if it could not be determined.
|
||||
"""
|
||||
try:
|
||||
response: Response = httpx2.get(
|
||||
_STEAM_APPDETAILS_URL,
|
||||
params={"appids": app_id, "l": "english"},
|
||||
timeout=10.0,
|
||||
)
|
||||
response.raise_for_status()
|
||||
except (HTTPError, OSError) as exc:
|
||||
logger.warning("Failed to fetch Steam game name for app %s: %s", app_id, exc)
|
||||
return None
|
||||
|
||||
try:
|
||||
data: object = response.json()
|
||||
except ValueError as exc:
|
||||
logger.warning("Invalid JSON from Steam Store API for app %s: %s", app_id, exc)
|
||||
return None
|
||||
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
details: object = data.get(app_id)
|
||||
if not isinstance(details, dict) or not details.get("success"):
|
||||
logger.warning("Steam Store API returned no details for app %s", app_id)
|
||||
return None
|
||||
app_data: object = details.get("data")
|
||||
if not isinstance(app_data, dict):
|
||||
return None
|
||||
name: object = app_data.get("name")
|
||||
if isinstance(name, str) and name.strip():
|
||||
return name.strip()
|
||||
return None
|
||||
|
||||
|
||||
def get_cached_steam_game_name(reader: Reader, feed: Feed | str) -> str | None:
|
||||
"""Return the Steam game name cached in the reader DB for *feed*, if any.
|
||||
|
||||
Args:
|
||||
reader: The reader instance.
|
||||
feed: The feed (or feed URL) to look up.
|
||||
|
||||
Returns:
|
||||
The cached game name, or ``None`` when nothing is cached.
|
||||
"""
|
||||
try:
|
||||
cached = reader.get_tag(feed, STEAM_GAME_NAME_TAG, None)
|
||||
except ReaderError:
|
||||
logger.exception("Failed to read Steam game name tag for %s", feed)
|
||||
return None
|
||||
if isinstance(cached, str) and cached.strip():
|
||||
return cached.strip()
|
||||
return None
|
||||
|
||||
|
||||
def set_steam_game_name(reader: Reader, feed: Feed | str, game_name: str) -> None:
|
||||
"""Cache *game_name* for *feed* in the reader DB.
|
||||
|
||||
Args:
|
||||
reader: The reader instance.
|
||||
feed: The feed (or feed URL) to store the name for.
|
||||
game_name: The game name to cache.
|
||||
"""
|
||||
try:
|
||||
reader.set_tag(feed, STEAM_GAME_NAME_TAG, game_name) # pyright: ignore[reportArgumentType]
|
||||
except ReaderError:
|
||||
logger.exception("Failed to cache Steam game name for %s", feed)
|
||||
|
||||
|
||||
def get_feed_display_title(feed: Feed, reader: Reader) -> str:
|
||||
"""Return the display title for a feed.
|
||||
|
||||
For Steam feeds, uses the game name (e.g. "Dota") instead of the raw feed
|
||||
title (e.g. "570 RSS Feed"). The name is read from the reader DB cache;
|
||||
when the cache is empty it is fetched from the Steam Store API once and
|
||||
then cached, so title rendering never makes "unnecessary network requests"
|
||||
for feeds that have already been resolved. Falls back to the feed's own
|
||||
title when the game name cannot be determined. Non-Steam feeds are
|
||||
returned with their title unchanged.
|
||||
|
||||
Args:
|
||||
feed: The feed to render a title for.
|
||||
reader: The reader instance.
|
||||
|
||||
Returns:
|
||||
The display title for *feed*.
|
||||
"""
|
||||
feed_url: str = getattr(feed, "url", "") or ""
|
||||
fallback_title: str = getattr(feed, "title", "") or ""
|
||||
if not is_steam_url(feed_url):
|
||||
return fallback_title
|
||||
|
||||
app_id: str | None = extract_app_id(feed_url)
|
||||
if not app_id:
|
||||
return fallback_title
|
||||
|
||||
cached: str | None = get_cached_steam_game_name(reader, feed)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
# Don't hammer the API for app IDs whose lookup recently failed.
|
||||
now: float = time.monotonic()
|
||||
last_attempt: float = _steam_game_name_attempts.get(app_id, 0.0)
|
||||
if now - last_attempt < _STEAM_GAME_NAME_RETRY_COOLDOWN:
|
||||
return fallback_title
|
||||
|
||||
_steam_game_name_attempts[app_id] = now
|
||||
game_name: str | None = get_steam_game_name(app_id)
|
||||
if game_name:
|
||||
set_steam_game_name(reader, feed, game_name)
|
||||
return game_name
|
||||
return fallback_title
|
||||
|
||||
|
||||
def get_cached_feed_display_title(feed: Feed, reader: Reader) -> str:
|
||||
"""Return the display title for a feed using only cached data.
|
||||
|
||||
For Steam feeds, uses the cached game name when available, otherwise the
|
||||
feed's own title. Unlike :func:`get_feed_display_title`, this never
|
||||
performs network requests or writes to the reader — it only reads the
|
||||
cache. Meant for read-only render paths (e.g. web UI pages) that must not
|
||||
block on the Steam Store API.
|
||||
|
||||
Args:
|
||||
feed: The feed to render a title for.
|
||||
reader: The reader instance.
|
||||
|
||||
Returns:
|
||||
The display title for *feed* using only cached data.
|
||||
"""
|
||||
feed_url: str = getattr(feed, "url", "") or ""
|
||||
fallback_title: str = getattr(feed, "title", "") or ""
|
||||
if not is_steam_url(feed_url):
|
||||
return fallback_title
|
||||
|
||||
app_id: str | None = extract_app_id(feed_url)
|
||||
if not app_id:
|
||||
return fallback_title
|
||||
|
||||
cached: str | None = get_cached_steam_game_name(reader, feed)
|
||||
if cached:
|
||||
return cached
|
||||
return fallback_title
|
||||
|
||||
|
||||
def _try_read_icon_file(app_id: str) -> WebhookFile | None:
|
||||
"""Read a Steam game icon from disk, returning a ``WebhookFile`` or ``None``.
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ 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
|
||||
from discord_rss_bot.extensions import run_modify_webhook
|
||||
from discord_rss_bot.extensions.steam import get_cached_feed_display_title
|
||||
from discord_rss_bot.extensions.steam import get_feed_display_title
|
||||
from discord_rss_bot.filter.evaluator import get_entry_filter_decision_from_reader
|
||||
from discord_rss_bot.is_url_valid import is_url_valid
|
||||
from discord_rss_bot.settings import default_custom_embed
|
||||
|
|
@ -141,12 +143,14 @@ MESSAGE_PAYLOAD_KEYS: tuple[str, ...] = (
|
|||
)
|
||||
|
||||
|
||||
def get_feed_display_name(feed: Feed) -> str:
|
||||
def get_feed_display_name(feed: Feed, reader: Reader | None = None) -> str:
|
||||
"""Return a human-readable display name for a feed.
|
||||
|
||||
For YouTube feeds whose title is just "Videos", shows
|
||||
``"{author}'s Videos"`` instead. Other feeds return the title
|
||||
as-is.
|
||||
``"{author}'s Videos"`` instead. For Steam feeds, shows the game name
|
||||
(e.g. "Dota") instead of the raw feed title (e.g. "570 RSS Feed") when
|
||||
*reader* is provided and the name is cached. Other feeds return the
|
||||
title as-is.
|
||||
"""
|
||||
url: str = feed.url or ""
|
||||
title: str = feed.title or ""
|
||||
|
|
@ -156,6 +160,14 @@ def get_feed_display_name(feed: Feed) -> str:
|
|||
if author:
|
||||
return f"{author}'s Videos"
|
||||
|
||||
if reader is not None:
|
||||
steam_title: str = get_cached_feed_display_title(feed, reader)
|
||||
if steam_title and steam_title != title:
|
||||
try:
|
||||
return html.unescape(steam_title)
|
||||
except (TypeError, AttributeError):
|
||||
return steam_title
|
||||
|
||||
try:
|
||||
return html.unescape(title) if title else url
|
||||
except (TypeError, AttributeError):
|
||||
|
|
@ -648,7 +660,7 @@ def upsert_sent_webhook_record(
|
|||
delivery_mode: DeliveryMode = get_entry_delivery_mode(reader, entry)
|
||||
record: SentWebhookRecord = {
|
||||
"feed_url": entry.feed.url,
|
||||
"feed_title": entry.feed.title or "",
|
||||
"feed_title": get_feed_display_title(entry.feed, reader),
|
||||
"entry_id": entry.id,
|
||||
"entry_title": entry.title or "",
|
||||
"entry_link": entry.link or "",
|
||||
|
|
@ -878,9 +890,9 @@ def apply_feed_webhook_identity(webhook: DiscordWebhook, entry: Entry, reader: R
|
|||
avatar_url: str = get_validated_message_avatar_url(reader, feed)
|
||||
|
||||
if not username:
|
||||
# Fall back to feed author or title so the webhook name is
|
||||
# Fall back to feed author or display title so the webhook name is
|
||||
# identifiable rather than the generic Discord webhook default.
|
||||
feed_author: str = feed.authors_str or feed.title or ""
|
||||
feed_author: str = feed.authors_str or get_feed_display_title(feed, reader) or ""
|
||||
if feed_author:
|
||||
username = html.unescape(feed_author)[:80]
|
||||
|
||||
|
|
@ -1043,7 +1055,7 @@ def update_sent_webhook_record_for_entry(
|
|||
return (
|
||||
{
|
||||
**record,
|
||||
"feed_title": entry.feed.title or "",
|
||||
"feed_title": get_feed_display_title(entry.feed, reader),
|
||||
"entry_title": entry.title or "",
|
||||
"entry_link": entry.link or "",
|
||||
"entry_updated": get_entry_timestamp(entry.updated),
|
||||
|
|
|
|||
|
|
@ -58,11 +58,14 @@ from discord_rss_bot.custom_message import get_embed
|
|||
from discord_rss_bot.custom_message import get_first_image
|
||||
from discord_rss_bot.custom_message import get_message_avatar_url
|
||||
from discord_rss_bot.custom_message import get_message_username
|
||||
from discord_rss_bot.custom_message import render_tag_reference_html
|
||||
from discord_rss_bot.custom_message import replace_tags_in_text_message
|
||||
from discord_rss_bot.custom_message import save_embed
|
||||
from discord_rss_bot.extensions import FeedExtension as FeedExtensionABC
|
||||
from discord_rss_bot.extensions import get_registry as get_extension_registry
|
||||
from discord_rss_bot.extensions import run_extensions
|
||||
from discord_rss_bot.extensions.steam import get_cached_feed_display_title
|
||||
from discord_rss_bot.extensions.steam import get_feed_display_title
|
||||
from discord_rss_bot.extensions.steam import is_steam_url as is_steam_feed_url
|
||||
from discord_rss_bot.extensions.storage import get_enabled_extensions_for_feed as get_enabled_extensions
|
||||
from discord_rss_bot.extensions.storage import set_enabled_extensions_for_feed as set_enabled_extensions
|
||||
|
|
@ -295,6 +298,7 @@ templates.env.filters["relative_time"] = relative_time
|
|||
templates.env.filters["feed_display_name"] = get_feed_display_name
|
||||
templates.env.globals["get_backup_path"] = get_backup_path # pyright: ignore[reportArgumentType]
|
||||
templates.env.globals["has_webhooks"] = has_webhooks # pyright: ignore[reportArgumentType]
|
||||
templates.env.globals["render_tag_reference_html"] = render_tag_reference_html # pyright: ignore[reportArgumentType]
|
||||
|
||||
|
||||
@app.get("/export_opml")
|
||||
|
|
@ -839,6 +843,7 @@ async def get_whitelist(
|
|||
context = {
|
||||
"request": request,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
**build_filter_form_context("whitelist", get_filter_values_from_reader(reader, feed, "whitelist")),
|
||||
**build_filter_preview_context(reader, feed, "whitelist"),
|
||||
}
|
||||
|
|
@ -970,6 +975,7 @@ async def get_blacklist(
|
|||
context = {
|
||||
"request": request,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
**build_filter_form_context("blacklist", get_filter_values_from_reader(reader, feed, "blacklist")),
|
||||
**build_filter_preview_context(reader, feed, "blacklist"),
|
||||
}
|
||||
|
|
@ -1442,6 +1448,7 @@ async def get_custom(
|
|||
context: dict[str, object] = {
|
||||
"request": request,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
"custom_message": get_custom_message(reader, feed),
|
||||
"message_username": get_message_username(reader, feed),
|
||||
"message_avatar_url": get_message_avatar_url(reader, feed),
|
||||
|
|
@ -1501,6 +1508,7 @@ async def get_embed_page(
|
|||
context: dict[str, object] = {
|
||||
"request": request,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
"title": embed.title,
|
||||
"description": embed.description,
|
||||
"color": embed.color,
|
||||
|
|
@ -1633,6 +1641,7 @@ async def get_extensions(
|
|||
context: dict[str, object] = {
|
||||
"request": request,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
"discovered_extensions": registry,
|
||||
"enabled_extensions": enabled,
|
||||
"extensions_dir": extensions_dir,
|
||||
|
|
@ -2185,7 +2194,9 @@ async def get_feed( # ruff:ignore[complex-structure, too-many-branches, too-man
|
|||
|
||||
context = {
|
||||
"request": request,
|
||||
"reader": reader,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
"entries": current_entries,
|
||||
"feed_counts": reader.get_feed_counts(feed=clean_feed_url),
|
||||
"html": html,
|
||||
|
|
@ -2253,7 +2264,9 @@ async def get_feed( # ruff:ignore[complex-structure, too-many-branches, too-man
|
|||
|
||||
context = {
|
||||
"request": request,
|
||||
"reader": reader,
|
||||
"feed": feed,
|
||||
"feed_title": get_feed_display_title(feed, reader),
|
||||
"entries": entries,
|
||||
"feed_counts": reader.get_feed_counts(feed=clean_feed_url),
|
||||
"html": html,
|
||||
|
|
@ -2343,7 +2356,7 @@ def create_html_for_feed( # ruff:ignore[complex-structure, too-many-locals]
|
|||
feed_link: str = ""
|
||||
if not current_feed_url or source_feed_url != current_feed_url:
|
||||
encoded_feed_url: str = urllib.parse.quote(source_feed_url)
|
||||
feed_title: str = entry.feed.title if hasattr(entry.feed, "title") and entry.feed.title else source_feed_url
|
||||
feed_title: str = get_cached_feed_display_title(entry.feed, reader) or source_feed_url
|
||||
feed_link = (
|
||||
f"<a class='text-muted' style='font-size: 0.85em;' "
|
||||
f"href='/feed?feed_url={encoded_feed_url}'>{feed_title}</a><br>"
|
||||
|
|
@ -2582,7 +2595,9 @@ async def get_sent_webhooks(
|
|||
webhook_names: dict[str, str] = {
|
||||
hook.get("url", ""): hook.get("name", "") for hook in webhooks if isinstance(hook, dict)
|
||||
}
|
||||
feed_titles: dict[str, str] = {feed.url: (feed.title or feed.url) for feed in reader.get_feeds()}
|
||||
feed_titles: dict[str, str] = {
|
||||
feed.url: (get_cached_feed_display_title(feed, reader) or feed.url) for feed in reader.get_feeds()
|
||||
}
|
||||
|
||||
context = {
|
||||
"request": request,
|
||||
|
|
@ -2653,6 +2668,7 @@ def make_context_index(request: Request, message: str = "", reader: Reader | Non
|
|||
|
||||
return {
|
||||
"request": request,
|
||||
"reader": effective_reader,
|
||||
"feeds": feed_list,
|
||||
"feed_count": effective_reader.get_feed_counts(),
|
||||
"entry_count": effective_reader.get_entry_counts(),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}
|
||||
Blacklist: {{ feed.title if feed.title else feed.url }} | discord-rss-bot
|
||||
Blacklist: {{ feed_title if feed_title else feed.url }} | discord-rss-bot
|
||||
{% endblock title %}
|
||||
{% block description %}
|
||||
Block matching entries from {{ feed.title if feed.title else feed.url }} before they are delivered to Discord.
|
||||
Block matching entries from {{ feed_title if feed_title else feed.url }} before they are delivered to Discord.
|
||||
{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="row g-3 filter-page">
|
||||
|
|
|
|||
|
|
@ -1,277 +1,65 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Message Template: {{ feed.title if feed.title else feed.url }} | discord-rss-bot{% endblock title %}
|
||||
{% block description %}Customize the plain text Discord message template for {{ feed.title if feed.title else feed.url }}.{% endblock description %}
|
||||
{% block title %}Message Template: {{ feed_title if feed_title else feed.url }} | discord-rss-bot{% endblock title %}
|
||||
{% block description %}Customize the plain text Discord message template for {{ feed_title if feed_title else feed.url }}.{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="p-2 border border-dark">
|
||||
<form action="/custom" method="post">
|
||||
<!-- Feed URL -->
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>You can modify the message that is sent to Discord.</li>
|
||||
<li>You can use \n to create a new line.</li>
|
||||
<li>
|
||||
You can remove the embed from links by adding < and> around the link. (For example <
|
||||
{% raw %} {{entry_link}} {% endraw %}>)
|
||||
</li>
|
||||
<br />
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_author}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.authors_str }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_added}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.added }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_last_exception}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.last_exception }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_last_updated}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.last_updated }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_link}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.link }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_subtitle}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.subtitle }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_title}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.title }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_updated}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.updated }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_updates_enabled}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.updates_enabled }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_url}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.url }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_user_title}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.user_title }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_version}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.version }}
|
||||
</li>
|
||||
<br />
|
||||
{% if entry %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_added}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.added }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_author}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.authors_str }}
|
||||
</li>
|
||||
{% if entry.content %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_content}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.content[0].value|discord_markdown }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_content_raw}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.content[0].value }}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_id}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.id }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_important}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.important }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_link}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.link }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_published}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.published }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_read}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.read }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_read_modified}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.read_modified }}
|
||||
</li>
|
||||
{% if entry.summary %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_summary}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.summary|discord_markdown }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_summary_raw}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.summary }}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_title}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.title }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_text}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.content[0].value|discord_markdown if entry and entry.content else (entry.summary|discord_markdown if entry and entry.summary else '') }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_updated}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.updated }}
|
||||
</li>
|
||||
<br />
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{image_1}}
|
||||
{% endraw %}
|
||||
</code>{{ first_image }}
|
||||
</li>
|
||||
{% if extension_variables %}
|
||||
<br />
|
||||
<li>Extension variables (from enabled extensions):</li>
|
||||
{% for var_name in extension_variables %}
|
||||
<li>
|
||||
<code>{% raw %}{{{% endraw %}{{ var_name }}{% raw %}}}{% endraw %}</code>
|
||||
{% if var_name in extension_values and extension_values[var_name] %}
|
||||
{{ extension_values[var_name] }}
|
||||
{% else %}
|
||||
(empty)
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
Something went wrong, there was no entry found. If this feed has entries and you still see this
|
||||
message, please contact the developer.
|
||||
{% endif %}
|
||||
<div class="p-2 border border-dark">
|
||||
<form action="/custom" method="post">
|
||||
<!-- Feed URL -->
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>You can modify the message that is sent to Discord.</li>
|
||||
<li>You can use \n to create a new line.</li>
|
||||
<li>
|
||||
You can remove the embed from links by adding < and > around the link. (For example <{% raw %}{{ entry_link }}{% endraw %}>)
|
||||
</li>
|
||||
</ul>
|
||||
{{ render_tag_reference_html(feed=feed, entry=entry, feed_title=feed_title, first_image=first_image, extension_variables=extension_variables, extension_values=extension_values) }}
|
||||
</div>
|
||||
<label for="custom_message" class="col-sm-6 col-form-label">Message</label>
|
||||
<input name="custom_message"
|
||||
type="text"
|
||||
class="form-control bg-dark border-dark text-muted"
|
||||
id="custom_message"
|
||||
{% if custom_message %} value="{{- custom_message -}}" {% endif %} />
|
||||
</div>
|
||||
<label for="custom_message" class="col-sm-6 col-form-label">Message</label>
|
||||
<input name="custom_message" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="custom_message" {% if custom_message %} value="{{- custom_message -}}" {% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Optional Discord webhook username & avatar for this feed -->
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>Optional: override the Discord webhook username and avatar for messages from this feed.</li>
|
||||
<li>Leave blank to use the webhook's default name and avatar.</li>
|
||||
<li>Invalid values (empty, disallowed characters, or bad avatar URLs) are ignored when sending.</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>
|
||||
<!-- Optional Discord webhook username & avatar for this feed -->
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>Optional: override the Discord webhook username and avatar for messages from this feed.</li>
|
||||
<li>Leave blank to use the webhook's default name and avatar.</li>
|
||||
<li>Invalid values (empty, disallowed characters, or bad avatar URLs) are ignored when sending.</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="message_username" class="col-sm-6 col-form-label">Message Username</label>
|
||||
<input name="message_username"
|
||||
type="text"
|
||||
class="form-control bg-dark border-dark text-muted"
|
||||
id="message_username"
|
||||
maxlength="80"
|
||||
placeholder="e.g. Power Of The Shell"
|
||||
{% if message_username %} value="{{- message_username -}}" {% endif %} />
|
||||
<label for="message_avatar_url" class="col-sm-6 col-form-label">Message Avatar Image</label>
|
||||
<input name="message_avatar_url"
|
||||
type="url"
|
||||
class="form-control bg-dark border-dark text-muted"
|
||||
id="message_avatar_url"
|
||||
placeholder="e.g. https://example.com/icon.png"
|
||||
{% if message_avatar_url %} value="{{- message_avatar_url -}}" {% endif %} />
|
||||
</div>
|
||||
<label for="message_username" class="col-sm-6 col-form-label">Message Username</label>
|
||||
<input name="message_username" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="message_username" maxlength="80" placeholder="e.g. Power Of The Shell"
|
||||
{% if message_username %} value="{{- message_username -}}" {% endif %} />
|
||||
<label for="message_avatar_url" class="col-sm-6 col-form-label">Message Avatar Image</label>
|
||||
<input name="message_avatar_url" type="url" class="form-control bg-dark border-dark text-muted"
|
||||
id="message_avatar_url" placeholder="e.g. https://example.com/icon.png"
|
||||
{% if message_avatar_url %} value="{{- message_avatar_url -}}" {% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add a hidden feed_url field to the form -->
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<!-- Submit button -->
|
||||
<div class="d-md-flex">
|
||||
<button class="btn btn-dark btn-sm">Update message</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Add a hidden feed_url field to the form -->
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<!-- Submit button -->
|
||||
<div class="d-md-flex">
|
||||
<button class="btn btn-dark btn-sm">Update message</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
|
|||
|
|
@ -1,324 +1,137 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Embed Template: {{ feed.title if feed.title else feed.url }} | discord-rss-bot{% endblock title %}
|
||||
{% block description %}Customize the Discord embed layout, colors, and media for {{ feed.title if feed.title else feed.url }}.{% endblock description %}
|
||||
{% block title %}Embed Template: {{ feed_title if feed_title else feed.url }} | discord-rss-bot{% endblock title %}
|
||||
{% block description %}Customize the Discord embed layout, colors, and media for {{ feed_title if feed_title else feed.url }}.{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="p-2 border border-dark">
|
||||
<form action="/embed" method="post">
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<br />
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_author}}
|
||||
{% endraw %}
|
||||
</code>{{feed.authors_str}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_added}}
|
||||
{% endraw %}
|
||||
</code>{{feed.added}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_last_exception}}
|
||||
{% endraw %}
|
||||
</code>{{feed.last_exception}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_last_updated}}
|
||||
{% endraw %}
|
||||
</code>{{feed.last_updated}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_link}}
|
||||
{% endraw %}
|
||||
</code>{{feed.link}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_subtitle}}
|
||||
{% endraw %}
|
||||
</code>{{feed.subtitle}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_title}}
|
||||
{% endraw %}
|
||||
</code>{{feed.title}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_updated}}
|
||||
{% endraw %}
|
||||
</code>{{feed.updated}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_updates_enabled}}
|
||||
{% endraw %}
|
||||
</code>{{feed.updates_enabled}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_url}}
|
||||
{% endraw %}
|
||||
</code>{{feed.url}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_user_title}}
|
||||
{% endraw %}
|
||||
</code>{{feed.user_title}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_version}}
|
||||
{% endraw %}
|
||||
</code>{{feed.version}}
|
||||
</li>
|
||||
<br />
|
||||
{% if entry %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_added}}
|
||||
{% endraw %}
|
||||
</code>{{entry.added}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_author}}
|
||||
{% endraw %}
|
||||
</code>{{entry.authors_str}}
|
||||
</li>
|
||||
{% if entry.content %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_content}}
|
||||
{% endraw %}
|
||||
</code>{{entry.content[0].value|discord_markdown}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_content_raw}}
|
||||
{% endraw %}
|
||||
</code>{{entry.content[0].value}}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_id}}
|
||||
{% endraw %}
|
||||
</code>{{entry.id}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_important}}
|
||||
{% endraw %}
|
||||
</code>{{entry.important}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_link}}
|
||||
{% endraw %}
|
||||
</code>{{entry.link}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_published}}
|
||||
{% endraw %}
|
||||
</code>{{entry.published}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_read}}
|
||||
{% endraw %}
|
||||
</code>{{entry.read}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_read_modified}}
|
||||
{% endraw %}
|
||||
</code>{{entry.read_modified}}
|
||||
</li>
|
||||
{% if entry.summary %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_summary}}
|
||||
{% endraw %}
|
||||
</code>{{entry.summary|discord_markdown}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_summary_raw}}
|
||||
{% endraw %}
|
||||
</code>{{entry.summary}}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_title}}
|
||||
{% endraw %}
|
||||
</code>{{entry.title}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_text}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.content[0].value|discord_markdown if entry and entry.content else (entry.summary|discord_markdown if entry and entry.summary else '') }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{entry_updated}}
|
||||
{% endraw %}
|
||||
</code>{{entry.updated}}
|
||||
</li>
|
||||
<br />
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{image_1}}
|
||||
{% endraw %}
|
||||
</code>{{ first_image }}
|
||||
</li>
|
||||
{% if extension_variables %}
|
||||
<br />
|
||||
<li>Extension variables (from enabled extensions):</li>
|
||||
{% for var_name in extension_variables %}
|
||||
<li>
|
||||
<code>{% raw %}{{{% endraw %}{{ var_name }}{% raw %}}}{% endraw %}</code>
|
||||
{% if var_name in extension_values and extension_values[var_name] %}
|
||||
{{ extension_values[var_name] }}
|
||||
{% else %}
|
||||
(empty)
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="p-2 border border-dark">
|
||||
<form action="/embed" method="post">
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
{{ render_tag_reference_html(feed=feed, entry=entry, feed_title=feed_title, first_image=first_image, extension_variables=extension_variables, extension_values=extension_values) }}
|
||||
</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 < and > around the link. (For example <{% raw %}{{ entry_link }}{% endraw %}>)</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
Something went wrong, there was no entry found. If this feed has entries and you still see this
|
||||
message, please contact the developer.
|
||||
{% endif %}
|
||||
</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 %} />
|
||||
<label for="description" class="col-sm-6 col-form-label">Description</label>
|
||||
<input name="description"
|
||||
type="text"
|
||||
class="form-control bg-dark border-dark text-muted"
|
||||
id="description"
|
||||
{% if description %} value="{{- description -}}" {% endif %} />
|
||||
<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="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 %} />
|
||||
<div class="form-text">
|
||||
Use {% raw %}{{ image_1 }}{% endraw %} to use the first image URL found in the entry. You can also configure how many images gets sent via the feed page.
|
||||
</div>
|
||||
<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"
|
||||
{% if show_steam_game_icon_in_thumbnail %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_steam_game_icon_in_thumbnail">
|
||||
Use Steam game icon as thumbnail override
|
||||
</label>
|
||||
</div>
|
||||
{% 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 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 < 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 %} />
|
||||
<label for="description" class="col-sm-6 col-form-label">Description</label>
|
||||
<input name="description" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="description" {% if description %} value="{{- description -}}" {% endif %} />
|
||||
<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="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 %} />
|
||||
<div class="form-text">
|
||||
Use {% raw %}{{image_1}}{% endraw %} to use the first image URL found in the entry. You can also configure how many images gets sent via the feed page.
|
||||
</div>
|
||||
<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"
|
||||
{% if show_steam_game_icon_in_thumbnail %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_steam_game_icon_in_thumbnail">
|
||||
Use Steam game icon as thumbnail override
|
||||
</label>
|
||||
</div>
|
||||
{% 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 -->
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<!-- Submit button -->
|
||||
<div class="d-md-flex">
|
||||
<button class="btn btn-dark btn-sm">Update embed</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Add a hidden feed_url field to the form -->
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<!-- Submit button -->
|
||||
<div class="d-md-flex">
|
||||
<button class="btn btn-dark btn-sm">Update embed</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}
|
||||
Extensions: {{ feed.title if feed.title else feed.url }} | discord-rss-bot
|
||||
Extensions: {{ feed_title if feed_title else feed.url }} | discord-rss-bot
|
||||
{% endblock title %}
|
||||
{% block description %}
|
||||
Enable or disable content extension plugins for {{ feed.title if feed.title else feed.url }}.
|
||||
Enable or disable content extension plugins for {{ feed_title if feed_title else feed.url }}.
|
||||
{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="p-2 border border-dark">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}
|
||||
{{ feed.title if feed.title else feed.url }} | discord-rss-bot
|
||||
{{ feed_title if feed_title else feed.url }} | discord-rss-bot
|
||||
{% endblock title %}
|
||||
{% block description %}
|
||||
Review feed health, delivery settings, filters, webhook attachment, and update intervals for {{ feed.title if feed.title else feed.url }}.
|
||||
Review feed health, delivery settings, filters, webhook attachment, and update intervals for {{ feed_title if feed_title else feed.url }}.
|
||||
{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="row g-3 feed-page">
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="feed-page__content">
|
||||
<h2 class="h3 mb-1">
|
||||
<a class="text-muted text-decoration-none feed-page__wrap"
|
||||
href="{{ feed.url }}">{{ feed | feed_display_name }}</a>
|
||||
href="{{ feed.url }}">{{ feed | feed_display_name(reader) }}</a>
|
||||
</h2>
|
||||
<p class="text-muted mb-0">{{ total_entries }} entries</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,9 +72,7 @@
|
|||
<ul class="list-group list-unstyled mb-0">
|
||||
{% for feed in domain_feeds %}
|
||||
<li>
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
||||
{{ feed | feed_display_name }}
|
||||
</a>
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">{{ feed | feed_display_name(reader) }}</a>
|
||||
{% if not feed.updates_enabled %}<span class="text-warning">Disabled</span>{% endif %}
|
||||
{% if feed.last_exception %}<span class="text-danger">({{ feed.last_exception.value_str }})</span>{% endif %}
|
||||
</li>
|
||||
|
|
@ -112,7 +110,7 @@
|
|||
{% for broken_feed in broken_feeds %}
|
||||
<a class="text-muted"
|
||||
href="/feed?feed_url={{ broken_feed.url|encode_url }}">
|
||||
{{ broken_feed | feed_display_name }}
|
||||
{{ broken_feed | feed_display_name(reader) }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
@ -126,9 +124,7 @@
|
|||
{% for feed in feeds_without_attached_webhook %}
|
||||
<li class="list-group-item bg-dark border-dark text-danger">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
||||
{{ feed | feed_display_name }}
|
||||
</a>
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">{{ feed | feed_display_name(reader) }}</a>
|
||||
{% if webhooks %}
|
||||
<form action="/attach_feed_webhook"
|
||||
method="post"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}
|
||||
Whitelist: {{ feed.title if feed.title else feed.url }} | discord-rss-bot
|
||||
Whitelist: {{ feed_title if feed_title else feed.url }} | discord-rss-bot
|
||||
{% endblock title %}
|
||||
{% block description %}
|
||||
Allow only matching entries from {{ feed.title if feed.title else feed.url }} to reach Discord.
|
||||
Allow only matching entries from {{ feed_title if feed_title else feed.url }} to reach Discord.
|
||||
{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="row g-3 filter-page">
|
||||
|
|
|
|||
Loading…
Reference in a new issue