Fix warnings; use httpx2 and authors_str
This commit is contained in:
parent
1065838ef7
commit
c481c7c88f
14 changed files with 65 additions and 49 deletions
|
|
@ -155,7 +155,7 @@ def replace_tags_in_text_message(entry: Entry, reader: Reader) -> str:
|
|||
entry_updated: str = entry.updated.strftime("%Y-%m-%d %H:%M:%S") if entry.updated else "Never"
|
||||
|
||||
list_of_replacements: list[dict[str, str]] = [
|
||||
{"{{feed_author}}": feed.author or ""},
|
||||
{"{{feed_author}}": feed.authors_str or ""},
|
||||
{"{{feed_added}}": feed_added},
|
||||
{"{{feed_last_exception}}": feed_last_exception},
|
||||
{"{{feed_last_updated}}": feed_last_updated},
|
||||
|
|
@ -168,7 +168,7 @@ def replace_tags_in_text_message(entry: Entry, reader: Reader) -> str:
|
|||
{"{{feed_user_title}}": feed.user_title or ""},
|
||||
{"{{feed_version}}": feed.version or ""},
|
||||
{"{{entry_added}}": entry_added},
|
||||
{"{{entry_author}}": entry.author or ""},
|
||||
{"{{entry_author}}": entry.authors_str or ""},
|
||||
{"{{entry_content}}": content},
|
||||
{"{{entry_content_raw}}": entry.content[0].value if entry.content else ""},
|
||||
{"{{entry_id}}": entry.id or ""},
|
||||
|
|
@ -318,7 +318,7 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb
|
|||
embed.title = ""
|
||||
|
||||
list_of_replacements: list[dict[str, str]] = [
|
||||
{"{{feed_author}}": feed.author or ""},
|
||||
{"{{feed_author}}": feed.authors_str or ""},
|
||||
{"{{feed_added}}": feed_added or ""},
|
||||
{"{{feed_last_exception}}": feed_last_exception},
|
||||
{"{{feed_last_updated}}": feed_last_updated or ""},
|
||||
|
|
@ -331,7 +331,7 @@ def replace_tags_in_embed(feed: Feed, entry: Entry, reader: Reader) -> CustomEmb
|
|||
{"{{feed_user_title}}": feed.user_title or ""},
|
||||
{"{{feed_version}}": feed.version or ""},
|
||||
{"{{entry_added}}": entry_added or ""},
|
||||
{"{{entry_author}}": entry.author or ""},
|
||||
{"{{entry_author}}": entry.authors_str or ""},
|
||||
{"{{entry_content}}": content or ""},
|
||||
{"{{entry_content_raw}}": entry.content[0].value if entry.content else ""},
|
||||
{"{{entry_id}}": entry.id},
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@ from urllib.parse import parse_qs
|
|||
from urllib.parse import urljoin
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
import httpx2
|
||||
import tldextract
|
||||
from fastapi import HTTPException
|
||||
from httpx2 import HTTPError
|
||||
from httpx2 import Response
|
||||
from markdownify import markdownify
|
||||
from playwright.sync_api import Browser
|
||||
from playwright.sync_api import Page
|
||||
|
|
@ -702,7 +704,7 @@ def get_webhook_files(webhook: DiscordWebhook) -> list[WebhookFile]: # noqa: C9
|
|||
return files
|
||||
|
||||
|
||||
def get_retry_after_seconds(response: httpx.Response) -> float | None:
|
||||
def get_retry_after_seconds(response: Response) -> float | None:
|
||||
"""Return Discord's retry delay for a rate-limited response when available."""
|
||||
response_json: JsonObject = get_response_json(response)
|
||||
retry_after: JsonValue = response_json.get("retry_after")
|
||||
|
|
@ -727,7 +729,7 @@ def request_discord_webhook(
|
|||
files: list[WebhookFile] | None,
|
||||
timeout: float,
|
||||
rate_limit_retry: bool,
|
||||
) -> httpx.Response:
|
||||
) -> Response:
|
||||
"""Send a Discord webhook request with optional multipart files.
|
||||
|
||||
Returns:
|
||||
|
|
@ -742,7 +744,7 @@ def request_discord_webhook(
|
|||
else:
|
||||
request_kwargs["json"] = payload
|
||||
|
||||
response: httpx.Response = httpx.request(method, url, **request_kwargs)
|
||||
response: Response = httpx2.request(method, url, **request_kwargs)
|
||||
if not rate_limit_retry or response.status_code != 429: # noqa: PLR2004
|
||||
return response
|
||||
|
||||
|
|
@ -751,11 +753,11 @@ def request_discord_webhook(
|
|||
return response
|
||||
|
||||
time.sleep(max(0.0, retry_after))
|
||||
return httpx.request(method, url, **request_kwargs)
|
||||
return httpx2.request(method, url, **request_kwargs)
|
||||
|
||||
|
||||
def send_webhook_message(webhook: DiscordWebhook, payload: JsonObject) -> httpx.Response:
|
||||
"""Execute a Discord webhook message create request using httpx.
|
||||
def send_webhook_message(webhook: DiscordWebhook, payload: JsonObject) -> Response:
|
||||
"""Execute a Discord webhook message create request using httpx2.
|
||||
|
||||
Returns:
|
||||
Discord API response.
|
||||
|
|
@ -777,11 +779,11 @@ def edit_sent_webhook_message(
|
|||
message_id: str,
|
||||
webhook: DiscordWebhook,
|
||||
payload: JsonObject,
|
||||
) -> httpx.Response:
|
||||
) -> Response:
|
||||
"""Edit an already-sent Discord webhook message.
|
||||
|
||||
Returns:
|
||||
httpx.Response: Discord API response.
|
||||
Response: Discord API response.
|
||||
"""
|
||||
clean_webhook_url, params = get_webhook_query_params(webhook_url, payload, webhook=webhook, wait=True)
|
||||
return request_discord_webhook(
|
||||
|
|
@ -938,8 +940,13 @@ def update_sent_webhook_record_for_entry(
|
|||
|
||||
now: str = datetime.datetime.now(tz=datetime.UTC).isoformat()
|
||||
try:
|
||||
response = edit_sent_webhook_message(webhook_url_value, message_id_value, webhook, edit_payload)
|
||||
except (AssertionError, RequestException, httpx.HTTPError, OSError, ValueError) as e:
|
||||
response: Response = edit_sent_webhook_message(
|
||||
webhook_url=webhook_url_value,
|
||||
message_id=message_id_value,
|
||||
webhook=webhook,
|
||||
payload=edit_payload,
|
||||
)
|
||||
except (AssertionError, RequestException, HTTPError, OSError, ValueError) as e:
|
||||
logger.exception("Failed to edit Discord webhook message %s for entry %s", message_id_value, entry.id)
|
||||
return (
|
||||
{
|
||||
|
|
@ -1484,13 +1491,13 @@ def fetch_ttvdrops_campaign_media_items(entry: Entry) -> list[JsonObject]:
|
|||
return []
|
||||
|
||||
try:
|
||||
response: httpx.Response = httpx.get(api_url, follow_redirects=True, timeout=10.0)
|
||||
response: Response = httpx2.get(api_url, follow_redirects=True, timeout=10.0)
|
||||
if response.status_code != 200: # noqa: PLR2004
|
||||
logger.warning("Failed to fetch ttvdrops campaign data from %s: %s", api_url, response.text[:500])
|
||||
return []
|
||||
|
||||
response_json = cast("JsonValue", response.json())
|
||||
except (httpx.HTTPError, ValueError, TypeError):
|
||||
except (HTTPError, ValueError, TypeError):
|
||||
logger.exception("Failed to fetch ttvdrops campaign data from %s", api_url)
|
||||
return []
|
||||
|
||||
|
|
@ -1759,7 +1766,7 @@ def send_to_discord(reader: Reader | None = None, feed: Feed | None = None, *, d
|
|||
)
|
||||
try:
|
||||
update_sent_webhooks_for_modified_entries(effective_reader, modified_entries)
|
||||
except (AssertionError, ReaderError, RequestException, httpx.HTTPError, OSError, ValueError):
|
||||
except (AssertionError, ReaderError, RequestException, HTTPError, OSError, ValueError):
|
||||
logger.exception("Failed to update saved Discord webhooks for modified feed entries.")
|
||||
|
||||
# Loop through the unread entries.
|
||||
|
|
@ -1826,7 +1833,7 @@ def execute_webhook(
|
|||
|
||||
request_payload: JsonObject = get_webhook_request_payload(webhook)
|
||||
payload: JsonObject = get_webhook_message_payload(webhook)
|
||||
response: httpx.Response = send_webhook_message(webhook, request_payload)
|
||||
response: Response = send_webhook_message(webhook, request_payload)
|
||||
logger.debug("Discord webhook response for entry %s: status=%s", entry.id, response.status_code)
|
||||
if response.status_code not in {200, 204}:
|
||||
msg: str = f"Error sending entry to Discord: {response.text}\n{pprint.pformat(request_payload)}"
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ def get_entry_fields(entry: Entry) -> dict[str, str]:
|
|||
"title": entry.title or "",
|
||||
"summary": entry.summary or "",
|
||||
"content": content_value,
|
||||
"author": entry.author or "",
|
||||
"author": entry.authors_str or "",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from typing import Annotated
|
|||
from typing import TypedDict
|
||||
from typing import cast
|
||||
|
||||
import httpx
|
||||
import httpx2
|
||||
import sentry_sdk
|
||||
import uvicorn
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
|
@ -30,7 +30,8 @@ from fastapi import Request
|
|||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from httpx import Response
|
||||
from httpx2 import HTTPError
|
||||
from httpx2 import Response
|
||||
from markdownify import markdownify
|
||||
from reader import Entry
|
||||
from reader import EntryNotFoundError
|
||||
|
|
@ -82,6 +83,7 @@ from discord_rss_bot.settings import get_reader
|
|||
if TYPE_CHECKING:
|
||||
from collections.abc import AsyncGenerator
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
from reader.types import JSONType
|
||||
|
||||
|
|
@ -1881,7 +1883,7 @@ def create_html_for_feed( # noqa: C901, PLR0914
|
|||
|
||||
html += f"""<div class="p-2 mb-2 border border-dark">
|
||||
{blacklisted}{whitelisted}{from_another_feed}<a class="text-muted text-decoration-none" href="{entry.link}"><h2>{entry.title}</h2></a>
|
||||
{feed_link}{f"By {entry.author} @" if entry.author else ""}{published} - {to_discord_html}
|
||||
{feed_link}{f"By {entry.authors_str} @" if entry.authors_str else ""}{published} - {to_discord_html}
|
||||
|
||||
{text}
|
||||
{video_embed_html}
|
||||
|
|
@ -1939,8 +1941,8 @@ def get_data_from_hook_url(hook_name: str, hook_url: str) -> WebhookInfo:
|
|||
return our_hook
|
||||
|
||||
try:
|
||||
response: Response = httpx.get(clean_hook_url, timeout=10.0)
|
||||
except httpx.HTTPError as e:
|
||||
response: Response = httpx2.get(clean_hook_url, timeout=10.0)
|
||||
except HTTPError as e:
|
||||
logger.warning("Failed to fetch webhook metadata for %s: %s", clean_hook_url, e)
|
||||
return our_hook
|
||||
|
||||
|
|
@ -2209,7 +2211,7 @@ async def update_feed(
|
|||
|
||||
try:
|
||||
update_sent_webhooks_for_modified_entries(reader, modified_entries)
|
||||
except (AssertionError, ReaderError, httpx.HTTPError, OSError, ValueError):
|
||||
except (AssertionError, ReaderError, HTTPError, OSError, ValueError):
|
||||
logger.exception("Failed to update saved Discord webhooks for manually updated feed: %s", feed_url)
|
||||
|
||||
logger.info("Manually updated feed: %s", feed_url)
|
||||
|
|
@ -2230,18 +2232,18 @@ async def manual_backup(
|
|||
Returns:
|
||||
RedirectResponse: Redirect to the index page with a success or error message.
|
||||
"""
|
||||
backup_path = get_backup_path()
|
||||
backup_path: Path | None = get_backup_path()
|
||||
if backup_path is None:
|
||||
message = "Git backup is not configured. Set GIT_BACKUP_PATH environment variable to enable backups."
|
||||
message: str = "Git backup is not configured. Set GIT_BACKUP_PATH environment variable to enable backups."
|
||||
logger.warning("Manual git backup attempted but GIT_BACKUP_PATH is not configured")
|
||||
return RedirectResponse(url=f"/?message={urllib.parse.quote(message)}", status_code=303)
|
||||
|
||||
try:
|
||||
commit_state_change(reader, "Manual backup triggered from web UI")
|
||||
message = "Successfully created git backup!"
|
||||
message: str = "Successfully created git backup!"
|
||||
logger.info("Manual git backup completed successfully")
|
||||
except Exception as e:
|
||||
message = f"Failed to create git backup: {e}"
|
||||
message: str = f"Failed to create git backup: {e}"
|
||||
logger.exception("Manual git backup failed")
|
||||
|
||||
return RedirectResponse(url=f"/?message={urllib.parse.quote(message)}", status_code=303)
|
||||
|
|
@ -2419,8 +2421,8 @@ def resolve_final_feed_url(url: str) -> tuple[str, str | None]:
|
|||
return clean_url, "URL is invalid"
|
||||
|
||||
try:
|
||||
response: Response = httpx.get(clean_url, follow_redirects=True, timeout=10.0)
|
||||
except httpx.HTTPError as e:
|
||||
response: Response = httpx2.get(clean_url, follow_redirects=True, timeout=10.0)
|
||||
except HTTPError as e:
|
||||
return clean_url, str(e)
|
||||
|
||||
if not response.is_success:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
{% endif %}
|
||||
</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
{% if row.entry.author %}By {{ row.entry.author }} |{% endif %}
|
||||
{% if row.entry.authors_str %}By {{ row.entry.authors_str }} |{% endif %}
|
||||
{{ row.published_label }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
{% raw %}
|
||||
{{feed_author}}
|
||||
{% endraw %}
|
||||
</code>{{ feed.author }}
|
||||
</code>{{ feed.authors_str }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
|
|
@ -114,7 +114,7 @@
|
|||
{% raw %}
|
||||
{{entry_author}}
|
||||
{% endraw %}
|
||||
</code>{{ entry.author }}
|
||||
</code>{{ entry.authors_str }}
|
||||
</li>
|
||||
{% if entry.content %}
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
{% raw %}
|
||||
{{feed_author}}
|
||||
{% endraw %}
|
||||
</code>{{feed.author}}
|
||||
</code>{{feed.authors_str}}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
{% raw %}
|
||||
{{entry_author}}
|
||||
{% endraw %}
|
||||
</code>{{entry.author}}
|
||||
</code>{{entry.authors_str}}
|
||||
</li>
|
||||
{% if entry.content %}
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class DiscordWebhook:
|
|||
"""Discord webhook request data.
|
||||
|
||||
This intentionally mirrors the subset of `discord-webhook` used by the app
|
||||
while leaving the actual HTTP transport to `httpx`.
|
||||
while leaving the actual HTTP transport to `httpx2`.
|
||||
"""
|
||||
|
||||
def __init__( # noqa: D107
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue