1
0
Fork 0

Add back reddit inbox listening mode; now configurable using the 'mode' config parameter.

Add support for notifying reddit users of generation in 'immediately' mode.
This commit is contained in:
Casper V. Kristensen 2018-08-25 19:32:34 +02:00
parent abb36769fe
commit 809ffd05ec
Signed by: caspervk
GPG key ID: B1156723DB3BDDA8
3 changed files with 73 additions and 22 deletions

View file

@ -11,7 +11,7 @@ pip3 install --upgrade https://git.caspervk.net/caspervk/dailyreleases/archive/m
**It requires Python 3.6 or later.** **It requires Python 3.6 or later.**
### Usage ### 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 ### Configuration
The default configuration file will be copied to `~/.dailyreleases/config.ini` on the first run. All fields under the The default configuration file will be copied to `~/.dailyreleases/config.ini` on the first run. All fields under the

View file

@ -1,7 +1,10 @@
[main] [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. # mode =
production = false # 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] [logging]
@ -19,10 +22,15 @@ user_agent = <platform>:<app ID>:<version string> (by /u/<reddit username>)
username = xxxxxxxxxxxxx username = xxxxxxxxxxxxx
password = xxxxxxx 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 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 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. # 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] [web]
# Number of seconds to cache web requests (google, steam etc.). May help reduce the number of requests if the same game # 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 cache_time = 600

View file

@ -1,3 +1,4 @@
import inspect
import json import json
import logging import logging
import re import re
@ -7,6 +8,7 @@ import time
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
import prawcore
import requests_cache import requests_cache
from dailyreleases import config, __version__, util from dailyreleases import config, __version__, util
@ -41,12 +43,38 @@ class DailyReleasesBot(object):
self.reddit = Reddit(self.config) self.reddit = Reddit(self.config)
def run(self): def run(self):
# If not production, generate now (without posting to reddit) and then exit mode = self.config["main"]["mode"]
if not self.config["main"].getboolean("production"):
self.generate()
return
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: def find_store_links(self, game_name) -> dict:
links = {} links = {}
@ -349,7 +377,7 @@ class DailyReleasesBot(object):
logger.debug("Generated post:\n%s", post_str) logger.debug("Generated post:\n%s", post_str)
return post_str return post_str
def generate(self, do_post=False): def generate(self, post=False, pm_recipients=None):
logger.info("-------------------------------------------------------------------------------------------------") logger.info("-------------------------------------------------------------------------------------------------")
start_time = time.time() start_time = time.time()
@ -358,26 +386,41 @@ class DailyReleasesBot(object):
releases = self.predb.get_releases() releases = self.predb.get_releases()
parsed_releases, failed_dirnames = self.parse_releases(releases, already_posted) 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 # 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")) title = "Daily Releases ({})".format((datetime.today() - timedelta(hours=12)).strftime("%B %-d, %Y"))
if do_post: generated_post = self.generate_post(parsed_releases)
# Post to bot's subreddit generated_post_src = textwrap.indent(generated_post, " ")
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"]) 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 # Manually approve posts since reddit seem to think posts with many links are spam
source_post.mod.approve() reddit_src_post.mod.approve()
preview_post.mod.approve() reddit_post.mod.approve()
self.save_already_posted(already_posted) 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("Execution took %s seconds", int(time.time() - start_time))
logger.info("-------------------------------------------------------------------------------------------------") 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): def load_already_posted(self):
try: try:
with config.DATA_DIR.joinpath("already_posted").open() as file: with config.DATA_DIR.joinpath("already_posted").open() as file: