From 54796470b1ba51cb9e29195815d6d53b8d2cc7a5 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sun, 23 Jun 2019 16:03:15 +0100 Subject: [PATCH] First version of rss.py --- README.md | 2 +- modules/rss.py | 118 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 modules/rss.py diff --git a/README.md b/README.md index d8200419..a30d7a91 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Python3 event-driven modular IRC bot! ## Requirements -Python3.6+ and [BeautifulSoup4](https://pypi.python.org/pypi/beautifulsoup4), [lxml](https://pypi.org/project/lxml/), [netifaces](https://pypi.org/project/netifaces/), [requests](https://pypi.org/project/requests/), [scrypt](https://pypi.python.org/pypi/scrypt), [suds](https://pypi.python.org/pypi/suds-jurko) and [tweepy](https://pypi.org/project/tweepy/). Use `pip3 install -r requirements.txt` to install them all at once. +Python3.6+ and [BeautifulSoup4](https://pypi.python.org/pypi/beautifulsoup4), [feedparser](https://pypi.org/project/feedparser/), [lxml](https://pypi.org/project/lxml/), [netifaces](https://pypi.org/project/netifaces/), [requests](https://pypi.org/project/requests/), [scrypt](https://pypi.python.org/pypi/scrypt), [suds](https://pypi.python.org/pypi/suds-jurko) and [tweepy](https://pypi.org/project/tweepy/). Use `pip3 install -r requirements.txt` to install them all at once. ## Setup See [docs/help/setup.md](docs/help/setup.md). diff --git a/modules/rss.py b/modules/rss.py new file mode 100644 index 00000000..5cef8a72 --- /dev/null +++ b/modules/rss.py @@ -0,0 +1,118 @@ +from src import ModuleManager, utils +import feedparser + +RSS_INTERVAL = 60 # 1 minute + +def _format_entry(feed_title, entry): + title = entry["title"] + + author = entry.get("author", None) + author = " by %s" % author if author else "" + + link = entry.get("link", None) + link = " - %s" % link if link else "" + + return "%s: %s%s%s" % (feed_title, title, author, link) + +class Module(ModuleManager.BaseModule): + _title = "RSS" + def on_load(self): + self.timers.add("rss", RSS_INTERVAL) + + @utils.hook("timer.rss") + def timer(self, event): + event["timer"].redo() + hook_settings = self.bot.database.channel_settings.find_by_setting( + "rss-hooks") + hooks = {} + for server_id, channel_name, urls in hook_settings: + server = self.bot.get_server_by_id(server_id) + if server and channel_name in server.channels: + channel = server.channels.get(channel_name) + for url in urls: + if not url in hooks: + hooks[url] = [] + hooks[url].append(channel) + + for url, channels in hooks.items(): + try: + feed = feedparser.parse(url) + except: + feed = None + + if not feed or not feed["feed"]: + self.log.warn("Failed to parse rss for %s", [url], + exc_info=True) + continue + + feed_title = feed["feed"]["title"] + entry_formatted = {} + + for channel in channels: + seen_ids = channel.get_setting("rss-seen-ids", []) + new_ids = [] + valid = 1 + for entry in feed["entries"]: + if entry["id"] in seen_ids: + new_ids.append(entry["id"]) + continue + + if valid == 4: + continue + valid += 1 + + if not entry["id"] in entry_formatted: + output = _format_entry(feed_title, entry) + entry_formatted[entry["id"]] = output + else: + output = entry_formatted[entry["id"]] + + self.events.on("send.stdout").call(target=channel, + module_name="RSS", server=server, message=output) + new_ids.append(entry["id"]) + + channel.set_setting("rss-seen-ids", new_ids) + + @utils.hook("received.command.rss", min_args=1, channel_only=True) + def rss(self, event): + """ + :help: Modify RSS/Atom configuration for the current channel + :usage: list + :usage: add + :usage: remove + :permission: rss + """ + changed = False + message = None + + rss_hooks = event["target"].get_setting("rss-hooks", []) + + subcommand = event["args_split"][0].lower() + if subcommand == "list": + event["stdout"].write("RSS hooks: %s" % ", ".join(rss_hooks)) + elif subcommand == "add": + if not len(event["args_split"]) > 1: + raise utils.EventError("Please provide a URL") + + url = event["args_split"][1] + if url in rss_hooks: + raise utils.EventError("That URL is already being watched") + rss_hooks.append(url) + changed = True + message = "Added RSS feed" + elif subcommand == "remove": + if not len(event["args_split"]) > 1: + raise utils.EventError("Please provide a URL") + + url = event["args_split"][1] + if not url in rss_hooks: + raise utils.EventError("I'm not watching that URL") + rss_hooks.remove(url) + changed = True + message = "Removed RSS feed" + else: + raise utils.EventError("Unknown subcommand '%s'" % subcommand) + + if changed: + event["target"].set_setting("rss-hooks", rss_hooks) + event["stdout"].write(message) diff --git a/requirements.txt b/requirements.txt index 904f66ed..35153a7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ beautifulsoup4 +feedparser lxml netifaces requests