Merge pull request #12 from prosolis/claude/fix-isthereanydeal-api-W5umt

Fixed post formatting issues
This commit is contained in:
prosolis
2026-02-27 21:33:00 -08:00
committed by GitHub
3 changed files with 43 additions and 56 deletions

View File

@@ -1,25 +1,18 @@
"""Message formatting for Matrix deal posts. """Message formatting for Matrix deal posts.
Produces both a plain-text fallback and HTML formatted body for Matrix messages. Produces both a plain-text fallback and HTML formatted body for Matrix messages.
HTML is built directly using the Matrix-supported HTML subset rather than going
through a Markdown-to-HTML converter, which collapses line breaks.
""" """
from datetime import datetime, timezone from datetime import datetime, timezone
from html import escape
import markdown
from .cheapshark import CheapSharkDeal from .cheapshark import CheapSharkDeal
from .currency import format_price from .currency import format_price
from .epic import EpicFreeGame from .epic import EpicFreeGame
from .itad_deals import ITADDeal from .itad_deals import ITADDeal
_md = markdown.Markdown()
def _render_html(md_text: str) -> str:
"""Convert Markdown to HTML, resetting the parser between calls."""
_md.reset()
return _md.convert(md_text)
def format_deal(deal: CheapSharkDeal, is_historical_low: bool = False) -> tuple[str, str]: def format_deal(deal: CheapSharkDeal, is_historical_low: bool = False) -> tuple[str, str]:
"""Format a CheapShark deal into (plain_text, html) for Matrix. """Format a CheapShark deal into (plain_text, html) for Matrix.
@@ -32,22 +25,18 @@ def format_deal(deal: CheapSharkDeal, is_historical_low: bool = False) -> tuple[
sale_multi = format_price(sale_price) sale_multi = format_price(sale_price)
normal_display = format_price(normal_price) normal_display = format_price(normal_price)
title = escape(deal.title)
lines = [ html_lines = [
f"**🎮 [DEAL] {deal.title}**", f"<strong>🎮 [DEAL] {title}</strong>",
f"> {discount}% off on {deal.store_name} ~~{normal_display}~~", f"{discount}% off on {escape(deal.store_name)} <del>{escape(normal_display)}</del>",
f"> 💰 **{sale_multi}**", f"💰 <strong>{escape(sale_multi)}</strong>",
] ]
if is_historical_low: if is_historical_low:
lines.append("> 🏆 _All-time low!_") html_lines.append("🏆 <em>All-time low!</em>")
html_lines.append(f'🔗 <a href="{escape(deal.deal_url)}">View Deal</a>')
html = "<br>\n".join(html_lines)
lines.append(f"> 🔗 [View Deal]({deal.deal_url})")
md_text = "\n".join(lines)
html = _render_html(md_text)
# Plain text fallback — strip markdown syntax
plain_lines = [ plain_lines = [
f"🎮 [DEAL] {deal.title}", f"🎮 [DEAL] {deal.title}",
f" {discount}% off on {deal.store_name} (was {normal_display})", f" {discount}% off on {deal.store_name} (was {normal_display})",
@@ -56,7 +45,6 @@ def format_deal(deal: CheapSharkDeal, is_historical_low: bool = False) -> tuple[
if is_historical_low: if is_historical_low:
plain_lines.append(" 🏆 All-time low!") plain_lines.append(" 🏆 All-time low!")
plain_lines.append(f" 🔗 {deal.deal_url}") plain_lines.append(f" 🔗 {deal.deal_url}")
plain_text = "\n".join(plain_lines) plain_text = "\n".join(plain_lines)
return plain_text, html return plain_text, html
@@ -69,20 +57,17 @@ def format_itad_deal(deal: ITADDeal) -> tuple[str, str]:
""" """
sale_multi = format_price(deal.sale_price) sale_multi = format_price(deal.sale_price)
normal_display = format_price(deal.normal_price) normal_display = format_price(deal.normal_price)
title = escape(deal.title)
lines = [ html_lines = [
f"**🎮 [DEAL] {deal.title}**", f"<strong>🎮 [DEAL] {title}</strong>",
f"> {deal.discount}% off on {deal.shop_name} ~~{normal_display}~~", f"{deal.discount}% off on {escape(deal.shop_name)} <del>{escape(normal_display)}</del>",
f"> 💰 **{sale_multi}**", f"💰 <strong>{escape(sale_multi)}</strong>",
] ]
if deal.is_historical_low: if deal.is_historical_low:
lines.append("> 🏆 _All-time low!_") html_lines.append("🏆 <em>All-time low!</em>")
html_lines.append(f'🔗 <a href="{escape(deal.url)}">View Deal</a>')
lines.append(f"> 🔗 [View Deal]({deal.url})") html = "<br>\n".join(html_lines)
md_text = "\n".join(lines)
html = _render_html(md_text)
plain_lines = [ plain_lines = [
f"🎮 [DEAL] {deal.title}", f"🎮 [DEAL] {deal.title}",
@@ -92,7 +77,6 @@ def format_itad_deal(deal: ITADDeal) -> tuple[str, str]:
if deal.is_historical_low: if deal.is_historical_low:
plain_lines.append(" 🏆 All-time low!") plain_lines.append(" 🏆 All-time low!")
plain_lines.append(f" 🔗 {deal.url}") plain_lines.append(f" 🔗 {deal.url}")
plain_text = "\n".join(plain_lines) plain_text = "\n".join(plain_lines)
return plain_text, html return plain_text, html
@@ -103,26 +87,25 @@ def format_epic_free(game: EpicFreeGame) -> tuple[str, str]:
Returns (body, formatted_body). Returns (body, formatted_body).
""" """
lines = [ title = escape(game.title)
f"**🆓 [FREE] {game.title}**",
"> Free on Epic Games Store", html_lines = [
f"<strong>🆓 [FREE] {title}</strong>",
"Free on Epic Games Store",
] ]
if game.end_date: if game.end_date:
try: try:
end_dt = datetime.fromisoformat(game.end_date.replace("Z", "+00:00")) end_dt = datetime.fromisoformat(game.end_date.replace("Z", "+00:00"))
date_str = end_dt.strftime("%B %-d") date_str = end_dt.strftime("%B %-d")
lines.append(f"> 📅 _Free until {date_str}_") html_lines.append(f"📅 <em>Free until {date_str}</em>")
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
if game.url: if game.url:
lines.append(f"> 🔗 [Claim Now]({game.url})") html_lines.append(f'🔗 <a href="{escape(game.url)}">Claim Now</a>')
html = "<br>\n".join(html_lines)
md_text = "\n".join(lines)
html = _render_html(md_text)
# Plain text fallback
plain_lines = [ plain_lines = [
f"🆓 [FREE] {game.title}", f"🆓 [FREE] {game.title}",
" Free on Epic Games Store", " Free on Epic Games Store",
@@ -136,7 +119,6 @@ def format_epic_free(game: EpicFreeGame) -> tuple[str, str]:
pass pass
if game.url: if game.url:
plain_lines.append(f" 🔗 {game.url}") plain_lines.append(f" 🔗 {game.url}")
plain_text = "\n".join(plain_lines) plain_text = "\n".join(plain_lines)
return plain_text, html return plain_text, html
@@ -144,24 +126,24 @@ def format_epic_free(game: EpicFreeGame) -> tuple[str, str]:
def format_epic_upcoming(game: EpicFreeGame) -> tuple[str, str]: def format_epic_upcoming(game: EpicFreeGame) -> tuple[str, str]:
"""Format an upcoming Epic free game into (plain_text, html) for Matrix.""" """Format an upcoming Epic free game into (plain_text, html) for Matrix."""
lines = [ title = escape(game.title)
f"**📢 [UPCOMING FREE] {game.title}**",
"> Coming soon — Free on Epic Games Store", html_lines = [
f"<strong>📢 [UPCOMING FREE] {title}</strong>",
"Coming soon — Free on Epic Games Store",
] ]
if game.end_date: if game.end_date:
try: try:
end_dt = datetime.fromisoformat(game.end_date.replace("Z", "+00:00")) end_dt = datetime.fromisoformat(game.end_date.replace("Z", "+00:00"))
date_str = end_dt.strftime("%B %-d") date_str = end_dt.strftime("%B %-d")
lines.append(f"> 📅 _Free until {date_str}_") html_lines.append(f"📅 <em>Free until {date_str}</em>")
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
if game.url: if game.url:
lines.append(f"> 🔗 [Store Page]({game.url})") html_lines.append(f'🔗 <a href="{escape(game.url)}">Store Page</a>')
html = "<br>\n".join(html_lines)
md_text = "\n".join(lines)
html = _render_html(md_text)
plain_lines = [ plain_lines = [
f"📢 [UPCOMING FREE] {game.title}", f"📢 [UPCOMING FREE] {game.title}",
@@ -169,7 +151,6 @@ def format_epic_upcoming(game: EpicFreeGame) -> tuple[str, str]:
] ]
if game.url: if game.url:
plain_lines.append(f" 🔗 {game.url}") plain_lines.append(f" 🔗 {game.url}")
plain_text = "\n".join(plain_lines) plain_text = "\n".join(plain_lines)
return plain_text, html return plain_text, html

View File

@@ -81,11 +81,18 @@ async def fetch_deals(
if not deal_data: if not deal_data:
continue continue
# Only include actual games and DLC — skip non-game content
# (courses, software bundles, etc.)
entry_type = entry.get("type")
title = entry.get("title", "?")
if entry_type not in ("game", "dlc"):
logger.debug("Filtered out %s: type=%s", title, entry_type)
continue
cut = deal_data.get("cut", 0) cut = deal_data.get("cut", 0)
price_amount = deal_data.get("price", {}).get("amount", 0) price_amount = deal_data.get("price", {}).get("amount", 0)
regular_amount = deal_data.get("regular", {}).get("amount", 0) regular_amount = deal_data.get("regular", {}).get("amount", 0)
currency = deal_data.get("price", {}).get("currency", "USD") currency = deal_data.get("price", {}).get("currency", "USD")
title = entry.get("title", "?")
# Apply filters # Apply filters
if cut < min_discount: if cut < min_discount:

View File

@@ -2,5 +2,4 @@ matrix-nio>=0.21.0
httpx>=0.25.0 httpx>=0.25.0
aiosqlite>=0.19.0 aiosqlite>=0.19.0
apscheduler>=3.10.0 apscheduler>=3.10.0
markdown>=3.5.0
python-dotenv>=1.0.0 python-dotenv>=1.0.0