-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathreddit_auto_crosspost_bot.py
More file actions
161 lines (135 loc) · 5.49 KB
/
reddit_auto_crosspost_bot.py
File metadata and controls
161 lines (135 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""Main entry point of the program"""
import logging
from logging.handlers import RotatingFileHandler
import argparse
import time
from distutils import util
import os
import praw
import requests
import schedule
import prawcore
import urllib3
import reddit_instantiator
import unwanted_submission_remover
import inbox_handler
import phase1_handler
import phase2_handler
import phase3_handler
comment_stream = None
inbox_stream = None
def configure_logging():
file_handler = RotatingFileHandler("app.log", mode='a', delay=0,
maxBytes=5 * 1024 * 1024,
backupCount=1, encoding='utf-8')
stream_handler = logging.StreamHandler()
debug = bool(util.strtobool(os.environ.get('DEBUG')))
level = logging.INFO
if debug:
level = logging.DEBUG
file_handler.setLevel(logging.INFO)
stream_handler.setLevel(level)
logging_blacklist = ['prawcore', 'urllib3.connectionpool', 'schedule']
for item in logging_blacklist:
logging.getLogger(item).disabled = True
logging.basicConfig(format='%(asctime)-15s - %(name)s - %(levelname)s - %(message)s',
level=level,
handlers=[
file_handler,
stream_handler
])
def init_streams():
reddit = reddit_instantiator.get_reddit_instance()
scanned_subreddits = 'all'
subreddit = reddit.subreddit(scanned_subreddits)
comment_stream = subreddit.stream.comments(skip_existing=True, pause_after=-1)
inbox_stream = reddit.inbox.stream(mark_read=False, pause_after=-1)
return (comment_stream, inbox_stream)
def set_schedule():
schedule.every(7).minutes.do(unwanted_submission_remover.delete_unwanted_submissions)
schedule.every(20).minutes.do(phase2_handler.filter_comments_from_db)
listen_only = bool(util.strtobool(os.environ.get('LISTEN_ONLY')))
if not listen_only:
schedule.every(6).minutes.do(phase3_handler.process_comment_entries)
debug = bool(util.strtobool(os.environ.get('DEBUG')))
if debug:
schedule.run_all()
def main():
configure_logging()
logging.info('Running reddit_auto_crosspost_bot')
parser = argparse.ArgumentParser()
parser.add_argument('--only-phase2', action='store_true')
args = parser.parse_args()
if args.only_phase2:
phase2_handler.filter_comments_from_db(verbose=True)
return
start_bot()
def start_bot():
set_schedule()
comment_stream, inbox_stream = init_streams()
while True:
try:
main_loop(comment_stream, inbox_stream)
except Exception as e:
should_raise = handle_exception(e)
if should_raise:
raise
else:
comment_stream, inbox_stream = init_streams()
def handle_exception(e):
should_raise = True
debug = bool(util.strtobool(os.environ.get('DEBUG')))
if debug:
return should_raise
if type(e) in (prawcore.exceptions.ServerError,
prawcore.exceptions.Forbidden,
requests.exceptions.ConnectTimeout,):
# Sometimes the reddit service fails (e.g. error 503)
# One time I got an error 403 (unaothorized) for no apparent reason
# Other times the internet connection fails
# just wait a bit a try again
logging.info(f'Ecnountered network error {e}. Waiting and retrying.')
time.sleep(30)
should_raise = False
elif type(e) is prawcore.exceptions.RequestException:
is_max_retry_or_read_timeout_error = (
e.original_exception and
e.original_exception.args and
len(e.original_exception.args) > 0 and
( isinstance(e.original_exception.args[0], urllib3.exceptions.MaxRetryError) or
isinstance(e.original_exception.args[0], urllib3.exceptions.ReadTimeoutError)
)
)
if is_max_retry_or_read_timeout_error:
logging.info(f'Ecnountered network error {e}. Waiting and retrying.')
time.sleep(30)
should_raise = False
elif type(e) is praw.exceptions.RedditAPIException:
if e.error_type == 'DELETED_COMMENT':
logging.info(f'Attempted to interact with a deleted comment. {e}')
should_raise = False
elif e.error_type == 'THREAD_LOCKED':
logging.info(f'Attempted to comment in a locked thread. {e}')
should_raise = False
elif e.error_type == 'SOMETHING_IS_BROKEN':
logging.info(f'Got that weird unhelpful "Something is broken" API exception from reddit. {e}')
should_raise = False
if should_raise:
logging.exception(e)
return should_raise
def main_loop(comment_stream, inbox_stream):
for comment in comment_stream:
if comment is None:
break
phase1_handler.handle_incoming_comment(comment)
for comment in inbox_stream:
if comment is None:
break
inbox_handler.respond_to_comment(comment)
schedule.run_pending()
if __name__ == '__main__':
main()
# TODO Change title of crossposts specific subreddits according to their rules (e.g. when crossposting into /r/TIHI rename the post to "Thanks I hate it")
# TODO Investigate what caused this bug:
# https://www.reddit.com/r/MightyHarvest/comments/k6sj6v/this_is_our_8_year_old_pear_tree_puts_out_one/
# https://www.reddit.com/r/MightyHarvest/comments/inordb/my_pear_tree_one_yearly_perfect_pear_no_leaves/