-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathiot.py
More file actions
executable file
·203 lines (176 loc) · 8.81 KB
/
iot.py
File metadata and controls
executable file
·203 lines (176 loc) · 8.81 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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/python3
import praw
import sys, os
import logging
import datetime
import argparse
args = None
if __name__ == "__main__":
os.chdir(os.path.dirname(sys.argv[0]))
parser = argparse.ArgumentParser(description='A Reddit bot to encourage better political discussions.',
epilog='This script exits after finishing its work, so it should be run in a cron job or similar.')
parser.add_argument('username', nargs='?', help='Username for the bot. If not provided, will not attempt to post.')
parser.add_argument('password', nargs='?', help='Password for the bot. If not provided, will prompt.')
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument('--test', help="Only post to /r/test.", action='store_true')
mode.add_argument('--live', help="Post to any subreddit in the list.", action='store_true')
group = parser.add_mutually_exclusive_group()
group.add_argument('--debug', help="Show debug messages.", action='store_true')
group.add_argument('-q','--quiet', dest='quiet', help="Suppress info messages in console.", action='store_true')
parser.add_argument('--no-fetch', help="Don't check for new threads, only use existing data.", action='store_true')
parser.add_argument('--no-post', help="Don't make any posts.", action='store_true')
args = parser.parse_args()
# set up logging
rootLogger = logging.getLogger()
rootLogger.setLevel(logging.DEBUG)
fh = logging.FileHandler('iot.log')
if args and args.debug:
fh.setLevel(logging.DEBUG)
else:
fh.setLevel(logging.INFO)
fh.setFormatter(logging.Formatter(fmt="%(asctime)s %(name)s [%(levelname)s]: %(message)s"))
ch = logging.StreamHandler()
if args and args.debug:
ch.setLevel(logging.DEBUG)
elif args and args.quiet:
ch.setLevel(logging.WARNING)
else:
ch.setLevel(logging.INFO)
rootLogger.addHandler(fh)
rootLogger.addHandler(ch)
logger = logging.getLogger(__name__)
import iot_db as db
def get_threads(subreddit_name):
sr = reddit.get_subreddit(subreddit_name)
threads = sr.get_hot(limit=50)
c = db.db.cursor()
for thread in threads:
if thread.is_self:
continue
row = db.select_one("SELECT * FROM threads WHERE permalink=?", thread.permalink)
if row == None:
logger.info("Found new thread %s in %s", thread.id, sr.display_name)
row = db.select_one("SELECT id FROM articles WHERE url=?", thread.url)
if row == None:
article_id = db.insert('articles', url=thread.url)
else:
article_id = row['id']
db.insert('threads', {'article_id': article_id, 'poster': thread.author.name, 'subreddit': sr.display_name,
'permalink': thread.permalink, 'karma': thread.score, 'comment_count': thread.num_comments,
'posted_at': datetime.datetime.utcfromtimestamp(thread.created_utc)})
else:
logger.info("Updating thread %d in %s from %d to %d comments", row['id'], sr.display_name, row['comment_count'], thread.num_comments)
c.execute("UPDATE threads SET comment_count=? WHERE id=?", (thread.num_comments, row['id']))
db.db.commit()
def get_best_comment(thread_id):
c = db.db.cursor()
thread_row = db.select_one("SELECT * FROM threads WHERE id=?", thread_id)
if thread_row == None:
raise KeyError("Thread %d not found in database." % thread_id)
thread = reddit.get_submission(url=thread_row['permalink'])
# comments are sorted by best by default
# loop until we find a real comment
for comment in thread.comments:
if not(comment.author): #[deleted]
continue
# don't quote ourselves :/
if args and args.username and comment.author.name.lower() == args.username.lower():
continue
if type(comment) == praw.objects.MoreComments:
return
if db.select_one("SELECT id FROM comments WHERE permalink=?", comment.permalink):
return #don't double-create
logger.info("Found new comment %s on thread %s in %s", comment.id, thread.id, thread.subreddit.display_name)
db.insert('comments', {'thread_id': thread_row['id'], 'poster': comment.author.name, 'body': comment.body,
'permalink': comment.permalink, 'karma': comment.score, 'posted_at': datetime.datetime.utcfromtimestamp(comment.created_utc)})
return
def quote_comment(comment):
return "\n".join(["> " + line for line in comment.split("\n")])
def do_post(source=None, target=None):
if not(source) or not(target):
raise ArgumentError("Must provide both source and target thread ids")
logger.info("Cross-post from %d to %d", source, target)
c = db.db.cursor()
source_row = db.select_one("SELECT * FROM threads WHERE id=?", source)
if source_row == None:
raise KeyError("Thread %d not found in database." % source)
target_row = db.select_one("SELECT * FROM threads WHERE id=?", target)
if target_row == None:
raise KeyError("Thread %d not found in database." % source)
comment_row = db.select_one("SELECT * FROM comments WHERE thread_id=? AND LOWER(poster)!=? ORDER BY id DESC", source, (args.username.lower() if args else ''))
if comment_row == None:
logger.info("No comment found for %d", source)
return
post = """This article is also being discussed in [a thread in /r/%(subreddit)s](%(comment_permalink)s).
Selected comment from that thread:
%(quoted_comment)s
^(by u/%(poster)s)
***
[^(about this bot)](http://www.reddit.com/r/botwatch/comments/1it7o3/inotherthreads_a_bot_to_find_crossposted_articles/)
""" % {'subreddit': source_row['subreddit'], 'permalink': source_row['permalink'], 'quoted_comment': quote_comment(comment_row['body']),
'poster': comment_row['poster'], 'comment_permalink': comment_row['permalink']}
if not(args) or args.username == None:
logger.info("Not posting because not logged in.")
return
if args and args.no_post:
logger.info("Not posting because --no-post was given.")
return
if args and args.test and target_row['subreddit'] != 'test':
logger.info("Not posting because --test given and not in /r/test.")
return
logger.debug(post)
thread = reddit.get_submission(url=target_row['permalink'])
comment = thread.add_comment(post)
comment_id = db.insert('comments', {'thread_id': target, 'poster': comment.author.name, 'body': comment.body,
'permalink': comment.permalink, 'karma': comment.score, 'posted_at': datetime.datetime.utcfromtimestamp(comment.created_utc)})
db.insert('xposts', {'source_id': source, 'target_id': target, 'status': 'posted', 'comment_id': comment_id})
##MAIN PROGRAM
db.init_db()
reddit = None
subreddits = ["anarchism","conservative","inthenews","liberal","libertarian","news","politics","socialism","technology","worldnews","worldpolitics","test"]
if __name__ == "__main__":
reddit = praw.Reddit(user_agent="InOtherThreads v0.2 github.com/DeltaWhy/in-other-threads")
if args.username == None:
logger.warning("No login given, running in test mode.")
elif args.password == None:
logger.debug("Logging in as %s", args.username)
reddit.login(args.username)
else:
logger.debug("Logging in as %s", args.username)
reddit.login(args.username, args.password)
if not(args.no_fetch):
for subreddit in subreddits:
get_threads(subreddit)
articles = db.get_article_ids()
sources = {}
for article in articles:
threads = db.get_source_thread_ids(article)
if len(threads) > 0:
sources[article] = threads
if not(args.no_fetch):
# flatten the sources
for thread in set([id for sublist in sources.values() for id in sublist]):
get_best_comment(thread)
targets = {}
for article in articles:
if article in sources:
threads = db.get_target_thread_ids(article)
if len(threads) > 0:
targets[article] = threads
possible_xposts = []
for article,target_threads in targets.items():
for target_thread in target_threads:
source_threads = [x for x in sources[article]] #no list.copy in Python 3.1!!!
if target_thread in source_threads:
source_threads.remove(target_thread)
for source_thread in source_threads:
possible_xposts.append((source_thread, target_thread))
logger.debug("Possible xposts: %s", possible_xposts)
for source, target in possible_xposts:
xpost = db.select_one("SELECT * FROM xposts WHERE source_id=? AND target_id=?", source, target)
if xpost == None:
do_post(source=source, target=target)
logger.info("Quitting after one post to avoid rate limit issues.")
exit(0)
else:
logger.info("%d to %d has already been posted", source, target)