Created
December 14, 2014 13:02
-
-
Save phikal/903b3d1a74b891247372 to your computer and use it in GitHub Desktop.
A bot for Reddit.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# sendto - Reddit bot v1.2 | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# PRAW - PRAW: The Python Reddit Api Wrapper // https://praw.readthedocs.org/en/v2.1.16/ | |
import praw | |
import re | |
from threading import Thread | |
from time import sleep | |
import traceback | |
import logging as log | |
import datetime | |
# Possible replies | |
res_sucesss = """ | |
**Post has been sent to /r/{}.** | |
Link: {} | |
*^(Hi! I'm a bot. No need to reply. About me: http://redd.it/2ihemu)* | |
""" | |
res_nonexist = """ | |
**Subreddit (/r/{}) does not exist.** | |
*^(Hi! I'm a bot. No need to reply. About me: http://redd.it/2ihemu)* | |
""" | |
res_alredy = """ | |
**Link alredy submitted to /r/{}.** | |
*^(Hi! I'm a bot. No need to reply. About me: http://redd.it/2ihemu)* | |
""" | |
res_direct = """ | |
**This link has been sent from /r/{} by /u/{}.** | |
Comment of origin: {} | |
*^(Hi! I'm a bot. No need to reply. About me: http://redd.it/2ihemu)* | |
""" | |
res_ignored = """ | |
**This subreddit ({}) is ignored.*** | |
*^(Hi! I'm a bot. No need to reply. About me: http://redd.it/2ihemu)* | |
""" | |
r = praw.Reddit(user_agent='/u/sendto bot 1.0 by /u/SurviAvi') | |
# Compile syntax matching RE | |
find = re.compile("/u/sendto(!)?\s+/r/(\w+)(?:\s+(.*))?$") | |
# All responses to be processed | |
queue = [] | |
# All ignored subreddits | |
ignored = [] | |
verbose = 1 | |
# Processing thread | |
class Work(Thread): | |
def __init__(self): | |
Thread.__init__(self) | |
self.daemon = True | |
self.start() | |
def run(self): | |
while True: | |
# Check if there is anything to process | |
if len(queue) == 0: | |
sleep(30) | |
continue | |
# Get first item | |
work = queue.pop(0) | |
# Get comment and subreddit | |
sub = r.get_subreddit(work['sub']) | |
com = r.get_info(thing_id='t1_'+work['id']) | |
# Try posting and replying to comment | |
try: | |
resp = sub.submit(work['title'], url=work['link']) | |
com.reply(res_sucesss.format(work['sub'], resp.short_link)) | |
if 'direct' in work: | |
resp.add_comment(res_direct.format(com.subreddit.display_name, com.author.name, com.permalink)) | |
print(work['id']+': processed') | |
# Errors: | |
except praw.errors.InvalidSubreddit: | |
com.reply(res_nonexist.format(work['sub'])) | |
log.info(work['id']+': posted on inv. subreddit') | |
except praw.errors.AlreadySubmitted: | |
com.reply(res_alredy.format(work['sub'])) | |
log.warning(work['id']+': already submitted') | |
except (praw.errors.RateLimitExceeded, praw.errors.APIException): | |
queue.append(work) | |
log.warning(work['id']+': exceded rate limit or quota filled') | |
sleep(180) | |
except: | |
log.error('[PT] Other error:\n' + traceback.format_exc()) | |
break | |
def main(user, passw): | |
# Login using api | |
r.login(user, passw) | |
# Start processing thread | |
Work() | |
# Start parsing incoming comments from /r/all/new | |
log.info('STARTED') | |
while True: | |
try: | |
i = 0 | |
for comment in praw.helpers.comment_stream(r, 'all', limit=None, verbosity=verbose): # Get all incoming comments | |
# Should it be ignored? | |
if comment.subreddit.display_name in ignored: | |
continue | |
# Check if commeny body matches notify syntax | |
mat = find.match(comment.body) | |
if mat: | |
# Should it be ignored? | |
if mat.group(2) in ignored: | |
comment.reply(res_ignored.format(mat.group(2))) | |
continue | |
log.info('parsed comment: '+comment.id) | |
obj = { | |
'sub' : mat.group(2), | |
'title' : mat.group(3) or '"'+comment.submission.title+'"', | |
'id' : comment.id | |
} | |
# Direct link? | |
if mat.group(1): | |
obj['link'] = comment.submission.url | |
obj['direct'] = True | |
# Get link for submission ( is parent submis. or comment) | |
elif comment.is_root: | |
obj['link'] = comment.submission.permalink | |
else: | |
obj['link'] = r.get_info(thing_id=comment.parent_id).permalink | |
# Add to response list | |
queue.append(obj) | |
except KeyboardInterrupt: | |
log.info("Stopped by user.") | |
break | |
except: | |
log.error('[MT] Error:\n'+traceback.format_exc()) | |
break | |
if __name__ == '__main__': | |
from json import load, dump | |
from os.path import isfile | |
import atexit | |
file_name = 'config.json' | |
queue_file = 'queue.json' | |
# Check if file exists | |
if not isfile(file_name): | |
log.warning('config.json missing') | |
exit() | |
# Get config.json data | |
with open(file_name) as file: | |
data = load(file) | |
# ignored subreddits | |
if 'ignore' in data: | |
ignored = data['ignore'] | |
assert isinstance(ignored, list) | |
def onexit(): | |
with open(queue_file, 'w') as outfile: | |
dump(queue, outfile) | |
atexit.register(onexit) | |
if isfile(queue_file): | |
with open(queue_file) as file: | |
queue = load(file) | |
assert isinstance(queue, list) | |
# stdout redirect | |
if 'stdout' in data: | |
log.basicConfig(filename=data['stdout'], filemode='w', level=log.INFO) | |
verbose = 0 | |
# Start program | |
try: | |
main(data['user'], data['pass']) | |
except KeyboardInterrupt: | |
log.info("Stopped by user.") | |
except: | |
log.error('[MT] Error:\n'+traceback.format_exc()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment