diff --git a/notifeed/cli.py b/notifeed/cli.py index a5a085f..635f16f 100644 --- a/notifeed/cli.py +++ b/notifeed/cli.py @@ -40,7 +40,12 @@ def cli(debug, db_path): log.setLevel(logging.DEBUG) formatter = logging.Formatter("%(levelname)s: %(message)s") sh.setFormatter(formatter) - db.init(db_path if db_path is not None else DEFAULT_DB_PATH) + if db_path is None: + DEFAULT_DB_PATH.parent.mkdir(parents=True, exist_ok=True) + path = DEFAULT_DB_PATH + else: + path = db_path + db.init(path) Database.seed() db.close() diff --git a/notifeed/constants.py b/notifeed/constants.py index c5c4cef..3c60844 100644 --- a/notifeed/constants.py +++ b/notifeed/constants.py @@ -7,9 +7,10 @@ # 3rd party import aiohttp +import appdirs # }}} BROTLI_SUPPORTED = find_spec("brotli") is not None -DEFAULT_DB_PATH = pathlib.Path(__file__).resolve().parent.parent / "notifeed.db" +DEFAULT_DB_PATH = pathlib.Path(appdirs.user_config_dir("notifeed")) / "notifeed.db" diff --git a/notifeed/db.py b/notifeed/db.py index dd40b2c..52e7076 100644 --- a/notifeed/db.py +++ b/notifeed/db.py @@ -20,7 +20,7 @@ ) # local modules -from notifeed.feeds import RemoteFeedAsync +from notifeed.feeds import RemoteFeedAsync, RemotePost from notifeed.notifications import NotificationChannel, NotificationChannelAsync # }}} @@ -85,31 +85,45 @@ def as_obj(self, session: aiohttp.ClientSession): async def check_latest_post(self, session: aiohttp.ClientSession): feed = self.as_obj(session) await feed.load() - latest = feed.posts[0] - - latest_hash = hashlib.sha256(latest.content.encode()).hexdigest() - latest_stored = next(self.posts, None) - - if latest_stored is None: - ... # no post previously saved (aka, a new DB, or the feed had no posts previously) - else: - if latest_stored.url == latest.id: - hashes_match = latest_stored.content_hash == latest_hash - if hashes_match: - return None # nothing new - else: - ... # latest post was updated since we last saw it - else: - ... # new post - - Post.replace( - id=latest.id, - url=latest.url, - title=latest.title, - content_hash=latest_hash, - feed=feed.url, - ) - return latest + fetched = feed.posts[0] + + stored: Optional[Post] = next(iter(self.posts), None) + + def save_post(post: RemotePost): + return Post.create( + id=post.id, + url=post.url, + title=post.title, + content_hash=post.content_hash, + feed=post.feed.url, + ) + + if stored is not None: + if fetched.id == stored.id: + hashes_match = fetched.content_hash == stored.content_hash + if hashes_match: # nothing new + log.debug(f"Hash for {repr(fetched.title)} matches stored hash (post is unchanged).") + return None + else: # latest post was updated since we last saw it + log.debug(f"Latest post has been updated (content hash changed)") + changes = { + 'url': fetched.url, + 'title': fetched.title, + 'content_hash': fetched.content_hash, + } + Post.update(changes).where(Post.id == stored.id).execute() + return None + else: # new post + log.debug(f"The latest post has a different ID than the stored post.") + save_post(fetched) + Post.delete().where(Post.id == stored.id).execute() + else: # no post previously saved (aka, a new DB, or the feed had no posts previously) + log.debug(f"No saved post was found.") + save_post(fetched) + + return fetched + + return None class Post(Database): diff --git a/notifeed/feeds.py b/notifeed/feeds.py index 320d608..a8c7df6 100644 --- a/notifeed/feeds.py +++ b/notifeed/feeds.py @@ -3,6 +3,7 @@ # Imports {{{ # builtins import aiohttp +import hashlib import textwrap from typing import Union import operator @@ -210,3 +211,7 @@ def images(self): def __repr__(self): return f"{self.__class__.__name__}({repr(self.title)})" + + @property + def content_hash(self): + return hashlib.sha256(self.content.encode()).hexdigest() diff --git a/setup.cfg b/setup.cfg index 88c4882..02771d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = notifeed -version = 1.1.0 +version = 1.2.0 author = Logan Swartzendruber author_email = logan.swartzendruber@gmail.com description = Automatically get notifications for new posts on your favorite RSS/Atom feeds. @@ -22,6 +22,7 @@ packages = find: python_requires = >=3.8 install_requires = aiohttp[speedups] + appdirs atoma bs4 click