From 8421e956a72d3fc7ea6840d445a743e0a26a4502 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Wed, 27 Dec 2023 09:44:07 +0000 Subject: [PATCH 1/2] Write unittests --- .vscode/settings.json | 11 +++++++++ src/bot.py | 5 +++- src/extensions/tasks.py | 17 +++++++++---- src/logs.py | 2 +- src/main.py | 6 +++-- src/tests.py | 53 +++++++++++++++++++++++++++++++++++------ 6 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1d89707 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.testing.unittestArgs": [ + "-v", + "-s", + "./src", + "-p", + "test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/src/bot.py b/src/bot.py index 079eda0..e7ff73e 100644 --- a/src/bot.py +++ b/src/bot.py @@ -17,10 +17,13 @@ log = logging.getLogger(__name__) class DiscordBot(commands.Bot): - def __init__(self, BASE_DIR: Path): + def __init__(self, BASE_DIR: Path, developing: bool): super().__init__(command_prefix="-", intents=Intents.all()) self.functions = Functions(self) self.BASE_DIR = BASE_DIR + self.developing = developing + + log.info("developing=%s", developing) async def sync_app_commands(self): """ diff --git a/src/extensions/tasks.py b/src/extensions/tasks.py index 5e25966..e56f2df 100644 --- a/src/extensions/tasks.py +++ b/src/extensions/tasks.py @@ -12,8 +12,8 @@ from discord import Interaction, TextChannel from discord.ext import commands, tasks from discord.errors import Forbidden -from feed import Source, Article # pylint disable=E0401 -from db import DatabaseManager, FeedChannelModel, RssSourceModel, SentArticleModel # pylint disable=E0401 +from feed import Source, Article +from db import DatabaseManager, FeedChannelModel, RssSourceModel, SentArticleModel log = logging.getLogger(__name__) @@ -30,12 +30,19 @@ class TaskCog(commands.Cog): @commands.Cog.listener() async def on_ready(self): - """Instructions to call when the cog is ready.""" + """Instructions to execute when the cog is ready.""" - self.rss_task.start() # pylint disable=E1101 + if not self.bot.developing: + self.rss_task.start() log.info("%s cog is ready", self.__class__.__name__) + @commands.Cog.listener(name="cog_unload") + async def on_unload(self): + """Instructions to execute before the cog is unloaded.""" + + self.rss_task.cancel() + @tasks.loop(minutes=10) async def rss_task(self): """Automated task responsible for processing rss feeds.""" @@ -68,6 +75,8 @@ class TaskCog(commands.Cog): channel = self.bot.get_channel(feed.discord_channel_id) + # TODO: integrate the `validate_feed` code into here, also do on list command and show errors. + unparsed_content = await self.bot.functions.get_unparsed_feed(feed.rss_source.rss_url) parsed_feed = parse(unparsed_content) source = Source.from_parsed(parsed_feed) diff --git a/src/logs.py b/src/logs.py index 6c83d30..575a334 100644 --- a/src/logs.py +++ b/src/logs.py @@ -84,7 +84,7 @@ class LogSetup: logging.basicConfig( level=log_level, handlers=(queue_handler,), - format='[%(asctime)s] [%(levelname)-8s] [%(name)-18s]: %(message)s' + format='[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s' ) # Create a new log file diff --git a/src/main.py b/src/main.py index 7117f6a..bfb6807 100644 --- a/src/main.py +++ b/src/main.py @@ -31,15 +31,17 @@ async def main(): if not token: raise ValueError("Token is empty") + developing = bool(getenv("DEVELOPING")) + # Setup logging settings and mute spammy loggers logsetup = LogSetup(BASE_DIR) - logsetup.setup_logs(logging.DEBUG) + logsetup.setup_logs(logging.DEBUG if developing else logging.INFO) logsetup.update_log_levels( ('discord', 'PIL', 'urllib3', 'aiosqlite', 'charset_normalizer'), level=logging.WARNING ) - async with DiscordBot(BASE_DIR) as bot: + async with DiscordBot(BASE_DIR, developing=developing) as bot: await bot.load_extensions() await bot.start(token, reconnect=True) diff --git a/src/tests.py b/src/tests.py index f46df5a..cc63568 100644 --- a/src/tests.py +++ b/src/tests.py @@ -1,15 +1,54 @@ +import unittest +from sqlalchemy import select +from sqlalchemy.engine.cursor import CursorResult +from sqlalchemy.engine.result import ChunkedIteratorResult + +from db import DatabaseManager, FeedChannelModel, AuditModel + +class TestDatabaseConnections(unittest.IsolatedAsyncioTestCase): + """The purpose of this test, is to ensure that the database connections function properly.""" -def test_article_embed(): - assert True, "" + async def test_select__feed_channel_model(self): + """This test runs a select query on the `FeedChannelModel`""" + + async with DatabaseManager() as database: + query = select(FeedChannelModel).limit(1000) + result = await database.session.execute(query) + self.assertIsInstance( + result, + ChunkedIteratorResult, + f"Result should be `ChunkedIteratorResult`, not {type(result)!r}" + ) -def main(): - - # test article embed - test_article_embed() + async def test_select__rss_source_model(self): + """This test runs a select query on the `RssSourceModel`""" + + async with DatabaseManager() as database: + query = select(RssSourceModel).limit(1000) + result = await database.session.execute(query) + + self.assertIsInstance( + result, + ChunkedIteratorResult, + f"Result should be `ChunkedIteratorResult`, not {type(result)!r}" + ) + + async def test_select__audit_model(self): + """This test runs a select query on the `AuditModel`""" + + async with DatabaseManager() as database: + query = select(AuditModel).limit(1000) + result = await database.session.execute(query) + + self.assertIsInstance( + result, + ChunkedIteratorResult, + f"Result should be `ChunkedIteratorResult`, not {type(result)!r}" + ) if __name__ == "__main__": - main() + unittest.main() From 8d02605019464b9cb099fd3e84a16a644772dd59 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 26 Jan 2024 17:52:33 +0000 Subject: [PATCH 2/2] Update README.md --- README.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bdd0618..07e5819 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,7 @@ -# NewsBot +# PYRSS -Bot delivering news articles to discord servers. +An RSS driven Discord bot written in Python. -Plans +Provides user commands for storing RSS feed URLs that can be assigned to any given discord channel. -- Multiple news providers -- Choose how much of each provider should be delivered -- Check for duplicate articles between providers, and only deliver preferred provider article - - -## Dev Notes: - -For the sake of development, the following defintions apply: - -- Feed - An RSS feed stored within the database, submitted by a user. -- Assigned Feed - A discord channel set to receive content from a Feed. \ No newline at end of file +Content is shared every 10 minutes as an Embed. \ No newline at end of file