def process(com, val): """ Multiprocessing target. Gets the Markov model, uses it to get a sentence, and posts that as a reply. """ warnings.simplefilter('ignore') if com == None: return id = com.name author = com.author.name if com.author else 'None' target_user = val[val.find(' ')+1:] with silent(): r = rlogin.get_auth_r(USER, APP, "2.1", uas="Windows:User Simulator/v0.3 by /u/Trambelus, operating on behalf of %s" % author) if target_user[:3] == '/u/': target_user = target_user[3:] log('%s: started %s for %s on %s' % (id, target_user, author, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)))) model = get_markov(r, id, target_user) if isinstance(model, str): com.reply(model % target_user) else: reply_r = model.make_sentence(tries=100) if reply_r == None: com.reply("Couldn't simulate %s: maybe this user is a bot, or has too few unique comments." % target_user) return reply = unidecode(reply_r) log("%s: Replying:\n%s" % (id, reply)) com.reply(reply) log('%s: Finished' % id)
def monitor(): """ Main loop. Looks through username notifications, comment replies, and whatever else, and launches a single process for every new request it finds. """ get_r = lambda: rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, main thread" % VERSION) started = [] q = mp.Queue() quit_proc = mp.Process(target=wait, args=(q, )) quit_proc.start() req_pat = re.compile( r"\+(\s)?/u/%s\s?(\[.\])?\s+(/u/)?[\w\d\-_]{3,20}" % USER.lower()) with silent(): r = get_r() t0 = time.time() log('Restarted') while True: try: # Every 55 minutes, refresh the login. if (time.time() - t0 > 55 * 60): with silent(): r = get_r() log("Refreshed login") t0 = time.time() mentions = r.get_inbox() for com in mentions: res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on try: if USER.lower() in [ rep.author.name.lower() for rep in com.replies if rep.author != None ]: continue # We've already hit this one, move on except praw.errors.Forbidden: continue if com.name in started: continue # We've already started on this one, move on started.append(com.name) warnings.simplefilter("ignore") mp.Process(target=process, args=(q, com, res.group(0))).start() if not quit_proc.is_alive(): log("Stopping main process") return while q.qsize() > 0: item = q.get() if item == 'clear': log("Clearing list of started tasks") started = [] elif item in started: started.remove(item) time.sleep(1) # General-purpose catch to make the script unbreakable. except praw.errors.InvalidComment: continue # This one was completely trashing the console, so handle it silently. except Exception as ex: log(str(type(ex)) + ": " + str(ex))
def get_user_top(sort): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, updating local user cache" % VERSION) redditor = r.get_redditor(USER) comments = redditor.get_comments(limit=None, sort=sort) for c in comments: print("%s at %s in %s: %s" % (c.name, dfmt(c.created_utc), c.subreddit.display_name, c.score)) with open("%s.txt" % sort,"a") as f: f.write(c.name + '\n')
def process(q, com, val): """ Multiprocessing target. Gets the Markov model, uses it to get a sentence, and posts that as a reply. """ warnings.simplefilter('ignore') if com == None: return id = com.name author = com.author.name if com.author else 'None' sub = com.subreddit.display_name val = val.replace('\n',' ') val = val.replace('\t',' ') val = val.replace(chr(160),' ') target_user = val[val.rfind(' ')+1:].strip() if author.lower() in NO_REPLY: com.reply("I see what you're trying to do.%s" % get_footer()) return if ('+/u/%s' % USER).lower() in target_user.lower(): com.reply("User '%s' appears to have broken the bot. That is not nice, %s.%s" % (author,author,get_footer())) return idx = com.body.lower().find(target_user.lower()) target_user = com.body[idx:idx+len(target_user)] with silent(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, operating on behalf of %s" % (VERSION,author)) if target_user[:3] == '/u/': target_user = target_user[3:] #log('%s: Started %s for %s on %s' % (id, target_user, author, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)))) (model, sentence_avg) = get_markov(r, id, target_user) try: if isinstance(model, str): com.reply((model % target_user) + get_footer()) log('%s by %s in %s on %s:\n%s\n' % (target_user, author, com.subreddit.display_name, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)), model % target_user)) else: reply_r = [] for _ in range(sentence_avg): tmp_s = model.make_sentence(tries=TRIES) if tmp_s == None: com.reply("Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user,get_footer())) return reply_r.append(tmp_s) if sentence_avg == 0: com.reply("Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user,get_footer())) return reply_r = ' '.join(reply_r) reply = unidecode(reply_r) if com.subreddit.display_name == 'EVEX': target_user = target_user + random.choice(['-senpai','-kun','-chan','-san','-sama']) log('%s (%d) by %s in %s on %s, reply:\n%s\n' % (target_user, sentence_avg, author, sub, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)), reply)) com.reply('%s\n\n ~ %s%s' % (reply,target_user,get_footer())) #log('%s: Finished' % id) except praw.errors.RateLimitExceeded as ex: log(id + ": Rate limit exceeded: " + str(ex)) q.put(id) except praw.errors.Forbidden: log("Could not reply to comment by %s in %s" % (author, sub)) except praw.errors.APIException: log("Parent comment by %s in %s was deleted" % (author, sub))
def open_by_id(id): import webbrowser r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, updating local user cache" % (platform.system(), VERSION)) webbrowser.open_new_tab(r.get_info(thing_id=id).permalink)
def monitor_sub(q, index): started = [] get_r = lambda: rlogin.get_auth_r( USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, main thread %d" % (VERSION, index)) req_pat = re.compile(r"\+(\s)?/u/%s\s?(\[.\])?\s+(/u/)?[\w\d\-_]{3,20}" % USER.lower()) with silent(): r = get_r() t0 = time.time() log('Started main thread %d' % (index + 1)) while True: try: # Every 55 minutes, refresh the login. if (time.time() - t0 > 55 * 60): with silent(): r = get_r() log("Refreshed login") t0 = time.time() mentions = r.get_inbox(limit=INBOX_LIMIT) for com in mentions: if int(com.name[3:], 36) % MONITOR_PROCESSES != index: continue res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on try: if USER.lower() in [ rep.author.name.lower() for rep in com.replies if rep.author != None ]: continue # We've already hit this one, move on except praw.errors.Forbidden: continue if com.name in started: continue # We've already started on this one, move on started.append(com.name) warnings.simplefilter("ignore") mp.Process(target=process, args=(q, com, res.group(0))).start() while q.qsize() > 0: item = q.get() if item == 'clear': log("Clearing list of started tasks") started = [] elif item == 'quit': log("Stopping main process") return elif item in started: started.remove(item) # General-purpose catch to make the script unbreakable. except praw.errors.InvalidComment: continue # This one was completely trashing the console, so handle it silently. except Exception as ex: log(str(type(ex)) + ": " + str(ex))
def monitor(): """ Main loop. Looks through username notifications, comment replies, and whatever else, and launches a single process for every new request it finds. """ started = [] q = mp.Queue() quit_proc = mp.Process(target=wait, args=(q,)) quit_proc.start() req_pat = re.compile(r"\+/u/%s\s(/u/)?[\w\d\-_]{3,20}" % USER.lower()) r = rlogin.get_auth_r(USER, APP) t0 = time.time() log('Restarted') while True: try: # Every 55 minutes, refresh the login. if (time.time() - t0 > 55*60): r = rlogin.get_auth_r(USER, APP) t0 = time.time() mentions = r.get_inbox() for com in mentions: if com.author == None: continue # Extreme edge case: they deleted their comment before the reply could process res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on if USER in [rep.author.name for rep in com.replies if rep.author != None]: continue # We've already hit this one, move on if com.name in started: continue # We've already started on this one, move on started.append(com.name) warnings.simplefilter("ignore") mp.Process(target=process, args=(com, res.group(0))).start() if not quit_proc.is_alive(): log("Quitting") return if q.qsize() > 0: if q.get() == 'clear': started = [] time.sleep(1) # General-purpose catch to make the script unbreakable. except Exception as ex: log("Error in main process: %s" % ex)
def count(user): r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, counting comments of %s" % (platform.system(), VERSION, user)) (history, num_comments, sentence_avg) = get_history(r, user) print("{}: {} comments, average {:.3f} sentences per comment".format( user, num_comments, sentence_avg))
def open_by_id(id): import webbrowser r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, updating local user cache" % (platform.system(), VERSION)) webbrowser.open_new_tab("http://www.reddit.com{}".format( r.comment(id).context))
def count(user): with silent(): r = rlogin.get_auth_r( USER, APP, VERSION, uas= "Windows:User Simulator/v%s by /u/Trambelus, counting comments of %s" % (VERSION, user)) history = get_history(r, user) print("{}: {} comments".format(user, len(history)))
def upgrade(): files = [f[:-4] for f in os.listdir(USERMOD_DIR) if f[-4:] == '.txt'] r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, upgrading cache" % VERSION) for user in files: info_fname = USERMOD_DIR + user + '.info' if os.path.isfile(info_fname): continue (history, num_comments, sentence_avg) = get_history(r, user) print("%s: average %d across %d comments" % (user, int(sentence_avg), num_comments)) with open(info_fname, 'w') as f: f.write(str(int(sentence_avg)))
def manual(user, num): """ This allows the script to be invoked like this: usim.py manual some-reddit-user This was useful for when the script failed to reply in some cases. I logged in as the script, got a manual response like this, and just pasted it in as a normal comment. They never knew. Don't tell them. """ r = rlogin.get_auth_r(USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, operating in manual mode" % (platform.system(),VERSION)) (model, sentence_avg) = get_markov(r, 'manual', user) print(' '.join([unidecode(model.make_sentence()) for i in range(sentence_avg)]))
def get_user_top(sort): r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, updating local user cache" % (platform.system(), VERSION)) redditor = r.get_redditor(USER) comments = redditor.get_comments(limit=None, sort=sort) for c in comments: print("%s at %s in %s: %s" % (c.name, dfmt(c.created_utc), c.subreddit.display_name, c.score)) with open("%s.txt" % sort, "a") as f: f.write(c.name + '\n')
def manual(user, num): """ This allows the script to be invoked like this: usim.py manual some-reddit-user This was useful for when the script failed to reply in some cases. I logged in as the script, got a manual response like this, and just pasted it in as a normal comment. They never knew. Don't tell them. """ with silent(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, operating in manual mode" % VERSION) model = get_markov(r, 'manual', user) log(unidecode(model.make_sentence()))
def get_banned(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, updating local user cache" % (platform.system(),VERSION)) with open(BANNED_FILE, 'r') as f: banned = [s.rstrip() for s in f.readlines()] c = 0 with open("bancount.txt", 'w') as f: for sub in banned: try: b = r.get_subreddit(sub).subscribers c += b except praw.errors.Forbidden: b = "Unknown" f.write("%s: %s\n" % (sub, b)) f.write("----------------\nTotal: %d" % c) print("Banned from %d subs, %d subscribers" % len(banned), c)
def monitor_sub(q, index): started = [] get_r = lambda: rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, main thread %d" % (VERSION, index)) req_pat = re.compile(r"\+(\s)?/u/%s\s?(\[.\])?\s+(/u/)?[\w\d\-_]{3,20}" % USER.lower()) with silent(): r = get_r() t0 = time.time() log('Started main thread %d' % (index+1)) while True: try: # Every 55 minutes, refresh the login. if (time.time() - t0 > 55*60): with silent(): r = get_r() log("Refreshed login") t0 = time.time() mentions = r.get_inbox(limit=INBOX_LIMIT) for com in mentions: if int(com.name[3:], 36) % MONITOR_PROCESSES != index: continue res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on try: if USER.lower() in [rep.author.name.lower() for rep in com.replies if rep.author != None]: continue # We've already hit this one, move on except praw.errors.Forbidden: continue if com.name in started: continue # We've already started on this one, move on started.append(com.name) warnings.simplefilter("ignore") mp.Process(target=process, args=(q, com, res.group(0))).start() while q.qsize() > 0: item = q.get() if item == 'clear': log("Clearing list of started tasks") started = [] elif item == 'quit': log("Stopping main process") return elif item in started: started.remove(item) # General-purpose catch to make the script unbreakable. except praw.errors.InvalidComment: continue # This one was completely trashing the console, so handle it silently. except Exception as ex: log(str(type(ex)) + ": " + str(ex))
def upgrade(): files = [f[:-4] for f in os.listdir(USERMOD_DIR) if f[-4:] == '.txt'] r = rlogin.get_auth_r( USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, upgrading cache" % VERSION) for user in files: info_fname = USERMOD_DIR + user + '.info' if os.path.isfile(info_fname): continue (history, num_comments, sentence_avg) = get_history(r, user) print("%s: average %d across %d comments" % (user, int(sentence_avg), num_comments)) with open(info_fname, 'w') as f: f.write(str(int(sentence_avg)))
def manual(user, num): """ This allows the script to be invoked like this: usim.py manual some-reddit-user This was useful for when the script failed to reply in some cases. I logged in as the script, got a manual response like this, and just pasted it in as a normal comment. They never knew. Don't tell them. """ r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, operating in manual mode" % (platform.system(), VERSION), ) (model, sentence_avg) = get_markov(r, "manual", user) print(" ".join([unidecode(model.make_sentence()) for i in range(sentence_avg)]))
def manual(user, num): """ This allows the script to be invoked like this: usim.py manual some-reddit-user This was useful for when the script failed to reply in some cases. I logged in as the script, got a manual response like this, and just pasted it in as a normal comment. They never knew. Don't tell them. """ with silent(): r = rlogin.get_auth_r( USER, APP, VERSION, uas= "Windows:User Simulator/v%s by /u/Trambelus, operating in manual mode" % VERSION) model = get_markov(r, 'manual', user) log(unidecode(model.make_sentence()))
def get_banned(): r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, updating local user cache" % (platform.system(), VERSION)) with open(BANNED_FILE, 'r') as f: banned = [s.rstrip() for s in f.readlines()] c = 0 with open("bancount.txt", 'w') as f: for sub in banned: try: b = r.get_subreddit(sub).subscribers c += b except praw.errors.Forbidden: b = "Unknown" f.write("%s: %s\n" % (sub, b)) f.write("----------------\nTotal: %d" % c) print("Banned from %d subs, %d subscribers" % len(banned), c)
def upgrade(): files = [f[:-4] for f in os.listdir(USERMOD_DIR) if f[-4:] == ".txt"] r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, upgrading cache" % (platform.system(), VERSION), ) for user in files: info_fname = os.path.join(USERMOD_DIR, user + ".info") if os.path.isfile(info_fname): continue (history, num_comments, sentence_avg) = get_history(r, user) print( "%s: average %d across %d comments" % (user, int(sentence_avg), num_comments) ) with open(info_fname, "w") as f: f.write(str(int(sentence_avg)))
def main(): # Holds every line of the file file_strs = [] # Import file with open(os.path.join(sys.path[0],LIST_PATH),'r') as f: file_strs = [s.rstrip() for s in f.readlines()] # Get local current time in struct_time (tuple) format lt = time.localtime() # Compare date portion of tuple to what's in the file if lt[:3] == tuple(map(int,file_strs[0].split('-'))): print("Already posted today; exiting") return # Obtain Reddit and subreddit from praw via rlogin r = rlogin.get_auth_r(USER, APP) sub = r.get_subreddit(SUB) # Which Bloom are we on today? index = int(file_strs[1]) # Trim list to submissions only; separate titles and URLs submissions = [s.split('\t') for s in file_strs[2:]] # If we've gone through all the file entries, exit. if len(submissions) <= index-3: print("No submissions left in file!") return # Construct post title title = submissions[index-3][0] if INCLUDE_POST_NUMS: title = 'Daily Bloom #%d: %s' % (index, submissions[index-3][0]) url = submissions[index-3][1] # Print to confirm print(title) print(url) # Update information to put back in the file file_strs[1] = str(index+1) file_strs[0] = time.strftime('%Y-%m-%d') # The big line. Submit to the PLounge. submission = r.submit(sub, title, url=url, send_replies=True) # Writeback with open(LIST_PATH,'w') as f: f.write('\n'.join(file_strs)) r.select_flair(submission, '20154dac-1ea8-11e5-8d27-0ef6ca535a4d', FLAIR)
def count(user): with silent(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, counting comments of %s" % (VERSION,user)) history = get_history(r, user) print("{}: {} comments".format(user, len(history)))
def process(q, com, val): """ Multiprocessing target. Gets the Markov model, uses it to get a sentence, and posts that as a reply. """ warnings.simplefilter('ignore') if com == None: return id = com.name author = com.author.name if com.author else '[deleted]' sub = com.subreddit.display_name ctime = time.strftime("%Y-%m-%d %X", time.localtime(com.created_utc)) val = val.replace('\n', ' ') val = val.replace('\t', ' ') val = val.replace(chr(160), ' ') target_user = val[val.rfind(' ') + 1:].strip() if author.lower() in NO_REPLY: try_reply(com, "I see what you're trying to do.%s" % get_footer()) return if ('+/u/%s' % USER).lower() in target_user.lower(): try_reply( q, com, "User '%s' appears to have broken the bot. That is not nice, %s.%s" % (author, author, get_footer())) return idx = com.body.lower().find(target_user.lower()) target_user = com.body[idx:idx + len(target_user)] with silent(): r = rlogin.get_auth_r( USER, APP, VERSION, uas= "Windows:User Simulator/v%s by /u/Trambelus, operating on behalf of %s" % (VERSION, author)) if target_user[:3] == '/u/': target_user = target_user[3:] if target_user == 'YOURUSERNAMEHERE': log("Corrected 'YOURUSERNAMEHERE' to %s" % author) target_user = author #log('%s: Started %s for %s on %s' % (id, target_user, author, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)))) try: next(r.get_redditor(target_user).get_comments(limit=1)) except praw.errors.NotFound: if levenshteinDistance(target_user, author) < 3: log("Corrected spelling from %s to %s" % (target_user, author)) target_user = author except StopIteration: pass except praw.errors.HTTPException: time.sleep(1) (model, sentence_avg) = get_markov(r, id, target_user) try: if isinstance(model, str): try_reply(q, com, (model % target_user) + get_footer()) log('%s: %s by %s in %s on %s:\n%s' % (id, target_user, author, sub, ctime, model % target_user), additional='\n') else: if sentence_avg == 0: try_reply( q, com, "Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user, get_footer())) return reply_r = [] for _ in range(random.randint(1, sentence_avg * 2)): tmp_s = model.make_sentence(tries=TRIES) if tmp_s == None: try_reply( q, com, "Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user, get_footer())) return reply_r.append(tmp_s) reply_r = ' '.join(reply_r) reply = unidecode(reply_r) if com.subreddit.display_name == 'EVEX': target_user = target_user + random.choice( ['-senpai', '-kun', '-chan', '-san', '-sama']) log('%s: %s (%d) by %s in %s on %s, reply' % (id, target_user, sentence_avg, author, sub, ctime), additional='\n%s\n' % reply) target_user = target_user.replace('_', '\_') try_reply(q, com, '%s\n\n ~ %s%s' % (reply, target_user, get_footer())) #log('%s: Finished' % id) except praw.errors.RateLimitExceeded as ex: log("%s: %s (%d) by %s in %s on %s: rate limit exceeded: %s" % (id, target_user, sentence_avg, author, sub, ctime, str(ex))) q.put(id) except praw.errors.Forbidden: log("Could not reply to comment by %s in %s" % (author, sub)) except praw.errors.APIException: log("Parent comment by %s in %s was deleted" % (author, sub)) except praw.errors.HTTPException: log("%s: %s (%d) by %s in %s on %s: could not reply, will retry: %s" % (id, target_user, sentence_avg, author, sub, ctime, str(ex))) q.put(id)
def open_by_id(id): import webbrowser r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, updating local user cache" % VERSION) webbrowser.open_new_tab(r.get_info(thing_id=id).permalink)
def process(q, com, val, index, r=None): """ Multiprocessing target. Gets the Markov model, uses it to get a sentence, and posts that as a reply. """ warnings.simplefilter("ignore") if com == None: return id = com.name author = com.author.name if com.author else "[deleted]" if r is None: r = rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, operating on behalf of %s" % (platform.system(), VERSION, author), ) if com == None: return sub = com.subreddit.display_name ctime = time.strftime("%Y-%m-%d %X", time.localtime(com.created_utc)) val = val.replace("\n", " ") val = val.replace("\t", " ") val = val.replace(chr(160), " ") target = val[val.rfind(" ") + 1 :].strip() if author.lower() in NO_REPLY: try_reply(com, "I see what you're trying to do.%s" % FOOTER) return if ("+/u/%s" % USER).lower() in target.lower(): try_reply( com, "User '%s' appears to have broken the bot. That is not nice, %s.%s" % (author, author, FOOTER), ) return idx = com.body.lower().find(target.lower()) target = com.body[idx : idx + len(target)] r_subreddit = re.compile(r"/?r/[\w\d_]{0,21}") if target[:2] == "u/": target = target[2:] # ugly but it works if target[:3] == "/u/": target = target[3:] if target == "YOURUSERNAMEHERE": log("Corrected 'YOURUSERNAMEHERE' to %s" % author) target = author # log('%s: Started %s for %s on %s' % (id, target, author, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)))) try: if target[:3] != "/r/": next(r.redditor(target).comments.new(limit=1)) except prawcore.exceptions.NotFound: if levenshteinDistance(target, author) < 3: log("Corrected spelling from %s to %s" % (target, author)) target = author except StopIteration: pass except praw.exceptions.APIException: time.sleep(1) (model, sentence_avg) = get_markov(r, id, target) try: if isinstance(model, str): try_reply(com, (model % target) + FOOTER) log( "%s: (%d) %s by %s in %s on %s:\n%s" % (id, index, target, author, sub, ctime, model % target), additional="\n", ) else: if sentence_avg == 0: try_reply( com, "Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target, FOOTER), ) return reply_r = [] for _ in range(random.randint(1, sentence_avg)): tmp_s = model.make_sentence(tries=TRIES) if tmp_s == None: try_reply( com, "Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target, FOOTER), ) return reply_r.append(tmp_s) reply_r = " ".join(reply_r) reply = unidecode(reply_r) if com.subreddit.display_name == "EVEX": target = target + random.choice( ["-senpai", "-kun", "-chan", "-san", "-sama"] ) log( "%s: (%d) %s (%d) by %s in %s on %s, reply" % (id, index, target, sentence_avg, author, sub, ctime), additional="\n%s\n" % reply, ) if target[:3] != "/r/": target = target.replace("_", "\_") try_reply(com, "%s\n\n ~ %s%s" % (reply, target, FOOTER)) # log('%s: Finished' % id) except prawcore.exceptions.Forbidden as ex: log("Could not reply to comment by %s in %s: %s" % (author, sub, str(ex))) except prawcore.exceptions.APIException: log("Parent comment by %s in %s was deleted" % (author, sub)) except prawcore.exceptions.PrawcoreException as ex: log( "%s: (%d) %s (%d) by %s in %s on %s: could not reply, will retry: %s" % (id, index, target, sentence_avg, author, sub, ctime, str(ex)) ) q.put(id)
def count(user): with silent(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, counting comments of %s" % (VERSION,user)) (history, num_comments, sentence_avg) = get_history(r, user) print("{}: {} comments, average {:.3f} sentences per comment".format(user, num_comments, sentence_avg))
def monitor_sub(q, index): started = [] with open(BANNED_FILE, "r") as f: banned = [s.rstrip() for s in f.readlines()] get_r = lambda: rlogin.get_auth_r( USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, main thread %d" % (platform.system(), VERSION, index), ) req_pat = re.compile( r"\+(\s)?/?u/%s\s?(\[.\])?\s+(/?(u|r)/)?[\w\d\-_]{3,32}" % USER.lower() ) r = get_r() t0 = time.time() log("Started main thread %d" % (index + 1)) while True: log("Restarting loop", console_only=True) try: # Every 55 minutes, refresh the login. if time.time() - t0 > 55 * 60: r = get_r() log("Refreshed login for thread %d" % (index + 1)) t0 = time.time() mentions = r.inbox.all() for com in mentions: if com.author and com.author.name.lower() in ABSOLUTELY_NO_REPLY: continue # Some joker set AutoModerator to constantly call the bot if int(com.name[3:], 36) % MONITOR_PROCESSES != index: continue # One of the other monitor threads is handling this one; skip if com.name in started: continue # We've already started on this one, move on started.append(com.name) try: if com.subreddit == None: continue if com.subreddit.display_name in banned: log( "%s: (%d) Ignored request from banned subreddit %s" % (com.name, index + 1, com.subreddit.display_name) ) continue except prawcore.exceptions.Forbidden: continue res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on try: if USER.lower() in [ rep.author.name.lower() for rep in com.replies.list() if rep.author != None ]: continue # We've already hit this one, move on except prawcore.exceptions.Forbidden: continue warnings.simplefilter("ignore") try: log("%s: processing" % (com.name), console_only=True) # mp.Process(target=process, args=(q, com, res.group(0), index+1)).start() process(q, com, res.group(0), index + 1, r) except Exception as ex: log("%s: exception: %s" % (com.name, ex)) continue while q.qsize() > 0: item = q.get() if item == "clear": log("Clearing list of started tasks") started = [] elif item == "quit": log("Stopping main process") return elif item in started: started.remove(item) time.sleep(1) except AssertionError: r = get_r() continue # General-purpose catch to make the script unbreakable. except Exception as ex: log(str(index + 1) + ": " + str(type(ex)) + ": " + str(ex)) time.sleep(1) continue
def process(q, com, val): """ Multiprocessing target. Gets the Markov model, uses it to get a sentence, and posts that as a reply. """ warnings.simplefilter('ignore') if com == None: return id = com.name author = com.author.name if com.author else '[deleted]' sub = com.subreddit.display_name ctime = time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)) val = val.replace('\n',' ') val = val.replace('\t',' ') val = val.replace(chr(160),' ') target_user = val[val.rfind(' ')+1:].strip() if author.lower() in NO_REPLY: try_reply(com,"I see what you're trying to do.%s" % get_footer()) return if ('+/u/%s' % USER).lower() in target_user.lower(): try_reply(q, com,"User '%s' appears to have broken the bot. That is not nice, %s.%s" % (author,author,get_footer())) return idx = com.body.lower().find(target_user.lower()) target_user = com.body[idx:idx+len(target_user)] with silent(): r = rlogin.get_auth_r(USER, APP, VERSION, uas="Windows:User Simulator/v%s by /u/Trambelus, operating on behalf of %s" % (VERSION,author)) if target_user[:3] == '/u/': target_user = target_user[3:] if target_user == 'YOURUSERNAMEHERE': log("Corrected 'YOURUSERNAMEHERE' to %s" % author) target_user = author #log('%s: Started %s for %s on %s' % (id, target_user, author, time.strftime("%Y-%m-%d %X",time.localtime(com.created_utc)))) try: next(r.get_redditor(target_user).get_comments(limit=1)) except praw.errors.NotFound: if levenshteinDistance(target_user, author) <3: log("Corrected spelling from %s to %s" % (target_user, author)) target_user = author except StopIteration: pass except praw.errors.HTTPException: time.sleep(1) (model, sentence_avg) = get_markov(r, id, target_user) try: if isinstance(model, str): try_reply(q, com,(model % target_user) + get_footer()) log('%s: %s by %s in %s on %s:\n%s' % (id, target_user, author, sub, ctime, model % target_user), additional='\n') else: if sentence_avg == 0: try_reply(q, com,"Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user,get_footer())) return reply_r = [] for _ in range(random.randint(1,sentence_avg*2)): tmp_s = model.make_sentence(tries=TRIES) if tmp_s == None: try_reply(q, com,"Couldn't simulate %s: maybe this user is a bot, or has too few unique comments.%s" % (target_user,get_footer())) return reply_r.append(tmp_s) reply_r = ' '.join(reply_r) reply = unidecode(reply_r) if com.subreddit.display_name == 'EVEX': target_user = target_user + random.choice(['-senpai','-kun','-chan','-san','-sama']) log('%s: %s (%d) by %s in %s on %s, reply' % (id, target_user, sentence_avg, author, sub, ctime), additional='\n%s\n' % reply) target_user = target_user.replace('_','\_') try_reply(q, com,'%s\n\n ~ %s%s' % (reply,target_user,get_footer())) #log('%s: Finished' % id) except praw.errors.RateLimitExceeded as ex: log("%s: %s (%d) by %s in %s on %s: rate limit exceeded: %s" % (id, target_user, sentence_avg, author, sub, ctime, str(ex))) q.put(id) except praw.errors.Forbidden: log("Could not reply to comment by %s in %s" % (author, sub)) except praw.errors.APIException: log("Parent comment by %s in %s was deleted" % (author, sub)) except praw.errors.HTTPException: log("%s: %s (%d) by %s in %s on %s: could not reply, will retry: %s" % (id, target_user, sentence_avg, author, sub, ctime, str(ex))) q.put(id)
def monitor_sub(q, index): started = [] with open(BANNED_FILE, 'r') as f: banned = [s.rstrip() for s in f.readlines()] get_r = lambda: rlogin.get_auth_r(USER, APP, VERSION, uas="%s:User Simulator/v%s by /u/Trambelus, main thread %d" % (platform.system(),VERSION, index)) req_pat = re.compile(r"\+(\s)?/u/%s\s?(\[.\])?\s+(/u/)?[\w\d\-_]{3,20}" % USER.lower()) r = get_r() t0 = time.time() log('Started main thread %d' % (index+1)) while True: log("Restarting loop", console_only=True) try: # Every 55 minutes, refresh the login. if (time.time() - t0 > 55*60): r = get_r() log("Refreshed login for thread %d" % (index+1)) t0 = time.time() mentions = r.get_inbox(limit=INBOX_LIMIT) for com in mentions: if int(com.name[3:], 36) % MONITOR_PROCESSES != index: continue # One of the other monitor threads is handling this one; skip if com.name in started: continue # We've already started on this one, move on started.append(com.name) try: if com.subreddit == None: continue if com.subreddit.display_name in banned: log("%s: (%d) Ignored request from banned subreddit %s" % (com.name,index+1,com.subreddit.display_name)) continue except praw.errors.Forbidden: continue res = re.search(req_pat, com.body.lower()) if res == None: continue # We were mentioned but it's not a proper request, move on try: if USER.lower() in [rep.author.name.lower() for rep in com.replies if rep.author != None]: continue # We've already hit this one, move on except praw.errors.Forbidden: continue warnings.simplefilter("ignore") try: log("%s: processing" % (com.name), console_only=True) #mp.Process(target=process, args=(q, com, res.group(0), index+1)).start() process(q, com, res.group(0), index+1) except Exception: continue while q.qsize() > 0: item = q.get() if item == 'clear': log("Clearing list of started tasks") started = [] elif item == 'quit': log("Stopping main process") return elif item in started: started.remove(item) time.sleep(1) except praw.errors.InvalidComment: continue # This one was completely trashing the console, so handle it silently. except AssertionError: r = get_r() continue # General-purpose catch to make the script unbreakable. except Exception as ex: log(str(index+1) + ": " + str(type(ex)) + ": " + str(ex)) time.sleep(1) continue