diff --git a/.vscode/launch.json b/.vscode/launch.json index cb46920..781b0bd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,4 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { @@ -9,7 +6,10 @@ "type": "debugpy", "request": "launch", "module": "uvicorn", - "args": ["discord_rss_bot.main:app", "--reload"], + "args": [ + "discord_rss_bot.main:app", + "--reload" + ], "jinja": true, "justMyCode": true } diff --git a/.vscode/settings.json b/.vscode/settings.json index ab0e63e..f929fff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,8 +3,11 @@ "botuser", "Genshins", "levelname", + "Lovinator", + "markdownified", "markdownify", "pipx", "thead" - ] + ], + "python.analysis.typeCheckingMode": "basic" } diff --git a/discord_rss_bot/custom_filters.py b/discord_rss_bot/custom_filters.py index 090aef2..99fe77d 100644 --- a/discord_rss_bot/custom_filters.py +++ b/discord_rss_bot/custom_filters.py @@ -1,12 +1,16 @@ +from __future__ import annotations + import urllib.parse from functools import lru_cache - -from reader import Entry, Reader +from typing import TYPE_CHECKING from discord_rss_bot.filter.blacklist import entry_should_be_skipped, feed_has_blacklist_tags from discord_rss_bot.filter.whitelist import has_white_tags, should_be_sent from discord_rss_bot.settings import get_reader +if TYPE_CHECKING: + from reader import Entry, Reader + # Our reader reader: Reader = get_reader() diff --git a/discord_rss_bot/custom_message.py b/discord_rss_bot/custom_message.py index e63d059..9cb03e5 100644 --- a/discord_rss_bot/custom_message.py +++ b/discord_rss_bot/custom_message.py @@ -4,7 +4,7 @@ import json import logging from dataclasses import dataclass -from bs4 import BeautifulSoup +from bs4 import BeautifulSoup, Tag from markdownify import markdownify from reader import Entry, Feed, Reader, TagNotFoundError @@ -143,8 +143,13 @@ def get_first_image(summary: str | None, content: str | None) -> str: # TODO(TheLovinator): We should find a better way to get the image. if content and (images := BeautifulSoup(content, features="lxml").find_all("img")): for image in images: - if not is_url_valid(image.attrs["src"]): - logger.warning("Invalid URL: %s", image.attrs["src"]) + if not isinstance(image, Tag) or "src" not in image.attrs: + logger.error("Image is not a Tag or does not have a src attribute.") + continue + + src = str(image.attrs["src"]) + if not is_url_valid(src): + logger.warning("Invalid URL: %s", src) continue # Genshins first image is a divider, so we ignore it. @@ -153,16 +158,20 @@ def get_first_image(summary: str | None, content: str | None) -> str: "https://img-os-static.hoyolab.com/divider_config/", "https://hyl-static-res-prod.hoyolab.com/divider_config/", ] - if not image.attrs["src"].startswith(tuple(skip_images)): + if not str(image.attrs["src"]).startswith(tuple(skip_images)): return str(image.attrs["src"]) if summary and (images := BeautifulSoup(summary, features="lxml").find_all("img")): for image in images: - if not is_url_valid(image.attrs["src"]): + if not isinstance(image, Tag) or "src" not in image.attrs: + logger.error("Image is not a Tag or does not have a src attribute.") + continue + + if not is_url_valid(str(image.attrs["src"])): logger.warning("Invalid URL: %s", image.attrs["src"]) continue # Genshins first image is a divider, so we ignore it. - if not image.attrs["src"].startswith("https://img-os-static.hoyolab.com/divider_config"): + if not str(image.attrs["src"]).startswith("https://img-os-static.hoyolab.com/divider_config"): return str(image.attrs["src"]) return "" @@ -317,8 +326,7 @@ def save_embed(custom_reader: Reader, feed: Feed, embed: CustomEmbed) -> None: "footer_text": embed.footer_text, "footer_icon_url": embed.footer_icon_url, } - - custom_reader.set_tag(feed, "embed", json.dumps(embed_dict)) # type: ignore + custom_reader.set_tag(feed, "embed", json.dumps(embed_dict)) # pyright: ignore[reportArgumentType] def get_embed(custom_reader: Reader, feed: Feed) -> CustomEmbed: @@ -335,7 +343,7 @@ def get_embed(custom_reader: Reader, feed: Feed) -> CustomEmbed: if embed: if not isinstance(embed, str): - return get_embed_data(embed) # type: ignore + return get_embed_data(embed) # pyright: ignore[reportArgumentType] embed_data: dict[str, str | int] = json.loads(embed) return get_embed_data(embed_data) diff --git a/discord_rss_bot/feeds.py b/discord_rss_bot/feeds.py index aff8bee..ccb0a14 100644 --- a/discord_rss_bot/feeds.py +++ b/discord_rss_bot/feeds.py @@ -9,7 +9,12 @@ from discord_webhook import DiscordEmbed, DiscordWebhook from fastapi import HTTPException from reader import Entry, EntryNotFoundError, Feed, FeedExistsError, Reader, ReaderError, StorageError, TagNotFoundError -from discord_rss_bot import custom_message +from discord_rss_bot.custom_message import ( + CustomEmbed, + get_custom_message, + replace_tags_in_embed, + replace_tags_in_text_message, +) from discord_rss_bot.filter.blacklist import entry_should_be_skipped from discord_rss_bot.filter.whitelist import has_white_tags, should_be_sent from discord_rss_bot.is_url_valid import is_url_valid @@ -42,10 +47,12 @@ def send_entry_to_discord(entry: Entry, custom_reader: Reader | None = None) -> if not webhook_url: return "No webhook URL found." + webhook_message: str = "" + # Try to get the custom message for the feed. If the user has none, we will use the default message. # This has to be a string for some reason so don't change it to "not custom_message.get_custom_message()" - if custom_message.get_custom_message(reader, entry.feed) != "": # noqa: PLC1901 - webhook_message: str = custom_message.replace_tags_in_text_message(entry=entry) + if get_custom_message(reader, entry.feed) != "": # noqa: PLC1901 + webhook_message: str = replace_tags_in_text_message(entry=entry) if not webhook_message: webhook_message = "No message found." @@ -69,7 +76,7 @@ def send_entry_to_discord(entry: Entry, custom_reader: Reader | None = None) -> return None -def set_description(custom_embed: custom_message.CustomEmbed, discord_embed: DiscordEmbed) -> None: +def set_description(custom_embed: CustomEmbed, discord_embed: DiscordEmbed) -> None: """Set the description of the embed. Args: @@ -87,7 +94,7 @@ def set_description(custom_embed: custom_message.CustomEmbed, discord_embed: Dis discord_embed.set_description(embed_description) if embed_description else None -def set_title(custom_embed: custom_message.CustomEmbed, discord_embed: DiscordEmbed) -> None: +def set_title(custom_embed: CustomEmbed, discord_embed: DiscordEmbed) -> None: """Set the title of the embed. Args: @@ -115,7 +122,7 @@ def create_embed_webhook(webhook_url: str, entry: Entry) -> DiscordWebhook: feed: Feed = entry.feed # Get the embed data from the database. - custom_embed: custom_message.CustomEmbed = custom_message.replace_tags_in_embed(feed=feed, entry=entry) + custom_embed: CustomEmbed = replace_tags_in_embed(feed=feed, entry=entry) discord_embed: DiscordEmbed = DiscordEmbed() @@ -238,8 +245,8 @@ def send_to_discord(custom_reader: Reader | None = None, feed: Feed | None = Non else: # If the user has set the custom message to an empty string, we will use the default message, otherwise we # will use the custom message. - if custom_message.get_custom_message(reader, entry.feed) != "": # noqa: PLC1901 - webhook_message = custom_message.replace_tags_in_text_message(entry) + if get_custom_message(reader, entry.feed) != "": # noqa: PLC1901 + webhook_message = replace_tags_in_text_message(entry) else: webhook_message: str = str(default_custom_message) @@ -342,8 +349,12 @@ def create_feed(reader: Reader, feed_url: str, webhook_dropdown: str) -> None: if hooks := reader.get_tag((), "webhooks", []): # Get the webhook URL from the dropdown. for hook in hooks: - if hook["name"] == webhook_dropdown: # type: ignore - webhook_url = hook["url"] # type: ignore + if not isinstance(hook, dict): + logger.error("Webhook is not a dict: %s", hook) + continue + + if hook["name"] == webhook_dropdown: # pyright: ignore[reportArgumentType] + webhook_url = hook["url"] break if not webhook_url: @@ -356,7 +367,7 @@ def create_feed(reader: Reader, feed_url: str, webhook_dropdown: str) -> None: try: reader.get_tag(clean_feed_url, "webhook") except TagNotFoundError: - reader.set_tag(clean_feed_url, "webhook", webhook_url) # type: ignore + reader.set_tag(clean_feed_url, "webhook", webhook_url) # pyright: ignore[reportArgumentType] except ReaderError as e: raise HTTPException(status_code=404, detail=f"Error adding feed: {e}") from e @@ -375,10 +386,10 @@ def create_feed(reader: Reader, feed_url: str, webhook_dropdown: str) -> None: raise HTTPException(status_code=404, detail="Default custom message couldn't be found.") # This is the webhook that will be used to send the feed to Discord. - reader.set_tag(clean_feed_url, "webhook", webhook_url) # type: ignore + reader.set_tag(clean_feed_url, "webhook", webhook_url) # pyright: ignore[reportArgumentType] # This is the default message that will be sent to Discord. - reader.set_tag(clean_feed_url, "custom_message", default_custom_message) # type: ignore + reader.set_tag(clean_feed_url, "custom_message", default_custom_message) # pyright: ignore[reportArgumentType] # Update the full-text search index so our new feed is searchable. reader.update_search() diff --git a/discord_rss_bot/filter/blacklist.py b/discord_rss_bot/filter/blacklist.py index aabd21b..808d7c9 100644 --- a/discord_rss_bot/filter/blacklist.py +++ b/discord_rss_bot/filter/blacklist.py @@ -1,7 +1,12 @@ -from reader import Entry, Feed, Reader +from __future__ import annotations + +from typing import TYPE_CHECKING from discord_rss_bot.filter.utils import is_word_in_text +if TYPE_CHECKING: + from reader import Entry, Feed, Reader + def feed_has_blacklist_tags(custom_reader: Reader, feed: Feed) -> bool: """Return True if the feed has blacklist tags. diff --git a/discord_rss_bot/filter/whitelist.py b/discord_rss_bot/filter/whitelist.py index eb884f6..a55a514 100644 --- a/discord_rss_bot/filter/whitelist.py +++ b/discord_rss_bot/filter/whitelist.py @@ -1,7 +1,12 @@ -from reader import Entry, Feed, Reader +from __future__ import annotations + +from typing import TYPE_CHECKING from discord_rss_bot.filter.utils import is_word_in_text +if TYPE_CHECKING: + from reader import Entry, Feed, Reader + def has_white_tags(custom_reader: Reader, feed: Feed) -> bool: """Return True if the feed has whitelist tags. diff --git a/discord_rss_bot/healthcheck.py b/discord_rss_bot/healthcheck.py index 6940ce7..5c35a1e 100644 --- a/discord_rss_bot/healthcheck.py +++ b/discord_rss_bot/healthcheck.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import requests diff --git a/discord_rss_bot/is_url_valid.py b/discord_rss_bot/is_url_valid.py index 28c7edb..cca1491 100644 --- a/discord_rss_bot/is_url_valid.py +++ b/discord_rss_bot/is_url_valid.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from urllib.parse import ParseResult, urlparse diff --git a/discord_rss_bot/main.py b/discord_rss_bot/main.py index c98f4bd..82eae26 100644 --- a/discord_rss_bot/main.py +++ b/discord_rss_bot/main.py @@ -7,7 +7,7 @@ import typing import urllib.parse from contextlib import asynccontextmanager from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from functools import lru_cache from typing import TYPE_CHECKING, Annotated, cast @@ -21,7 +21,6 @@ from fastapi.templating import Jinja2Templates from httpx import Response from markdownify import markdownify from reader import Entry, EntryNotFoundError, Feed, FeedNotFoundError, Reader, TagNotFoundError -from reader.types import JSONType from starlette.responses import RedirectResponse from discord_rss_bot import settings @@ -45,6 +44,8 @@ from discord_rss_bot.settings import get_reader if TYPE_CHECKING: from collections.abc import Iterable + from reader.types import JSONType + LOGGING_CONFIG = { "version": 1, @@ -93,7 +94,7 @@ async def lifespan(app: FastAPI) -> typing.AsyncGenerator[None]: # Update all feeds every 15 minutes. # TODO(TheLovinator): Make this configurable. - scheduler.add_job(send_to_discord, "interval", minutes=15, next_run_time=datetime.now(tz=timezone.utc)) + scheduler.add_job(send_to_discord, "interval", minutes=15, next_run_time=datetime.now(tz=UTC)) scheduler.start() logger.info("Scheduler started.") yield @@ -135,7 +136,7 @@ async def post_add_webhook( # Webhooks are stored as a list of dictionaries. # Example: [{"name": "webhook_name", "url": "webhook_url"}] - webhooks = cast(list[dict[str, str]], webhooks) + webhooks = cast("list[dict[str, str]]", webhooks) # Only add the webhook if it doesn't already exist. stripped_webhook_name = webhook_name.strip() @@ -143,7 +144,7 @@ async def post_add_webhook( # Add the new webhook to the list of webhooks. webhooks.append({"name": webhook_name.strip(), "url": webhook_url.strip()}) - reader.set_tag((), "webhooks", webhooks) # type: ignore + reader.set_tag((), "webhooks", webhooks) # pyright: ignore[reportArgumentType] return RedirectResponse(url="/", status_code=303) @@ -172,7 +173,7 @@ async def post_delete_webhook(webhook_url: Annotated[str, Form()]) -> RedirectRe # Webhooks are stored as a list of dictionaries. # Example: [{"name": "webhook_name", "url": "webhook_url"}] - webhooks = cast(list[dict[str, str]], webhooks) + webhooks = cast("list[dict[str, str]]", webhooks) # Only add the webhook if it doesn't already exist. webhooks_to_remove: list[dict[str, str]] = [ @@ -188,7 +189,7 @@ async def post_delete_webhook(webhook_url: Annotated[str, Form()]) -> RedirectRe raise HTTPException(status_code=500, detail="Webhook could not be deleted") # Add our new list of webhooks to the database. - reader.set_tag((), "webhooks", webhooks) # type: ignore + reader.set_tag((), "webhooks", webhooks) # pyright: ignore[reportArgumentType] return RedirectResponse(url="/", status_code=303) @@ -263,10 +264,10 @@ async def post_set_whitelist( RedirectResponse: Redirect to the feed page. """ clean_feed_url: str = feed_url.strip() if feed_url else "" - reader.set_tag(clean_feed_url, "whitelist_title", whitelist_title) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "whitelist_summary", whitelist_summary) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "whitelist_content", whitelist_content) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "whitelist_author", whitelist_author) # type: ignore[call-overload] + reader.set_tag(clean_feed_url, "whitelist_title", whitelist_title) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "whitelist_summary", whitelist_summary) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "whitelist_content", whitelist_content) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "whitelist_author", whitelist_author) # pyright: ignore[reportArgumentType][call-overload] return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303) @@ -326,10 +327,10 @@ async def post_set_blacklist( RedirectResponse: Redirect to the feed page. """ clean_feed_url: str = feed_url.strip() if feed_url else "" - reader.set_tag(clean_feed_url, "blacklist_title", blacklist_title) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "blacklist_summary", blacklist_summary) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "blacklist_content", blacklist_content) # type: ignore[call-overload] - reader.set_tag(clean_feed_url, "blacklist_author", blacklist_author) # type: ignore[call-overload] + reader.set_tag(clean_feed_url, "blacklist_title", blacklist_title) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "blacklist_summary", blacklist_summary) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "blacklist_content", blacklist_content) # pyright: ignore[reportArgumentType][call-overload] + reader.set_tag(clean_feed_url, "blacklist_author", blacklist_author) # pyright: ignore[reportArgumentType][call-overload] return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303) @@ -379,10 +380,10 @@ async def post_set_custom( RedirectResponse: Redirect to the feed page. """ our_custom_message: JSONType | str = custom_message.strip() - our_custom_message = typing.cast(JSONType, our_custom_message) + our_custom_message = typing.cast("JSONType", our_custom_message) default_custom_message: JSONType | str = settings.default_custom_message - default_custom_message = typing.cast(JSONType, default_custom_message) + default_custom_message = typing.cast("JSONType", default_custom_message) if our_custom_message: reader.set_tag(feed_url, "custom_message", our_custom_message) @@ -406,7 +407,7 @@ async def get_custom(feed_url: str, request: Request): """ feed: Feed = reader.get_feed(urllib.parse.unquote(feed_url.strip())) - context = { + context: dict[str, Request | Feed | str | Entry] = { "request": request, "feed": feed, "custom_message": get_custom_message(reader, feed), @@ -435,7 +436,7 @@ async def get_embed_page(feed_url: str, request: Request): # Get previous data, this is used when creating the form. embed: CustomEmbed = get_embed(reader, feed) - context = { + context: dict[str, Request | Feed | str | Entry | CustomEmbed] = { "request": request, "feed": feed, "title": embed.title, @@ -523,7 +524,7 @@ async def post_use_embed(feed_url: Annotated[str, Form()]) -> RedirectResponse: RedirectResponse: Redirect to the feed page. """ clean_feed_url: str = feed_url.strip() - reader.set_tag(clean_feed_url, "should_send_embed", True) # type: ignore + reader.set_tag(clean_feed_url, "should_send_embed", True) # pyright: ignore[reportArgumentType] return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303) @@ -538,7 +539,7 @@ async def post_use_text(feed_url: Annotated[str, Form()]) -> RedirectResponse: RedirectResponse: Redirect to the feed page. """ clean_feed_url: str = feed_url.strip() - reader.set_tag(clean_feed_url, "should_send_embed", False) # type: ignore + reader.set_tag(clean_feed_url, "should_send_embed", False) # pyright: ignore[reportArgumentType] return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303) @@ -764,10 +765,15 @@ async def get_webhooks(request: Request): Returns: HTMLResponse: The add webhook page. """ - hooks_with_data = [] + hooks_with_data: list[WebhookInfo] = [] - for hook in list(reader.get_tag((), "webhooks", [])): - our_hook: WebhookInfo = get_data_from_hook_url(hook_url=hook["url"], hook_name=hook["name"]) # type: ignore + webhook_list = list(reader.get_tag((), "webhooks", [])) + for hook in webhook_list: + if not isinstance(hook, dict): + logger.error("Webhook is not a dict: %s", hook) + continue + + our_hook: WebhookInfo = get_data_from_hook_url(hook_url=hook["url"], hook_name=hook["name"]) hooks_with_data.append(our_hook) context = {"request": request, "hooks_with_data": hooks_with_data} @@ -796,7 +802,7 @@ def make_context_index(request: Request): Returns: dict: The context for the index page. """ - hooks: list[dict] = list(reader.get_tag((), "webhooks", [])) # type: ignore + hooks: list[dict[str, str]] = cast("list[dict[str, str]]", list(reader.get_tag((), "webhooks", []))) feed_list = [] broken_feeds = [] @@ -911,7 +917,7 @@ def modify_webhook(old_hook: Annotated[str, Form()], new_hook: Annotated[str, Fo # Webhooks are stored as a list of dictionaries. # Example: [{"name": "webhook_name", "url": "webhook_url"}] - webhooks = cast(list[dict[str, str]], webhooks) + webhooks = cast("list[dict[str, str]]", webhooks) for hook in webhooks: if hook["url"] in old_hook.strip(): @@ -922,7 +928,7 @@ def modify_webhook(old_hook: Annotated[str, Form()], new_hook: Annotated[str, Fo raise HTTPException(status_code=500, detail="Webhook could not be modified") # Add our new list of webhooks to the database. - reader.set_tag((), "webhooks", webhooks) # type: ignore + reader.set_tag((), "webhooks", webhooks) # pyright: ignore[reportArgumentType] # Loop through all feeds and update the webhook if it # matches the old one. @@ -934,7 +940,7 @@ def modify_webhook(old_hook: Annotated[str, Form()], new_hook: Annotated[str, Fo continue if webhook == old_hook.strip(): - reader.set_tag(feed.url, "webhook", new_hook.strip()) # type: ignore + reader.set_tag(feed.url, "webhook", new_hook.strip()) # pyright: ignore[reportArgumentType] # Redirect to the webhook page. return RedirectResponse(url="/webhooks", status_code=303) diff --git a/discord_rss_bot/missing_tags.py b/discord_rss_bot/missing_tags.py index 4939ec9..84f375e 100644 --- a/discord_rss_bot/missing_tags.py +++ b/discord_rss_bot/missing_tags.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from reader import Feed, Reader, TagNotFoundError from discord_rss_bot.settings import default_custom_embed, default_custom_message @@ -13,8 +15,8 @@ def add_custom_message(reader: Reader, feed: Feed) -> None: try: reader.get_tag(feed, "custom_message") except TagNotFoundError: - reader.set_tag(feed.url, "custom_message", default_custom_message) # type: ignore - reader.set_tag(feed.url, "has_custom_message", True) # type: ignore + reader.set_tag(feed.url, "custom_message", default_custom_message) # pyright: ignore[reportArgumentType] + reader.set_tag(feed.url, "has_custom_message", True) # pyright: ignore[reportArgumentType] def add_has_custom_message(reader: Reader, feed: Feed) -> None: @@ -28,9 +30,9 @@ def add_has_custom_message(reader: Reader, feed: Feed) -> None: reader.get_tag(feed, "has_custom_message") except TagNotFoundError: if reader.get_tag(feed, "custom_message") == default_custom_message: - reader.set_tag(feed.url, "has_custom_message", False) # type: ignore + reader.set_tag(feed.url, "has_custom_message", False) # pyright: ignore[reportArgumentType] else: - reader.set_tag(feed.url, "has_custom_message", True) # type: ignore + reader.set_tag(feed.url, "has_custom_message", True) # pyright: ignore[reportArgumentType] def add_if_embed(reader: Reader, feed: Feed) -> None: @@ -43,7 +45,7 @@ def add_if_embed(reader: Reader, feed: Feed) -> None: try: reader.get_tag(feed, "if_embed") except TagNotFoundError: - reader.set_tag(feed.url, "if_embed", True) # type: ignore + reader.set_tag(feed.url, "if_embed", True) # pyright: ignore[reportArgumentType] def add_custom_embed(reader: Reader, feed: Feed) -> None: @@ -56,8 +58,8 @@ def add_custom_embed(reader: Reader, feed: Feed) -> None: try: reader.get_tag(feed, "embed") except TagNotFoundError: - reader.set_tag(feed.url, "embed", default_custom_embed) # type: ignore - reader.set_tag(feed.url, "has_custom_embed", True) # type: ignore + reader.set_tag(feed.url, "embed", default_custom_embed) # pyright: ignore[reportArgumentType] + reader.set_tag(feed.url, "has_custom_embed", True) # pyright: ignore[reportArgumentType] def add_has_custom_embed(reader: Reader, feed: Feed) -> None: @@ -71,9 +73,9 @@ def add_has_custom_embed(reader: Reader, feed: Feed) -> None: reader.get_tag(feed, "has_custom_embed") except TagNotFoundError: if reader.get_tag(feed, "embed") == default_custom_embed: - reader.set_tag(feed.url, "has_custom_embed", False) # type: ignore + reader.set_tag(feed.url, "has_custom_embed", False) # pyright: ignore[reportArgumentType] else: - reader.set_tag(feed.url, "has_custom_embed", True) # type: ignore + reader.set_tag(feed.url, "has_custom_embed", True) # pyright: ignore[reportArgumentType] def add_should_send_embed(reader: Reader, feed: Feed) -> None: @@ -86,7 +88,7 @@ def add_should_send_embed(reader: Reader, feed: Feed) -> None: try: reader.get_tag(feed, "should_send_embed") except TagNotFoundError: - reader.set_tag(feed.url, "should_send_embed", True) # type: ignore + reader.set_tag(feed.url, "should_send_embed", True) # pyright: ignore[reportArgumentType] def add_missing_tags(reader: Reader) -> None: diff --git a/tests/test_blacklist.py b/tests/test_blacklist.py index 4639240..4f5a317 100644 --- a/tests/test_blacklist.py +++ b/tests/test_blacklist.py @@ -45,7 +45,7 @@ def test_has_black_tags() -> None: def check_if_has_tag(reader: Reader, feed: Feed, blacklist_name: str) -> None: - reader.set_tag(feed, blacklist_name, "a") # type: ignore + reader.set_tag(feed, blacklist_name, "a") # pyright: ignore[reportArgumentType] assert_msg: str = f"Feed should have blacklist tags: {blacklist_name}" assert feed_has_blacklist_tags(custom_reader=reader, feed=feed) is True, assert_msg @@ -74,42 +74,42 @@ def test_should_be_skipped() -> None: # Test entry without any blacklists assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_title", "fvnnnfnfdnfdnfd") # type: ignore + reader.set_tag(feed, "blacklist_title", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is True, f"Entry should be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_title") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_title", "åäö") # type: ignore + reader.set_tag(feed, "blacklist_title", "åäö") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_title") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_summary", "ffdnfdnfdnfdnfdndfn") # type: ignore + reader.set_tag(feed, "blacklist_summary", "ffdnfdnfdnfdnfdndfn") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is True, f"Entry should be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_summary") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_summary", "åäö") # type: ignore + reader.set_tag(feed, "blacklist_summary", "åäö") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_summary") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_content", "ffdnfdnfdnfdnfdndfn") # type: ignore + reader.set_tag(feed, "blacklist_content", "ffdnfdnfdnfdnfdndfn") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is True, f"Entry should be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_content") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_content", "åäö") # type: ignore + reader.set_tag(feed, "blacklist_content", "åäö") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_content") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_author", "TheLovinator") # type: ignore + reader.set_tag(feed, "blacklist_author", "TheLovinator") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is True, f"Entry should be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_author") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" - reader.set_tag(feed, "blacklist_author", "åäö") # type: ignore + reader.set_tag(feed, "blacklist_author", "åäö") # pyright: ignore[reportArgumentType] assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" reader.delete_tag(feed, "blacklist_author") assert entry_should_be_skipped(reader, first_entry[0]) is False, f"Entry should not be skipped: {first_entry[0]}" diff --git a/tests/test_custom_filter.py b/tests/test_custom_filter.py index dd1fda1..94ce18e 100644 --- a/tests/test_custom_filter.py +++ b/tests/test_custom_filter.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pathlib import tempfile from pathlib import Path @@ -48,7 +50,7 @@ def test_entry_is_whitelisted() -> None: custom_reader.update_feed("https://lovinator.space/rss_test.xml") # whitelist_title - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_title", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_title", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_whitelisted(entry) is True: assert entry.title == "fvnnnfnfdnfdnfd", f"Expected: fvnnnfnfdnfdnfd, Got: {entry.title}" @@ -56,7 +58,7 @@ def test_entry_is_whitelisted() -> None: custom_reader.delete_tag("https://lovinator.space/rss_test.xml", "whitelist_title") # whitelist_summary - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_summary", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_summary", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_whitelisted(entry) is True: assert entry.summary == "fvnnnfnfdnfdnfd", f"Expected: fvnnnfnfdnfdnfd, Got: {entry.summary}" @@ -64,7 +66,7 @@ def test_entry_is_whitelisted() -> None: custom_reader.delete_tag("https://lovinator.space/rss_test.xml", "whitelist_summary") # whitelist_content - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_content", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "whitelist_content", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_whitelisted(entry) is True: assert_msg = f"Expected:

ffdnfdnfdnfdnfdndfn

, Got: {entry.content[0].value}" @@ -90,7 +92,7 @@ def test_entry_is_blacklisted() -> None: custom_reader.update_feed("https://lovinator.space/rss_test.xml") # blacklist_title - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_title", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_title", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_blacklisted(entry) is True: assert entry.title == "fvnnnfnfdnfdnfd", f"Expected: fvnnnfnfdnfdnfd, Got: {entry.title}" @@ -98,7 +100,7 @@ def test_entry_is_blacklisted() -> None: custom_reader.delete_tag("https://lovinator.space/rss_test.xml", "blacklist_title") # blacklist_summary - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_summary", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_summary", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_blacklisted(entry) is True: assert entry.summary == "fvnnnfnfdnfdnfd", f"Expected: fvnnnfnfdnfdnfd, Got: {entry.summary}" @@ -106,7 +108,7 @@ def test_entry_is_blacklisted() -> None: custom_reader.delete_tag("https://lovinator.space/rss_test.xml", "blacklist_summary") # blacklist_content - custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_content", "fvnnnfnfdnfdnfd") # type: ignore + custom_reader.set_tag("https://lovinator.space/rss_test.xml", "blacklist_content", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] for entry in custom_reader.get_entries(): if entry_is_blacklisted(entry) is True: assert_msg = f"Expected:

ffdnfdnfdnfdnfdndfn

, Got: {entry.content[0].value}" diff --git a/tests/test_feeds.py b/tests/test_feeds.py index 09fe719..e6e1381 100644 --- a/tests/test_feeds.py +++ b/tests/test_feeds.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import LiteralString import pytest -from reader import Feed, Reader, make_reader # type: ignore +from reader import Feed, Reader, make_reader from discord_rss_bot.feeds import send_to_discord, truncate_webhook_message from discord_rss_bot.missing_tags import add_missing_tags @@ -45,7 +45,7 @@ def test_send_to_discord() -> None: assert webhook_url is not None, f"The webhook URL should not be None. Got: {webhook_url}" # Add tag to the feed and check if it is there. - reader.set_tag(feed, "webhook", webhook_url) # type: ignore + reader.set_tag(feed, "webhook", webhook_url) # pyright: ignore[reportArgumentType] assert reader.get_tag(feed, "webhook") == webhook_url, f"The webhook URL should be '{webhook_url}'." # Send the feed to Discord. diff --git a/tests/test_main.py b/tests/test_main.py index 27eb214..59bd109 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import urllib.parse from typing import TYPE_CHECKING diff --git a/tests/test_search.py b/tests/test_search.py index 18a8f15..7518963 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import tempfile from pathlib import Path from typing import TYPE_CHECKING diff --git a/tests/test_settings.py b/tests/test_settings.py index e49a0b7..dd5b44e 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pathlib import tempfile from pathlib import Path @@ -52,8 +54,8 @@ def test_get_webhook_for_entry() -> None: custom_reader.update_feed("https://www.reddit.com/r/movies.rss") # Add a webhook to the database. - custom_reader.set_tag("https://www.reddit.com/r/movies.rss", "webhook", "https://example.com") # type: ignore - our_tag: str = custom_reader.get_tag("https://www.reddit.com/r/movies.rss", "webhook") # type: ignore + custom_reader.set_tag("https://www.reddit.com/r/movies.rss", "webhook", "https://example.com") # pyright: ignore[reportArgumentType] + our_tag = custom_reader.get_tag("https://www.reddit.com/r/movies.rss", "webhook") # pyright: ignore[reportArgumentType] assert our_tag == "https://example.com", f"The tag should be 'https://example.com'. But it was '{our_tag}'." # Close the reader, so we can delete the directory. diff --git a/tests/test_utils.py b/tests/test_utils.py index 4c5d2ca..0bccb6b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from discord_rss_bot.filter.utils import is_word_in_text diff --git a/tests/test_whitelist.py b/tests/test_whitelist.py index 47ecc46..cf39aa0 100644 --- a/tests/test_whitelist.py +++ b/tests/test_whitelist.py @@ -44,7 +44,7 @@ def test_has_white_tags() -> None: def check_if_has_tag(reader: Reader, feed: Feed, whitelist_name: str) -> None: - reader.set_tag(feed, whitelist_name, "a") # type: ignore + reader.set_tag(feed, whitelist_name, "a") # pyright: ignore[reportArgumentType] assert has_white_tags(custom_reader=reader, feed=feed) is True, "Feed should have whitelist tags" reader.delete_tag(feed, whitelist_name) assert has_white_tags(custom_reader=reader, feed=feed) is False, "Feed should not have any whitelist tags" @@ -70,42 +70,42 @@ def test_should_be_sent() -> None: # Test entry without any whitelists assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_title", "fvnnnfnfdnfdnfd") # type: ignore + reader.set_tag(feed, "whitelist_title", "fvnnnfnfdnfdnfd") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is True, "Entry should be sent" reader.delete_tag(feed, "whitelist_title") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_title", "åäö") # type: ignore + reader.set_tag(feed, "whitelist_title", "åäö") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" reader.delete_tag(feed, "whitelist_title") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_summary", "ffdnfdnfdnfdnfdndfn") # type: ignore + reader.set_tag(feed, "whitelist_summary", "ffdnfdnfdnfdnfdndfn") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is True, "Entry should be sent" reader.delete_tag(feed, "whitelist_summary") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_summary", "åäö") # type: ignore + reader.set_tag(feed, "whitelist_summary", "åäö") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" reader.delete_tag(feed, "whitelist_summary") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_content", "ffdnfdnfdnfdnfdndfn") # type: ignore + reader.set_tag(feed, "whitelist_content", "ffdnfdnfdnfdnfdndfn") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is True, "Entry should be sent" reader.delete_tag(feed, "whitelist_content") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_content", "åäö") # type: ignore + reader.set_tag(feed, "whitelist_content", "åäö") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" reader.delete_tag(feed, "whitelist_content") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_author", "TheLovinator") # type: ignore + reader.set_tag(feed, "whitelist_author", "TheLovinator") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is True, "Entry should be sent" reader.delete_tag(feed, "whitelist_author") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" - reader.set_tag(feed, "whitelist_author", "åäö") # type: ignore + reader.set_tag(feed, "whitelist_author", "åäö") # pyright: ignore[reportArgumentType] assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent" reader.delete_tag(feed, "whitelist_author") assert should_be_sent(reader, first_entry[0]) is False, "Entry should not be sent"