From 9c81fb0c81691241d60e97786406d1e3202efa1e Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Sat, 25 Apr 2026 22:37:28 -0400 Subject: [PATCH 01/10] readme: update readme with info about recent changes --- README.md | 74 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 48148d4..772dc73 100644 --- a/README.md +++ b/README.md @@ -11,46 +11,23 @@ requests >= 2.4.2 feedparser ``` -The remaining imports should all be part of the standard Python install. +The remaining imports should all be part of the standard Python install: hashlib, logging, asyncio, pathlib, json, time, os, sys, argparse, re, types. To install the required ones, use your distro's package manager -- don't use pip unless you want to run the entire script in a virtualenv (which would probably make the systemd unit file a bit more complex, I think you'd have to add a PreExec to activate the virtualenv and a PostExec to disable it?). ## Important Notes -The logger will try and put the logs in `/var/log/discorss`. Make sure to create this directory and give the user running the script write permissions there. If you want the logs to go somewhere else, just edit the log_dir variable near the top of discorss.py. Choose a directory that makes sense. Unfortunately, as far as I know, the XDG standards don't have an equivalent to the /var/log directory in the user directory, so I wasn't sure what the best default was. In the future, we may switch to logging using systemd and journald directly, though it is nice to have a separate file. +By default, DiscoRSS will try and put the logs in `/var/log/discorss`. Make sure to create this directory and give the user running the script write permissions there. If you want the logs to go somewhere else, just give the directory as an argument (shown below). Choose a directory that makes sense. Unfortunately, as far as I know, the XDG standards don't have an equivalent to the /var/log directory in the user directory, so I wasn't sure what the best default was. In the future, we may switch to logging using systemd and journald directly, though it is nice to have a separate file. + +### Script Arguments + +The script has a few different arguments that make it easy to customize certain things: + +* `-d / --dry-run`: Just like it says on the tin -- run the script, pull feeds, but don't post anything to Discord +* `-c / --config-file`: Give a path to an alternate location for the config file. The default is ~/.config/discorss/discorss.conf and that should be fine for the vast majority of users. +* `-l / --log-file`: Give a path to where you want the log file stored. The default is /var/log/discorss/app.log but you will have to create the /var/log/discorss directory (or have install.sh do it for you) and make it writeable by whatever user will be running the script. ## How to setup -Note: see the Automation section below for info about using the `install.sh` script to help get all the files in the right places. - -### Config file format - -To configure the script, create `~/.config/discorss/discorss.conf` using JSON formatting like this: - -```json -{ - "feeds": [ - { - "name": "Phoronix", - "siteurl": "https://www.phoronix.com/", - "url": "http://www.phoronix.com/rss.php", - "webhook": "webhook url", - "offset": -18000 - }, - { - "name": "Pagetable", - "siteurl": "https://pagetable.com", - "url": "https://www.pagetable.com/?feed=rss2", - "webhook": "webhook url", - "offset": -18000 - } - ] -} -``` - -Create a webhook for each feed (unless you want them all to show as the same webhook for whatever reason) and make sure to add it in to the config. I have it set up with a webhook for each site, each with the site's icon and name set for the webhook which makes the messages look really nice. - -The offset should only be required if feeds aren't showing up. This is because feedparser, in its infinite wisdom, just ignores the timezone when converting publish dates from feeds. So most feeds end up with an epoch in UTC. The offset should be the number of seconds between your time zone and UTC. This will eventually be fixed in a future update, I just need to sit down and wrangle with feedparser and datetime some more. All fields are mandatory, if you want to have no offset for example, set it to 0. The name and siteurl are used to create the "author" field in the Discord embed. - -## Automation +### Automation **New**: There is now `install.sh` in the repo which will automatically help you set up both the config file and the systemd unit files for the service and timer, using essentially the exact text below. It will copy them to the user systemd unit folder, `~/.config/systemd/user` and optionally enable the timer. It's a good idea to edit the configuration file at `~/.config/discorss/discorss.conf` and paste in your webhook URLs and add any other feeds you want before starting the timer, unless you can do it really quickly before the next 5 minute spot on the clock :) Of course, if it fires with an invalid config, the script will just crash, and you'll probably just have to manually start the timer once the config is fixed, so not a big deal. @@ -94,6 +71,35 @@ WantedBy=timers.target To change how often this fires, edit the OnCalendar parameter. The config above has it firing every 15 minutes at half past the minute. Look at the systemd timer man pages for help if you want to tweak it. +### Config file format + +To configure the script, create `~/.config/discorss/discorss.conf` (or have install.sh create it for you) using JSON formatting like this: + +```json +{ + "feeds": [ + { + "name": "Phoronix", + "siteurl": "https://www.phoronix.com/", + "url": "http://www.phoronix.com/rss.php", + "webhook": "webhook url", + "offset": -18000 + }, + { + "name": "Pagetable", + "siteurl": "https://pagetable.com", + "url": "https://www.pagetable.com/?feed=rss2", + "webhook": "webhook url", + "offset": -18000 + } + ] +} +``` + +Create a webhook for each feed (unless you want them all to show as the same webhook for whatever reason) and make sure to add it in to the config. I have it set up with a webhook for each site, each with the site's icon and name set for the webhook which makes the messages look really nice. + +The offset should only be required if feeds from the previous 6 hours aren't showing up when you first start the script. This is because feedparser, in its infinite wisdom, just ignores the timezone when converting publish dates from feeds. So most feeds end up with an epoch in UTC. The offset should be the number of seconds between your time zone and UTC. This will eventually be fixed in a future update, I just need to sit down and wrangle with feedparser and datetime some more. All fields are mandatory, if you want to have no offset for example, set it to 0. The name and siteurl are used to create the "author" field in the Discord embed. + ## Contributing Want to fix something or make a suggestion? Feel free! If you want to send a pull request, you *must* run the Python `black` formatter on the source code before committing. I have this set up in my editor to automatically run every time I save the file, but you could have it run as part of a git hook or something. For non-format stuff, please just follow the code style as best you can. For Python code, I separate multi-word variable names with underscores. So it should be `feed_time`, not `feedTime` or `FeedTime` or `feed-time`. Don't ask me why, but I use camelCase for other languages... but in Python I've switched to underscores. From 4128e7808c1056f027e669d393851ab2f9e3b1fe Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Sat, 25 Apr 2026 22:40:17 -0400 Subject: [PATCH 02/10] readme: fix small error --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 772dc73..2f50663 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The remaining imports should all be part of the standard Python install: hashlib ## Important Notes -By default, DiscoRSS will try and put the logs in `/var/log/discorss`. Make sure to create this directory and give the user running the script write permissions there. If you want the logs to go somewhere else, just give the directory as an argument (shown below). Choose a directory that makes sense. Unfortunately, as far as I know, the XDG standards don't have an equivalent to the /var/log directory in the user directory, so I wasn't sure what the best default was. In the future, we may switch to logging using systemd and journald directly, though it is nice to have a separate file. +By default, DiscoRSS will try and put the logs in `/var/log/discorss`. Make sure to create this directory and give the user running the script write permissions there. If you want the logs to go somewhere else, just give the path as an argument (shown below). Choose a directory that makes sense. Unfortunately, as far as I know, the XDG standards don't have an equivalent to the /var/log directory in the user directory, so I wasn't sure what the best default was. In the future, we may switch to logging using systemd and journald directly, though it is nice to have a separate file. ### Script Arguments @@ -23,7 +23,7 @@ The script has a few different arguments that make it easy to customize certain * `-d / --dry-run`: Just like it says on the tin -- run the script, pull feeds, but don't post anything to Discord * `-c / --config-file`: Give a path to an alternate location for the config file. The default is ~/.config/discorss/discorss.conf and that should be fine for the vast majority of users. -* `-l / --log-file`: Give a path to where you want the log file stored. The default is /var/log/discorss/app.log but you will have to create the /var/log/discorss directory (or have install.sh do it for you) and make it writeable by whatever user will be running the script. +* `-l / --log-file`: Give a path to where you want the log file stored. The default is /var/log/discorss/app.log but you will have to create the /var/log/discorss directory and make it writeable by whatever user will be running the script. ## How to setup From a34cc1ac8e46735c81f6a09762dc3f08aa4380f7 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Sat, 25 Apr 2026 22:47:37 -0400 Subject: [PATCH 03/10] readme: add demo screenshot --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2f50663..81256c7 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ feedparser The remaining imports should all be part of the standard Python install: hashlib, logging, asyncio, pathlib, json, time, os, sys, argparse, re, types. To install the required ones, use your distro's package manager -- don't use pip unless you want to run the entire script in a virtualenv (which would probably make the systemd unit file a bit more complex, I think you'd have to add a PreExec to activate the virtualenv and a PostExec to disable it?). +

+![DiscoRSS Demo Screenshot](https://frzn.dev/~amr/images/Screenshot_224228_1.png) +

+ ## Important Notes By default, DiscoRSS will try and put the logs in `/var/log/discorss`. Make sure to create this directory and give the user running the script write permissions there. If you want the logs to go somewhere else, just give the path as an argument (shown below). Choose a directory that makes sense. Unfortunately, as far as I know, the XDG standards don't have an equivalent to the /var/log directory in the user directory, so I wasn't sure what the best default was. In the future, we may switch to logging using systemd and journald directly, though it is nice to have a separate file. From e5a5f4f7d28bf42c214e9c1a6ca60e04b40624a3 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Sat, 25 Apr 2026 22:48:54 -0400 Subject: [PATCH 04/10] readme: fix URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81256c7..8faf1be 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ feedparser The remaining imports should all be part of the standard Python install: hashlib, logging, asyncio, pathlib, json, time, os, sys, argparse, re, types. To install the required ones, use your distro's package manager -- don't use pip unless you want to run the entire script in a virtualenv (which would probably make the systemd unit file a bit more complex, I think you'd have to add a PreExec to activate the virtualenv and a PostExec to disable it?).

-![DiscoRSS Demo Screenshot](https://frzn.dev/~amr/images/Screenshot_224228_1.png) +

## Important Notes From 0e8dba9d6dc5c4deeef1fa12e2d9a22732b11518 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Mon, 4 May 2026 11:48:29 -0400 Subject: [PATCH 05/10] chore: fix discord webhook call to include APP_VERSION --- discorss.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discorss.py b/discorss.py index fc15bb8..bf431b9 100755 --- a/discorss.py +++ b/discorss.py @@ -168,7 +168,9 @@ class Discorss: "attachments": [], } custom_header = { - "user-agent": "DiscoRSS (https://git.frzn.dev/amr/discorss, 0.2)", + "user-agent": "DiscoRSS (https://git.frzn.dev/amr/discorss, {})".format( + self.APP_VERSION + ), "content-type": "application/json", } webhook_string = json.dumps(webhook) From acb03cff271cfd434469d382eadb1e71377c0a46 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Tue, 9 Jun 2026 18:10:22 -0400 Subject: [PATCH 06/10] dev: attempting to add image thumbnail support --- discorss.py | 97 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/discorss.py b/discorss.py index bf431b9..49a7cfd 100755 --- a/discorss.py +++ b/discorss.py @@ -28,7 +28,8 @@ from types import SimpleNamespace class Discorss: FEED_TIMEOUT_SECONDS = 15 HASH_HISTORY_LIMIT = 10 - APP_VERSION = "0.3rc1" + APP_VERSION = "0.3rc2" + IMAGE_EXTENSIONS = (".jpg", ".jpeg", ".png", ".gif", ".webp") def __init__(self, args=None): if args is None: @@ -54,6 +55,7 @@ class Discorss: # Yes, I know you "can't parse HTML with regex", but # just watch me. self.html_filter = re.compile(r"\<\/?([A-Za-z0-9 \:\.\-\/\"\=])*\>") + self.img_src_filter = re.compile(r']+src=["\']([^"\']+)["\']', re.I) self.success_codes = [200, 201, 202, 203, 204, 205, 206] self.app_config = {} @@ -142,29 +144,32 @@ class Discorss: last_check, self.now, ) - webhook = { - "embeds": [ + embed = { + "title": str(latest_post["title"]), + "url": str(latest_post["link"]), + "color": 2123412, + "footer": { + "text": "DiscoRSS", + "icon_url": "https://frzn.dev/~amr/images/discorss.png", + }, + "author": { + "name": str(hook["name"]), + "url": str(hook["siteurl"]), + }, + "fields": [ { - "title": str(latest_post["title"]), - "url": str(latest_post["link"]), - "color": 2123412, - "footer": { - "text": "DiscoRSS", - "icon_url": "https://frzn.dev/~amr/images/discorss.png", - }, - "author": { - "name": str(hook["name"]), - "url": str(hook["siteurl"]), - }, - "fields": [ - { - "name": "Excerpt from post:", - "value": self.get_description(latest_post), - } - ], - # "timestamp": str(self.now), + "name": "Excerpt from post:", + "value": self.get_description(latest_post), } ], + # "timestamp": str(self.now), + } + image_url = self.get_image_url(latest_post) + if image_url is not None: + embed["thumbnail"] = {"url": image_url} + + webhook = { + "embeds": [embed], "attachments": [], } custom_header = { @@ -264,6 +269,56 @@ class Discorss: desc = desc + str(addons) return desc + # attempting to extract image previews from feeds which primarily feature + # images, like NASA's Picture of the Day feed + def get_image_url(self, feed): + image_candidates = [] + # check the most common fields, this should catch the majority of image + # feeds' embedded urls + for media in feed.get("media_content", []): + if self.is_image_url(media.get("url"), media.get("type")): + image_candidates.append(media["url"]) + + for enclosure in feed.get("enclosures", []): + if self.is_image_url(enclosure.get("href"), enclosure.get("type")): + image_candidates.append(enclosure["href"]) + + for link in feed.get("links", []): + if self.is_image_url(link.get("href"), link.get("type")): + image_candidates.append(link["href"]) + + for media in feed.get("media_thumbnail", []): + if self.is_image_url(media.get("url"), media.get("type")): + image_candidates.append(media["url"]) + + for field in ["summary_detail", "content"]: + value = feed.get(field) + if isinstance(value, list): + values = [item.get("value", "") for item in value] + elif isinstance(value, dict): + values = [value.get("value", "")] + else: + values = [] + for text in values: + match = self.img_src_filter.search(str(text)) + if match and self.is_image_url(match.group(1)): + image_candidates.append(match.group(1)) + + if len(image_candidates) > 0: + return image_candidates[0] + return None + + # a silly little helper just to validate image links + # this isn't 100% foolproof but it should work for the + # vast majority of feeds out there, unless they use some + # really weird image type like image/bpg + def is_image_url(self, url, mime_type=None): + if not url: + return False + if mime_type and str(mime_type).lower().startswith("image/"): + return True + return str(url).lower().split("?", 1)[0].endswith(self.IMAGE_EXTENSIONS) + # Some of this could go in __init__ def setup(self): os.environ["TZ"] = "America/Toronto" From 91c39042c80021a3ce03e8f870dd52dc11d2e9b8 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Tue, 9 Jun 2026 18:36:31 -0400 Subject: [PATCH 07/10] logging: added some more logging statements --- discorss.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/discorss.py b/discorss.py index 49a7cfd..8513a97 100755 --- a/discorss.py +++ b/discorss.py @@ -164,6 +164,11 @@ class Discorss: ], # "timestamp": str(self.now), } + self.logger.debug( + "Checking for images in post %s from %s...", + latest_post["title"], + hook["name"], + ) image_url = self.get_image_url(latest_post) if image_url is not None: embed["thumbnail"] = {"url": image_url} @@ -303,7 +308,9 @@ class Discorss: match = self.img_src_filter.search(str(text)) if match and self.is_image_url(match.group(1)): image_candidates.append(match.group(1)) - + self.logger.debug("Found the following image candidates in %s...", feed["name"]) + for i in image_candidates: + self.logger.debug("%s", i) if len(image_candidates) > 0: return image_candidates[0] return None @@ -329,7 +336,7 @@ class Discorss: logging.basicConfig( filename=self.log_file_path, encoding="utf-8", - level=logging.WARNING, + level=logging.ERROR, datefmt="%m/%d/%Y %H:%M:%S", format="%(asctime)s [%(threadName)s] -> %(levelname)s: %(message)s", ) From 84c0b40edf963d8df0bdf7300704cc9ed68b111c Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Thu, 11 Jun 2026 20:41:51 -0400 Subject: [PATCH 08/10] bugfix: corrected variable name causing errors, renamed some others --- discorss.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/discorss.py b/discorss.py index 8513a97..ea88c75 100755 --- a/discorss.py +++ b/discorss.py @@ -58,6 +58,7 @@ class Discorss: self.img_src_filter = re.compile(r']+src=["\']([^"\']+)["\']', re.I) self.success_codes = [200, 201, 202, 203, 204, 205, 206] self.app_config = {} + print(f"Logging to {self.log_file_path}") async def _fetch_feed(self, hook): response = await asyncio.to_thread( @@ -98,20 +99,20 @@ class Discorss: async def _process_feed(self, hook, last_check): self.logger.debug("Parsing feed %s...", hook["name"]) - feeds = await self._fetch_feed(hook) + feed = await self._fetch_feed(hook) latest_post = None prev_best = 0 bad_time = False self.logger.debug("About to sort through entries for feed %s ...", hook["name"]) - for feed in feeds["entries"]: + for post in feed["entries"]: try: - published_time = time.mktime(feed["published_parsed"]) + published_time = time.mktime(post["published_parsed"]) published_time = published_time + hook["offset"] except KeyError: - published_time = time.mktime(feed["updated_parsed"]) + published_time = time.mktime(post["updated_parsed"]) bad_time = True if published_time > prev_best: - latest_post = feed + latest_post = post prev_best = published_time if latest_post is None: @@ -251,9 +252,9 @@ class Discorss: # This function gets and formats the brief excerpt that goes in the embed # Different feeds put summaries in different fields, so we pick the best # one and limit it to 250 characters. - def get_description(self, feed, length=250, min_length=150, addons=None): + def get_description(self, post, length=250, min_length=150, addons=None): try: - temporary_string = str(feed["summary_detail"]["value"]) + temporary_string = str(post["summary_detail"]["value"]) temporary_string = self.html_filter.sub("", temporary_string) while length > min_length: if temporary_string[length - 1 : length] == " ": @@ -261,7 +262,7 @@ class Discorss: else: length -= 1 except KeyError: - temporary_string = str(feed["description"]) + temporary_string = str(post["description"]) temporary_string = self.html_filter.sub("", temporary_string) while length > min_length: if temporary_string[length - 1 : length] == " ": @@ -276,28 +277,28 @@ class Discorss: # attempting to extract image previews from feeds which primarily feature # images, like NASA's Picture of the Day feed - def get_image_url(self, feed): + def get_image_url(self, post): image_candidates = [] # check the most common fields, this should catch the majority of image # feeds' embedded urls - for media in feed.get("media_content", []): + for media in post.get("media_content", []): if self.is_image_url(media.get("url"), media.get("type")): image_candidates.append(media["url"]) - for enclosure in feed.get("enclosures", []): + for enclosure in post.get("enclosures", []): if self.is_image_url(enclosure.get("href"), enclosure.get("type")): image_candidates.append(enclosure["href"]) - for link in feed.get("links", []): + for link in post.get("links", []): if self.is_image_url(link.get("href"), link.get("type")): image_candidates.append(link["href"]) - for media in feed.get("media_thumbnail", []): + for media in post.get("media_thumbnail", []): if self.is_image_url(media.get("url"), media.get("type")): image_candidates.append(media["url"]) for field in ["summary_detail", "content"]: - value = feed.get(field) + value = post.get(field) if isinstance(value, list): values = [item.get("value", "") for item in value] elif isinstance(value, dict): @@ -308,7 +309,7 @@ class Discorss: match = self.img_src_filter.search(str(text)) if match and self.is_image_url(match.group(1)): image_candidates.append(match.group(1)) - self.logger.debug("Found the following image candidates in %s...", feed["name"]) + self.logger.debug("Found the following image candidates in %s...", post["title"]) for i in image_candidates: self.logger.debug("%s", i) if len(image_candidates) > 0: @@ -336,7 +337,7 @@ class Discorss: logging.basicConfig( filename=self.log_file_path, encoding="utf-8", - level=logging.ERROR, + level=logging.INFO, datefmt="%m/%d/%Y %H:%M:%S", format="%(asctime)s [%(threadName)s] -> %(levelname)s: %(message)s", ) From cd07e9806f0f8a5a28225ac505a3a617e67e96f5 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Fri, 12 Jun 2026 02:00:02 -0400 Subject: [PATCH 09/10] logging: add stop message, change default level --- discorss.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discorss.py b/discorss.py index ea88c75..be0ce2c 100755 --- a/discorss.py +++ b/discorss.py @@ -337,7 +337,7 @@ class Discorss: logging.basicConfig( filename=self.log_file_path, encoding="utf-8", - level=logging.INFO, + level=logging.DEBUG, datefmt="%m/%d/%Y %H:%M:%S", format="%(asctime)s [%(threadName)s] -> %(levelname)s: %(message)s", ) @@ -392,6 +392,8 @@ class Discorss: self.app_config["lastupdate"] = self.now with open(self.config_file_path, "w") as config_file: json.dump(self.app_config, config_file, indent=4) + + self.logger.info("Stopping DiscoRSS version {}...".format(self.APP_VERSION)) return From 64111aca1697e97a2716147c7aca67cb133b37a8 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Fri, 12 Jun 2026 13:36:06 -0400 Subject: [PATCH 10/10] chore: moved code to __init__, added some comments, small changes --- discorss.py | 87 +++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/discorss.py b/discorss.py index be0ce2c..2ac9284 100755 --- a/discorss.py +++ b/discorss.py @@ -60,6 +60,47 @@ class Discorss: self.app_config = {} print(f"Logging to {self.log_file_path}") + self.now = time.mktime(time.localtime()) + # Set up logging + self.logger = logging.getLogger(__name__) + logging.basicConfig( + filename=self.log_file_path, + encoding="utf-8", + level=logging.DEBUG, + datefmt="%m/%d/%Y %H:%M:%S", + format="%(asctime)s [%(threadName)s] -> %(levelname)s: %(message)s", + ) + # Check for log and config files/paths, create empty directories if needed + # TODO: change output to log file, as warning/error + if not Path(self.log_dir).exists(): + self.logger.warning( + "No log file path exists. Yark! We'll try and make %s...", self.log_dir + ) + try: + Path(self.log_dir).mkdir(parents=True, exist_ok=True) + except FileExistsError: + self.logger.critical( + "The path {} already exists and is not a directory!".format( + self.log_dir + ) + ) + if not Path(self.config_file_path).exists(): + self.logger.warning( + "No config file at {}! Snarf. We'll try and make {}...".format( + self.config_file_path, self.config_dir + ) + ) + try: + Path(self.config_dir).mkdir(parents=True, exist_ok=True) + except FileExistsError: + self.warning.critical( + "The config dir {} already exists and is not a directory! Please fix manually. Quitting!".format( + self.config_dir + ) + ) + sys.exit(255) + return + async def _fetch_feed(self, hook): response = await asyncio.to_thread( requests.get, @@ -325,52 +366,14 @@ class Discorss: return False if mime_type and str(mime_type).lower().startswith("image/"): return True + # this will fix urls with ? in them for parameters. this should work + # unless the server depends on the parameter to creat the image, but + # in that case we'll just hope it has a mime_type instead return str(url).lower().split("?", 1)[0].endswith(self.IMAGE_EXTENSIONS) - # Some of this could go in __init__ def setup(self): os.environ["TZ"] = "America/Toronto" time.tzset() - self.now = time.mktime(time.localtime()) - # Set up logging - self.logger = logging.getLogger(__name__) - logging.basicConfig( - filename=self.log_file_path, - encoding="utf-8", - level=logging.DEBUG, - datefmt="%m/%d/%Y %H:%M:%S", - format="%(asctime)s [%(threadName)s] -> %(levelname)s: %(message)s", - ) - # Check for log and config files/paths, create empty directories if needed - # TODO: change output to log file, as warning/error - if not Path(self.log_dir).exists(): - self.logger.warning( - "No log file path exists. Yark! We'll try and make %s...", self.log_dir - ) - try: - Path(self.log_dir).mkdir(parents=True, exist_ok=True) - except FileExistsError: - self.logger.critical( - "The path {} already exists and is not a directory!".format( - self.log_dir - ) - ) - if not Path(self.config_file_path).exists(): - self.logger.warning( - "No config file at {}! Snarf. We'll try and make {}...".format( - self.config_file_path, self.config_dir - ) - ) - try: - Path(self.config_dir).mkdir(parents=True, exist_ok=True) - except FileExistsError: - self.warning.critical( - "The config dir {} already exists and is not a directory! Please fix manually. Quitting!".format( - self.config_dir - ) - ) - sys.exit(255) - return # Loading the config file with open(self.config_file_path, "r") as config_file: self.app_config = json.load(config_file) @@ -402,7 +405,7 @@ class Discorss: def main(): parser = argparse.ArgumentParser( - description="DiscoRSS: publish feed updates to Discord webhooks." + description="\x1b[1;34mDisco\x1b[33mRSS\x1b[0m: publish feed updates to Discord webhooks." ) parser.add_argument( "-d",