From 809ffd05ec27cf747c35b153f11326ec2ebdafae Mon Sep 17 00:00:00 2001 From: "Casper V. Kristensen" Date: Sat, 25 Aug 2018 19:32:34 +0200 Subject: [PATCH] Add back reddit inbox listening mode; now configurable using the 'mode' config parameter. Add support for notifying reddit users of generation in 'immediately' mode. --- README.md | 2 +- dailyreleases/config.ini.default | 20 ++++++--- dailyreleases/main.py | 73 +++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2901c24..b56b71d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ pip3 install --upgrade https://git.caspervk.net/caspervk/dailyreleases/archive/m **It requires Python 3.6 or later.** ### Usage -The program can be started by running `dailyreleases` or `python3 -m dailyreleases` depending on system configuration. +The bot can be started by running `dailyreleases` or `python3 -m dailyreleases` depending on system configuration. ### Configuration The default configuration file will be copied to `~/.dailyreleases/config.ini` on the first run. All fields under the diff --git a/dailyreleases/config.ini.default b/dailyreleases/config.ini.default index 7b5697e..90b839c 100644 --- a/dailyreleases/config.ini.default +++ b/dailyreleases/config.ini.default @@ -1,7 +1,10 @@ [main] -# true : Listen for reddit PMs; on receipt, generate and submit post to own subreddit. Reply with link to post. -# false : Immediately generate and print post to log and console. Nothing is posted to reddit. -production = false + +# mode = +# immediately : Immediately generate and submit post to own subreddit. PM reddit notify_users with link to post. +# reply : Listen for reddit PMs; on receipt, generate and submit post to own subreddit. Reply with link to post. +# test : Generate and print to log and console. Nothing is posted to reddit. +mode = test [logging] @@ -19,10 +22,15 @@ user_agent = :: (by /u/) username = xxxxxxxxxxxxx password = xxxxxxx -# List of users who are allowed to PM the bot. +# List of users who are authorized to start the generation by PM'ing the bot. Only applies to 'reply' mode. authorized_users = spez,Deimorz,kn0thing -# Reddit perceives PMs with many links as spam, so the bot posts the code for the generated post in its own subreddit. +# List of users who should receive a PM on generation. Only applies to 'immediately' mode, since the sender will receive +# the PM in 'reply' mode. +notify_users = chooter,GallowBoob + +# Reddit perceives PMs with many links as spam, so the bot posts the code for the generated post in its own subreddit +# instead of sending it directly in the PM. bot_subreddit = dailyreleases # It also needs to know of the subreddit where the daily releases threads are posted so it can link to the previous one. @@ -42,5 +50,5 @@ key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx [web] # Number of seconds to cache web requests (google, steam etc.). May help reduce the number of requests if the same game -# has multiple scene releases on the same day. +# has multiple releases on the same day. cache_time = 600 \ No newline at end of file diff --git a/dailyreleases/main.py b/dailyreleases/main.py index 9aa8362..1847fee 100644 --- a/dailyreleases/main.py +++ b/dailyreleases/main.py @@ -1,3 +1,4 @@ +import inspect import json import logging import re @@ -7,6 +8,7 @@ import time from collections import defaultdict from datetime import datetime, timedelta +import prawcore import requests_cache from dailyreleases import config, __version__, util @@ -41,12 +43,38 @@ class DailyReleasesBot(object): self.reddit = Reddit(self.config) def run(self): - # If not production, generate now (without posting to reddit) and then exit - if not self.config["main"].getboolean("production"): - self.generate() - return + mode = self.config["main"]["mode"] - self.generate(do_post=True) + if mode == "test": + self.generate(post=False) + + if mode == "immediately": + self.generate(post=True, pm_recipients=self.config["reddit"]["notify_users"].split(",")) + + if mode == "reply": + self.listen_inbox() + + def listen_inbox(self): + logger.info("Listening on reddit inbox stream") + authorized_users = self.config["reddit"]["authorized_users"].split(",") + + while True: + try: + for message in self.reddit.praw.inbox.stream(): + if message.author in authorized_users: + self.generate(post=True, pm_recipients=(message.author.name,)) + else: + logger.info("Discarding PM from %s: not authorized user", message.author) + + message.mark_read() # mark message read last so we can retry after potential fatal errors + + except prawcore.PrawcoreException as e: + logger.warning("PrawcoreException: %s", e) + logger.info("Restarting inbox listener..") + + except KeyboardInterrupt: + print("Exiting (KeyboardInterrupt)") + break def find_store_links(self, game_name) -> dict: links = {} @@ -349,7 +377,7 @@ class DailyReleasesBot(object): logger.debug("Generated post:\n%s", post_str) return post_str - def generate(self, do_post=False): + def generate(self, post=False, pm_recipients=None): logger.info("-------------------------------------------------------------------------------------------------") start_time = time.time() @@ -358,26 +386,41 @@ class DailyReleasesBot(object): releases = self.predb.get_releases() parsed_releases, failed_dirnames = self.parse_releases(releases, already_posted) - post = self.generate_post(parsed_releases) - source = textwrap.indent(post, " ") - # The date of the post changes at midday instead of midnight to allow calling script after 00:00 title = "Daily Releases ({})".format((datetime.today() - timedelta(hours=12)).strftime("%B %-d, %Y")) - if do_post: - # Post to bot's subreddit - source_post = self.reddit.submit_post(f"{title} - Source", source, self.config["reddit"]["bot_subreddit"]) - preview_post = self.reddit.submit_post(title, post, self.config["reddit"]["bot_subreddit"]) + generated_post = self.generate_post(parsed_releases) + generated_post_src = textwrap.indent(generated_post, " ") + + if post: + # Post to bot's own subreddit + bot_subreddit = self.config["reddit"]["bot_subreddit"] + reddit_src_post = self.reddit.submit_post(f"{title} - Source", generated_post_src, bot_subreddit) + reddit_post = self.reddit.submit_post(title, generated_post, bot_subreddit) # Manually approve posts since reddit seem to think posts with many links are spam - source_post.mod.approve() - preview_post.mod.approve() + reddit_src_post.mod.approve() + reddit_post.mod.approve() self.save_already_posted(already_posted) + if pm_recipients is not None: + msg = inspect.cleandoc( + f""" + [Preview]({reddit_post.url}) + [Source]({reddit_src_post.url}) + Failed: {", ".join(failed_dirnames)} + """ + ) + for recipient in pm_recipients: + self.reddit.send_pm(recipient, title, msg) + logger.info("Execution took %s seconds", int(time.time() - start_time)) logger.info("-------------------------------------------------------------------------------------------------") + # Clean requests cache after each successful generation so it doesn't grow indefinitely + self.cache.remove_expired_responses() + def load_already_posted(self): try: with config.DATA_DIR.joinpath("already_posted").open() as file: