Compare commits

..

No commits in common. "f56451d05644a58c8ea7a3ab644af241871880e7" and "32a5228174263179688cd730ef9306671b152f21" have entirely different histories.

2 changed files with 23 additions and 34 deletions

View file

@ -16,7 +16,6 @@ import contextlib
import json import json
import logging import logging
import re import re
import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import ClassVar from typing import ClassVar
from typing import cast from typing import cast
@ -103,36 +102,6 @@ def fetch_post(post_id: str) -> JsonObject | None:
http_ok: int = 200 http_ok: int = 200
# ---------------------------------------------------------------------------
# In-memory cache for Hoyolab post data.
# Key: post_id (str)
# Value: tuple of (JsonObject | None, expiry_timestamp)
#
# Prevents redundant API calls when the same post is processed by both
# ``process_entry()`` and ``modify_webhook()`` within a short timeframe.
# ---------------------------------------------------------------------------
_POST_CACHE: dict[str, tuple[JsonObject | None, float]] = {}
#: Cache TTL in seconds. Entries older than this are re-fetched.
_CACHE_TTL_SECONDS: int = 300 # 5 minutes
def _cached_fetch_post(post_id: str) -> JsonObject | None:
"""Return cached post data if fresh, otherwise fetch and cache."""
now: float = time.time()
if post_id in _POST_CACHE:
cached_data, expiry = _POST_CACHE[post_id]
if now < expiry:
logger.debug("Cache hit for Hoyolab post %s", post_id)
return cached_data
logger.debug("Cache expired for Hoyolab post %s", post_id)
# Cache miss or expired — fetch fresh data.
post_data: JsonObject | None = fetch_post(post_id)
_POST_CACHE[post_id] = (post_data, now + _CACHE_TTL_SECONDS)
return post_data
def _as_json_object(value: JsonValue) -> JsonObject: def _as_json_object(value: JsonValue) -> JsonObject:
return cast("JsonObject", value) if isinstance(value, dict) else {} return cast("JsonObject", value) if isinstance(value, dict) else {}
@ -179,7 +148,7 @@ class HoyolabExtension(FeedExtension):
if not post_id: if not post_id:
return {} return {}
post_data: JsonObject | None = _cached_fetch_post(post_id) post_data: JsonObject | None = fetch_post(post_id)
if not post_data: if not post_data:
return {} return {}
@ -233,7 +202,7 @@ class HoyolabExtension(FeedExtension):
if not post_id: if not post_id:
return webhook return webhook
post_data: JsonObject | None = _cached_fetch_post(post_id) post_data: JsonObject | None = fetch_post(post_id)
if not post_data: if not post_data:
return webhook return webhook

View file

@ -56,6 +56,9 @@ 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.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 auto_enable_extensions_for_feed
from discord_rss_bot.extensions import run_modify_webhook from discord_rss_bot.extensions import run_modify_webhook
from discord_rss_bot.extensions.steam import extract_app_id as steam_extract_app_id
from discord_rss_bot.extensions.steam import get_icon_file_for_app as steam_get_icon_file
from discord_rss_bot.extensions.steam import is_steam_url
from discord_rss_bot.filter.evaluator import get_entry_filter_decision_from_reader 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.is_url_valid import is_url_valid
from discord_rss_bot.settings import default_custom_embed from discord_rss_bot.settings import default_custom_embed
@ -1762,7 +1765,7 @@ def create_components_v2_webhook(
) )
def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches] def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches, too-many-statements]
webhook_url: str, webhook_url: str,
entry: Entry, entry: Entry,
reader: Reader, reader: Reader,
@ -1839,6 +1842,20 @@ def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches]
if custom_embed.thumbnail_url: if custom_embed.thumbnail_url:
discord_embed.set_thumbnail(url=custom_embed.thumbnail_url) discord_embed.set_thumbnail(url=custom_embed.thumbnail_url)
# Steam feed: override thumbnail with the game's capsule image when enabled.
steam_thumbnail_file: WebhookFile | None = None
if custom_embed.show_steam_game_icon_in_thumbnail and is_steam_url(feed.url or ""):
app_id: str | None = steam_extract_app_id(feed.url) or steam_extract_app_id(str(entry.link or ""))
if app_id:
icon_file: WebhookFile | None = steam_get_icon_file(app_id)
if icon_file:
steam_thumbnail_file = icon_file
discord_embed.set_thumbnail(url=f"attachment://{icon_file.filename}")
else:
discord_embed.set_thumbnail(
url=f"https://cdn.cloudflare.steamstatic.com/steam/apps/{app_id}/capsule_sm_120.jpg",
)
if custom_embed.image_url: if custom_embed.image_url:
discord_embed.set_image(url=custom_embed.image_url) discord_embed.set_image(url=custom_embed.image_url)
@ -1851,6 +1868,9 @@ def create_embed_webhook( # ruff:ignore[complex-structure, too-many-branches]
if custom_embed.footer_icon_url and not custom_embed.footer_text: if custom_embed.footer_icon_url and not custom_embed.footer_text:
discord_embed.set_footer(text="-", icon_url=custom_embed.footer_icon_url) discord_embed.set_footer(text="-", icon_url=custom_embed.footer_icon_url)
if steam_thumbnail_file:
webhook.add_file(file=steam_thumbnail_file.content, filename=steam_thumbnail_file.filename)
webhook.add_embed(discord_embed) webhook.add_embed(discord_embed)
return webhook return webhook