Browse Source

Reddit poster (#1026)

* ignoring venv file

* I can read reddit posts

* Drop scope to just post articles, this will do for now

* This has side effects on reddit!

* finally found the url and context in the same func

* I think this mostly works

* Add collection sub to example

* Add readme file may be more handy then put it in source code

* Let's not put our life story in the module description

* Clean up code a bit, remove dead code, move random assignment below imports

* Make the searching stop work

* Simplify posting code

Late at night I apprantly don't do many reductions

* add -> append

* re-add search to avoid assertion error

* I guess we just catch the assertion error, don't know why it fails

* Ignore empty string subs that may be caused by additional spacing

* Use crosspost feature to reduce rate limit issues

* Don't  resbumit
Jappie Klooster 6 years ago
parent
commit
e2023213fb
4 changed files with 142 additions and 0 deletions
  1. 1 0
      reddit_poster/.gitignore
  2. 51 0
      reddit_poster/Readme.md
  3. 81 0
      reddit_poster/__init__.py
  4. 9 0
      reddit_poster/redditconf.py.example

+ 1 - 0
reddit_poster/.gitignore

@@ -0,0 +1 @@
+.venv

+ 51 - 0
reddit_poster/Readme.md

@@ -0,0 +1,51 @@
+Reddit poster posts articles to reddit
+===================================
+
+You can use the 'subreddit' attribute in you articles to specify which 
+subbreddit the article should be post in aside of your default sub.
+
+We look for the subreddit in the metadata.
+Then try to find a post that has the same title as this article.
+If we can't find a post we create it.
+
+## Usage
+I followed these steps for praw: https://praw.readthedocs.io/en/latest/getting_started/authentication.html#script-application
+Summeraized:
++ Go to https://www.reddit.com/prefs/apps/ 
++ create a script for example named 'reddit-poster'.
++ redirect uri is unused so can be http://localhost:8080
++ about uri is probably some page on your website, not important either I think
+
+This will create an 'application', ie it let's reddit know you have a script by 
+using the secret generated.
+Interesting from this screen is the secret and the app id. copy those over 
+somewhere.
+
+We need 4 pieces of information from you which you must *not* commit to source 
+code. These should all be considered private.
+This script will expect these values to be set in the settings.
+You should store them for real in either an ignored file, environment variables,
+or pass them each time as command line options.
+Also make sure to set your SITEURL, for proper backlinks
+
+https://praw.readthedocs.io/en/latest/code_overview/models/subreddit.html#praw.models.Subreddit.search
+https://praw.readthedocs.io/en/latest/code_overview/models/subreddit.html#praw.models.Subreddit.submit
+
+### Config example
+
+```python
+REDDIT_POSTER_AUTH = {
+    'username': 'blah',
+    'password': 'pass',
+    'client_id': 'client',
+    'client_secret': 'secret',
+    'user_agent': 'python:pelican_redditposter:1 by /u/jappieofficial'
+}
+REDDIT_POSTER_COLLECT_SUB = "jappie"
+```
+## Dependencies
++ praw 5.4
+
+On fedora the praw in yum is too old, however the pip requests doesn't 
+work for some reason, so I installed praw with pip and requests with dnf 
+which seems to work (requests probably needed some sys lib or something)

+ 81 - 0
reddit_poster/__init__.py

@@ -0,0 +1,81 @@
+"""
+Reddit poster posts articles to reddit
+
+You can use the 'subreddit' attribute in you articles to specify which 
+subbreddit the article should be post in aside of your default sub.
+"""
+
+from collections import OrderedDict
+
+from pelican import signals
+from pelican.generators import Generator
+from functools import partial
+import logging
+import praw
+
+log = logging.getLogger(__name__)
+
+def cross_post(reddit, submission, subs):
+    subreddits = [] if subs is None else subs.split(' ')
+    log.debug("Posting in marked subs: %s", subreddits)
+    for sub in subreddits:
+        if sub == '':
+            continue
+        log.debug("Posting in %s" % sub)
+        subreddit = reddit.subreddit(sub)
+        subreddit.subscribe() # must be subscribed to crosspost
+        submission.crosspost(subreddit)
+
+def make_posts(generator, metadata, url):
+    """
+    Make posts on reddit if it's not a draft, on whatever subs are specified
+    """
+    reddit = generator.get_reddit()
+    if reddit is None:
+        log.info("Reddit plugin not enabled")
+        return
+    if metadata.get('status') == "draft": # people don't want to post drafts
+        log.debug("ignoring draft %s" % metadata['title'])
+        return
+
+    collection = generator.settings['REDDIT_POSTER_COLLECT_SUB']
+    sub = reddit.subreddit(collection)
+    results = sub.search(metadata['title'])
+    if len([result for result in results]) > 0:
+        log.debug("ignoring %s because it is already on sub %s " % (metadata['title'], collection))
+        # post already was made to this sub
+        return
+    try:
+        submission = sub.submit(metadata['title'], url=url, resubmit=False)
+        cross_post(reddit, submission, metadata.get('subreddit'))
+    except praw.exceptions.APIException as e:
+        log.error("got an api exception: %s", e)
+    except AssertionError as e:
+        log.error("Received an assertion error %s", e)
+
+
+def init_reddit(generator):
+    """
+    this is a hack to make sure the reddit object keeps track of a session
+    trough article scanning, speeding up networking as the connection can be 
+    kept alive.
+    """
+    auth_dict = generator.settings.get('REDDIT_POSTER_AUTH')
+    if auth_dict is None:
+        log.info("Could not find REDDIT_POSTER_AUTH key in settings, reddit plugin won't function")
+        generator.get_reddit = lambda: None
+        return
+
+    reddit = praw.Reddit(**auth_dict)
+    generator.get_reddit = lambda: reddit # .. openworld has it's merrits
+
+def content_written(generator, content):
+    """
+    create a url and call make posts (which has less information)
+    """
+    url = "%s/%s" % (generator.settings.get('SITEURL', 'http://localhost:8000'), content.url)
+    make_posts(generator, content.metadata, url)
+
+def register():
+    signals.article_generator_write_article.connect(content_written)
+    signals.article_generator_init.connect(init_reddit)

+ 9 - 0
reddit_poster/redditconf.py.example

@@ -0,0 +1,9 @@
+REDDIT_POSTER_AUTH = {
+    'username': 'blah',
+    'password': 'pass',
+    'client_id': 'client',
+    'client_secret': 'secret',
+    'user_agent': 'python:pelican_redditposter:1 by /u/jappieofficial'
+}
+# a sub where all the posts get collected
+REDDIT_POSTER_COLLECT_SUB = "jappie"