Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- LICENCE = "WTFPL", "http://www.wtfpl.net/about/"
- VERSION = "v0.3.9"
- import logging
- import pickle
- import praw
- import prawcore
- import random
- import shelve
- import string
- import sys
- from datetime import datetime, timedelta
- from math import ceil
- from time import sleep
- from random import randint
- from statistics import mean
- # Set up logging facilities
- log = logging.getLogger("VirtueTron")
- log.setLevel(logging.DEBUG)
- formatter = logging.Formatter(fmt="%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y/%m/%d %I:%M:%S%p")
- file_log = logging.FileHandler(filename="VirtueTron.log", mode="a")
- file_log.setLevel(logging.INFO)
- file_log.setFormatter(formatter)
- log.addHandler(file_log)
- console_log = logging.StreamHandler(stream=sys.stdout)
- console_log.setLevel(logging.DEBUG)
- console_log.setFormatter(formatter)
- log.addHandler(console_log)
- log.info("VirtueTron® 9000™ {0} © CrashARuntimeToday@outlook.com".format(VERSION))
- # Connect to Reddit
- credentials = pickle.load(open("credentials.pickle", "rb")) # { client_id: "VirtueTron9000", client_secret: "🤖🤡🍆💯™", username: "SignalAVirtueToday", password: "https://youtu.be/RCVJ7bujnSc"}
- credentials["user_agent"] = "VirtueTron 9000 {0}".format(VERSION)
- reddit = praw.Reddit(**credentials)
- tbp = reddit.subreddit("TheBluePill")
- log.info("Hello, Reddit!")
- # Define some constants
- NICE_LIST = []
- NAUGHTY_LIST = ["theredpill", "marriedredpill", "mgtow", "braincels", "asktrp", "askmrp", "redpillwomen", "redpillwives", "cringeanarchy", "the_donald", "rpchristians", "pussypassdenied", "mensrights", "milliondollarextreme", "4chan", "whereareallthegoodmen"]
- IMMUTABLE_FLAIRS = ["vanguard", "vexatious", "endorsedflair", "alpha", "betaasfuck", "feeemale", "purged"]
- BAD_FLAIRS = ["purged", "vexatious"]
- GOOD_FLAIRS = ["endorsedflair", "vanguard", "alpha", "betaasfuck", "feeemale"]
- TODAYS_THREAT_LEVEL = {"tlsevere":"Severe", "tlhigh":"High", "tlelevated":"Elevated", "tlguarded":"Guarded", "tllow":"Low"}
- THREAT_MATRIX = {"tllow": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,7)],
- "tlguarded": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,5)],
- "tlelevated": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,2)],
- "tlhigh": BAD_FLAIRS,
- "tlsevere": []}
- FLAIR_EXEMPT = []
- # Helper functions
- delay = 2
- last_fuckup = None
- def praw_fucked_up():
- global delay
- global last_fuckup
- log.error("Reddit API error: waiting {0} seconds".format(delay))
- try:
- if delay > 128 or datetime.now() > last_fuckup + timedelta(minutes=2): delay = 2
- except TypeError:
- delay = 2
- sleep(delay)
- delay *= 2
- last_fuckup = datetime.now()
- def is_user_valid(name):
- try:
- for comment in reddit.redditor(name).new(limit=1):
- log.debug("User: {0} still exists (most recent comment id is {1})".format(name, comment.id))
- return True
- except (prawcore.exceptions.NotFound, prawcore.exceptions.Forbidden):
- return False
- def drop_user(name):
- log.info("User: {0} is deleted or suspended, dropping from userlist".format(name))
- del users[name]
- del user_data[name]
- def get_flair(name):
- if is_user_valid(name):
- while True:
- try:
- for flair in tbp.flair(redditor=name):
- return flair["flair_css_class"]
- except prawcore.PrawcoreException:
- praw_fucked_up()
- else:
- return False
- def set_flair(name, flair_text, flair_css):
- if is_user_valid(name):
- while True:
- try:
- tbp.flair.set(name, flair_text, flair_css)
- return True
- except prawcore.PrawcoreException:
- praw_fucked_up()
- else:
- return False
- def set_link_flair(submission, flair_text, flair_css):
- while True:
- try:
- submission.mod.flair(css_class=flair_css, text=flair_text)
- return
- except prawcore.PrawcoreException:
- praw_fucked_up()
- def add_approved(name):
- while True:
- try:
- tbp.contributor.add(name)
- return True
- except praw.exceptions.APIException as e:
- if e.error_type == "USER_DOESNT_EXIST":
- drop_user(name)
- return False
- except prawcore.PrawcoreException:
- praw_fucked_up()
- def del_approved(name):
- while True:
- try:
- tbp.contributor.remove(name)
- return True
- except praw.exceptions.APIException as e:
- if e.error_type == "USER_DOESNT_EXIST":
- drop_user(name)
- return False
- except prawcore.PrawcoreException:
- praw_fucked_up()
- #Shit tests
- def scrabble_get_draw(scrabble):
- BURN_RATE = 3
- VOWELS = ['u', 'a', 'i', 'o', 'e']
- if len(scrabble["safe_letters"]) < BURN_RATE:
- return False
- new_len = len(scrabble["safe_letters"]) + BURN_RATE
- letter = random.choice(scrabble["safe_letters"])
- scrabble["safe_letters"].remove(letter)
- scrabble["banned_letters"].append(letter)
- if letter not in VOWELS:
- while len(scrabble["safe_letters"]) < new_len:
- letter = random.choice(scrabble["safe_letters"])
- if letter not in VOWELS:
- scrabble["safe_letters"].remove(letter)
- scrabble["banned_letters"].append(letter)
- scrabble["banned_letters"] = sorted(scrabble["banned_letters"])
- return scrabble
- def scrabble_test(user, comment):
- # Start the game if the user isn't already playing scrabble
- if "scrabble" not in user.data["comment_eater"].keys():
- scrabble = {"start": datetime.now(), "banned_letters": [], "safe_letters": string.ascii_lowercase, "eaten": []}
- scrabble_get_draw(scrabble)
- reply = comment.reply("As an unmutual member of our community, you've been chosen for a shit test:\n\n Your comments are no longer allowed to contain the letters: {0}.\n\n If you manage to run out of letters within a week, you will be hard nexted.\n\n**Good luck!**\n\n*NOTE: This is an automated action, but this account is also attached to a human operator if you have any concerns. We appreciate your continued business!*".format(scrabble["letters"]))
- reply.mod.distinguish()
- log.info("User: {0} now playing scrabble! Banned letters are {1}".format(user.name, scrabble["letters"]))
- user.data["comment_eater"]["scrabble"] = scrabble
- user_data[user.name] = user.data
- return
- # Stop the game if the time limit's up
- scrabble = user.data["comment_eater"]["scrabble"]
- if datetime.now() > scrabble["start"] + timedelta(weeks=1):
- reply = comment.reply("You've successfully passed your shit test! Congratulations! As recompense you will receive a token SMV boost on r/TheBluePill.\n\n**Thanks for playing!**")
- reply.mod.distinguish()
- user.data["manual_score_bias"] += 3
- del user.data["comment_eater"]["scrabble"]
- user_data[user.name] = user.data
- return
- # Continue the game
- body = comment.body.lower()
- if comment.id not in scrabble["eaten"] and any(letter in body for letter in scrabble["banned_letters"]):
- scrabble = scrabble_get_draw(scrabble)
- if scrabble == False: # Out of letters to draw, test has been failed
- mesg = "Oh, no! It looks like you've run out of letters. This game is over.\n\n**Thanks for playing!**"
- reply = comment.reply(mesg)
- tbp.banned.add(user.name, ban_reason="AUTOBANNED: failed at scrabble", ban_message=mesg)
- set_flair(user.name, "PURGED", "purged")
- del user.data["comment_eater"]["scrabble"]
- user.data["flair"] = "purged"
- user_data[user.name] = user.data
- log.warn("User: {0} is purged (failed at scrabble)".format(user.name))
- return
- reply = comment.reply("Whoops! It looks like you used a banned letter. Your banned letters are now: {0}\n\n**Better luck next time!**".format(scrabble["letters"]))
- reply.mod.distinguish()
- if not comment.approved:
- scrabble["eaten"].append(comment.id)
- comment.mod.remove()
- log.info("Removing comment by user: {0} ({1}) [id: {2}] on submission '{3}' [id: {4}], failed scrabble test. Banned letters are now {5}".format(user.name, user.flair, comment.id, comment.submission.title, comment.submission.id, scrabble["banned_letters"]))
- user.data["comment_eater"]["scrabble"] = scrabble
- user_data[user.name] = user.data
- def brevity_test(user, comment):
- if "brevity" not in user.data["comment_eater"].keys():
- brevity = {"start": datetime.now(), "length": 160, "eaten": []}
- #MESSAGE USER ABOUT TEST
- user.data["comment_eater"]["brevity"] = brevity
- user_data[user.name] = user.data
- return
- brevity = user.data["comment_eater"]["brevity"]
- if len(comment.body) > brevity["length"]:
- brevity["length"] /= 2
- if brevity["length"] < 2:
- #BREVITY TEST FAILED MESSAGE, BAN USER (MAYBE DO SOME CLEANUP)
- return
- #WARN USER ABOUT FAILURE/ESCALATION, remove offending comment
- user_data[user.name] = user.data
- SHIT_TESTS = {"scrabble": scrabble_test, "brevity": brevity_test}
- class Tracker:
- SCAN_INTERVAL = timedelta(minutes=10)
- SCAN_DEPTH = 8
- def __init__(self, name, new_user=True):
- self.name = name
- if new_user:
- self.data = { "name": name,
- "scan_count": 0,
- "last_seen": None,
- "next_scan": None,
- "smv": 0,
- "flair": None,
- "good_score": [],
- "bad_score": [],
- "manual_score_bias": 0,
- "best_post": { "score": 0, "id": None },
- "worst_post": { "score": 0, "id": None },
- "comment_eater": {}
- }
- self.update()
- log.info("Adding new user: {0}".format(name))
- else:
- self.data = user_data[name]
- # Some simple helper functions
- @property
- def good_score(self):
- if len(self.data["good_score"]) > 0:
- return mean(self.data["good_score"]) + self.data["manual_score_bias"]
- else:
- log.warn("User: {0} has no tracked good points?".format(self.name))
- return 0
- @property
- def bad_score(self):
- if len(self.data["bad_score"]) > 0:
- return mean(self.data["bad_score"])
- else:
- log.warn("User: {0} has no tracked bad points?".format(self.name))
- return 0
- @property
- def score(self):
- return self.good_score + self.bad_score
- def update(self):
- self.data["last_seen"] = datetime.now()
- try:
- if self.data["last_seen"] < self.data["next_scan"]:
- log.debug("Skipping user: {0} (next scan on {1:%Y/%m/%d %I:%M:%S%p})".format(self.name, self.data["next_scan"]))
- return True
- except TypeError:
- pass
- good, bad, good_count, bad_count = 0, 0, 0, 0
- try:
- for comment in reddit.redditor(self.name).comments.new(limit=100):
- sub = comment.subreddit.display_name.lower()
- karma = comment.score - 1
- if sub == "thebluepill":
- good += karma
- good_count += 1
- try:
- if karma > self.data["best_post"]["score"]:
- self.data["best_post"] = {"score": karma, "id": comment.id}
- except TypeError:
- self.data["best_post"] = {"score": karma, "id": comment.id}
- try:
- if karma < self.data["worst_post"]["score"]:
- self.data["worst_post"] = {"score": karma, "id": comment.id}
- except TypeError:
- self.data["worst_post"] = {"score": karma, "id": comment.id}
- elif sub in NICE_LIST:
- good += karma / 2
- elif sub in NAUGHTY_LIST and karma > 1:
- bad -= karma
- bad_count += 1
- if good_count > 0:
- good /= good_count
- good *= good_count / 10 # Every 20 posts in r/TheBluePill doubles your score
- if bad_count > 0:
- bad /= bad_count
- bad *= bad_count / 5 # Every 10 posts in NAUGHTY_LIST doubles your bad score
- # Are they vexatious? (Of course they are!)
- if self.data["flair"] != "vexatious" and bad_count > 10 and abs(bad) > good:
- log.info("User: {0} is vexatious ({1} posts in NAUGHTY_LIST)".format(self.name, bad_count))
- set_flair(self.name, "VEXATIOUS LITIGANT", "vexatious")
- self.data["flair"] = "vexatious"
- self.data["good_score"].insert(0, good)
- if len(self.data["good_score"]) > Tracker.SCAN_DEPTH:
- self.data["good_score"].pop()
- log.debug("Dropping oldest good karma sample for user: {0}".format(self.name))
- self.data["bad_score"].insert(0, bad)
- if len(self.data["bad_score"]) > Tracker.SCAN_DEPTH:
- self.data["bad_score"].pop()
- log.debug("Dropping oldest bad karma sample for user: {0}".format(self.name))
- self.data["scan_count"] += 1
- self.data["next_scan"] = self.data["last_seen"] + Tracker.SCAN_INTERVAL
- log.info("Scanned user: {0} (scanned {1} times), good_karma: {2:.2f} ({3} comments), bad_karma: {4:.2f} ({5} comments)".format(self.name, self.data["scan_count"], good, good_count, bad, bad_count))
- user_data[self.name] = self.data
- return True
- except (prawcore.exceptions.NotFound, prawcore.exceptions.Forbidden):
- return False
- def update_flairs():
- log.info("Recalculating SMV")
- i = 0
- rank_count = 0
- rank_lowest = 0
- rank_highest = 0
- last_smv = 0
- total = len(users)
- # I'm so fuckin' dumb, we should've been doing it this way from the beginning
- # (grabbing the entire lists of purged/approved users at the start and checking locally instead of asking Reddit if individual users are purged/approved)
- purged_users = list(tbp.banned(limit=None))
- approved_users = list(tbp.contributor(limit=None))
- log.info("TBP has {0} purged users and {1} approved users ({2} active users tracked)".format(len(purged_users), len(approved_users), total))
- for user in sorted(users.values(), key=lambda user: user.score):
- dirty = False
- flair = user.data["flair"]
- # Check if user should be marked/unmarked as purged
- if flair != "purged" and user.name in purged_users:
- log.info("Marking user: {0} purged".format(user.name))
- set_flair(user.name, "PURGED", "purged")
- flair = "purged"
- user.data["flair"] = "purged"
- dirty = True
- elif flair == "purged" and user.name not in purged_users:
- log.info("User: {0} was unbanned, correcting flair".format(user.name))
- set_flair(user.name, "", "")
- flair = None
- user.data["flair"] = None
- dirty = True
- # Calculate SMV
- i += 1
- smv = ceil((i / total) * 10)
- if smv > last_smv:
- if not last_smv == 0:
- log.info("{0} Users at rank hb{1} (lowest: {2} at {3:.2f}, highest: {4} {5:.2f}), now processing rank hb{6}".format(rank_count, last_smv, rank_lowest_user, rank_lowest, rank_highest_user, rank_highest, smv))
- last_smv = smv
- rank_count = 0
- rank_lowest = user.score
- rank_lowest_user = user.name
- rank_count += 1
- rank_highest = user.score
- rank_highest_user = user.name
- if not flair:
- log.debug("No cached flair for user: {0}, checking Reddit".format(user.name))
- flair = get_flair(user.name)
- if flair == False:
- drop_user(user.name)
- continue
- if flair != None: # Don't update tracked flair if user isn't wearing a flair
- user.data["flair"] = flair
- log.debug("Reddit says user: {0}'s flair is {1}".format(user.name, flair))
- dirty = True
- log.debug("User: {0}, SMV: {1}, score: {2:.2f} (current flair {3})".format(user.name, smv, user.score, flair))
- # Update flairs
- if flair in IMMUTABLE_FLAIRS:
- if "flair" == "vexatious" and smv > 2 and user.good_score > abs(user.bad_score):
- log.warn("Should user: {0} be vexatious? SMV: {1}, good_score: {2:.2f}, bad_score: {3:.2f}".format(user.name, smv, user.good_karma, user.bad_karma))
- else:
- log.debug("Not changing user: {0} (immutable flair {1})".format(user.name, flair))
- elif flair != "hb{0}".format(smv):
- flair = "hb{0}".format(smv)
- user.data["flair"] = flair
- user.data["smv"] = smv
- if set_flair(user.name, "Hβ{0}".format(smv), "hb{0}".format(smv)) == False:
- drop_user(user.name)
- log.info("Updating user: {0} flair to hb{1}".format(user.name, smv))
- dirty = True
- else:
- log.debug("User: {0} still an Hβ{1}".format(user.name, smv))
- # Add/remove approved contributors
- if ((smv > 7 or flair in GOOD_FLAIRS) and flair not in BAD_FLAIRS) and user.name not in approved_users:
- log.debug("Adding approved contributor: {0}".format(user.name))
- add_approved(user.name)
- elif ((smv < 4 or flair in BAD_FLAIRS) and flair not in GOOD_FLAIRS) and user.name in approved_users:
- del_approved(user.name)
- log.info("Removing approved contributor: {0}".format(user.name))
- if dirty:
- user_data[user.name] = user.data
- log.info("{0} Users at rank hb{1} (lowest: {2} at {3:.2f}, highest: {4} {5:.2f})".format(rank_count, last_smv, rank_lowest_user, rank_lowest, rank_highest_user, rank_highest))
- BETA_TESTERS = "FailAShitTestToday"
- def botloop(first_flair_update=datetime.now()):
- DEAD_USER_INTERVAL = timedelta(days=1)
- FLAIR_UPDATE_INTERVAL = timedelta(minutes=15)
- next_flair_update = first_flair_update
- next_dead_user_update = first_flair_update + DEAD_USER_INTERVAL
- try:
- seen_comments = global_data["seen_comments"]
- except KeyError:
- seen_comments = []
- # Loop over comment stream indefinitely
- for comment in tbp.stream.comments():
- if datetime.now() > next_dead_user_update:
- log.info("Dropping dead users")
- user_count = len(users)
- dead_user_count = 0
- for user in list(users.values()):
- if not is_user_valid(user.name):
- dead_user_count += 1
- drop_user(user.name)
- next_dead_user_update = datetime.now() + DEAD_USER_INTERVAL
- log.info("Dead user scan complete: {0} of {1} tracked users dropped (next update due on/after: {2:%Y/%m/%d %I:%M:%S%p})".format(dead_user_count, user_count, next_dead_user_update))
- if datetime.now() > next_flair_update:
- update_flairs()
- next_flair_update = datetime.now() + FLAIR_UPDATE_INTERVAL
- log.debug("Flair update complete (next update due on/after: {0:%Y/%m/%d %I:%M:%S%p})".format(next_flair_update))
- # Don't bother processing the comment if it's already been looked at
- if comment.id in seen_comments:
- log.debug("Skipping comment '{0}' on submission '{1}' [id: {2}], already seen.".format(comment.id, comment.submission.title, comment.submission.id))
- continue
- # Grab submission link flair, assign one randomly if blank
- threat_level = comment.submission.link_flair_css_class
- if threat_level == None:
- if randint(0,1) == 0: # 50-50 shot of getting "Severe"
- threat_selector = 0
- else:
- threat_selector = randint(1, len(TODAYS_THREAT_LEVEL) - 3) # Won't randomly assign the last two link flairs
- threat_level = list(TODAYS_THREAT_LEVEL.keys())[threat_selector]
- set_link_flair(comment.submission, TODAYS_THREAT_LEVEL[threat_level], threat_level)
- log.info("Set threat level for submission '{0}' [id: {1}] to {2}".format(comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
- else:
- log.debug("Threat level for submission '{0}' [id: {1}] is {2})".format(comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
- # Skip over comments from deleted users
- if comment.author:
- dirty = False
- name = comment.author.name
- author_flair = comment.author_flair_css_class
- # Update tracker
- if not name in users.keys():
- users[name] = Tracker(name)
- else:
- if not users[name].update():
- drop_user(name)
- continue
- # Update tracked flair if comment flair doesn't match tracked flair
- user_flair = users[name].data["flair"]
- if author_flair and user_flair != author_flair:
- dirty = True
- log.info("Updating tracked flair for user: {0} -- was {1}, now {2}".format(name, user_flair, author_flair))
- user_flair = author_flair
- users[name].data["flair"] = user_flair
- # Handle unflaired users and their comments
- if not author_flair and users[name].data["scan_count"] > 3:
- log.debug("User: {0} should have flair: {1}, but comment flair is {2}".format(name, user_flair, author_flair))
- if "unflaired" not in users[name].data["comment_eater"].keys():
- users[name].data["comment_eater"]["unflaired"] = {"start":datetime.now(), "eaten": []}
- reply = comment.reply("Apologies for the inconvenience, but user flairs are mandatory on /r/TheBluePill. Please enable your user flair at your earliest convenience:\n\n- If you're on mobile Reddit, you'll need to first flip back to the desktop site (Burger Menu -> Desktop Site), then:\n- If you're on classic Reddit, you'll need to disable this Subreddit's custom CSS (preferences -> display options -> allow subreddits to show me custom themes)\n- If you're using an app to access Reddit, check the app's documentation, but you'll probably need to access the desktop site from a browser.\n\nYour comments will only be automatically removed if your SMV is too low, or the comment has negative karma. Once you're properly flaired again, these automatically removed comments will be restored.\n\nThanks for choosing r/TheBluePill!\n\netc. etc.,\n\n-Management\n\n*NOTE: This is an automated action, but this account is also attached to a human operator if you have any concerns. We appreciate your continued business!*")
- reply.mod.distinguish()
- log.info("Warned user: {0}, flairs are mandatory.".format(name))
- dirty = True
- else:
- unflaired = users[name].data["comment_eater"]["unflaired"]
- if name in FLAIR_EXEMPT:
- log.info("Not removing comment {0} by unflaired user: {1} (exempt)".format(comment.id, name))
- elif datetime.now() < unflaired["start"] + timedelta(minutes=15):
- log.info("Not removing comment {0} by unflaired user: {1} (still in grace period after warning)".format(comment.id, name))
- elif users[name].data["smv"] > 3 or comment.score > 0 or user_flair in GOOD_FLAIRS:
- log.info("Not removing comment {0} by unflaired user: {1} (too high value)".format(comment.id, name))
- elif comment.approved:
- log.info("Not removing comment {0} by unflaired user: {1} (manually approved)".format(comment.id, name))
- elif comment.id in unflaired["eaten"] or comment.removed:
- log.info("Not removing comment {0} by unflaired user: {1} (already removed)".format(comment.id, name))
- else:
- unflaired["eaten"].append(comment.id)
- comment.mod.remove()
- log.info("Removed comment by user {0} ({1}) [id: {2}] on submission {3} [id: {4}], not wearing flair!".format(name, author_flair, comment.id, comment.submission.title, comment.submission.id))
- dirty = True
- elif author_flair and "unflaired" in users[name].data["comment_eater"].keys() and len(users[name].data["comment_eater"]["unflaired"]["eaten"]) > 0:
- for comment in users[name].data["comment_eater"]["unflaired"]["eaten"]:
- reddit.comment(id=comment).approve()
- log.info("Restoring comment {0} for user: {1} (is wearing flair now)".format(comment, name))
- users[name].data["comment_eater"]["unflaired"]["eaten"] = []
- dirty = True
- # Remove comments if user's SMV is too low for submission's threat level
- if author_flair in THREAT_MATRIX[threat_level] and name != comment.submission.author and not comment.approved:
- dirty = True
- if "low_smv" not in users[name].data["comment_eater"].keys():
- users[name].data["comment_eater"]["low_smv"] = {"eaten": [comment.id]}
- else:
- users[name].data["comment_eater"]["low_smv"]["eaten"].append(comment.id)
- comment.mod.remove()
- log.info("Removing comment by user: {0} ({1}) [id: {2}] on submission '{3}' [id: {4}] ({5}), SMV too low!".format(name, author_flair, comment.id, comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
- #Shit tests
- if name in BETA_TESTERS:
- shit_test = [shit_test for eater in users[name].data["comment_eater"] if eater in SHIT_TESTS.keys()]
- if shit_test:
- if len(shit_test) > 1: # There should only ever be one active shit test
- log.error("Multiple shit tests assigned to user: {0}, only processing the first")
- shit_test = shit_test[0]
- log.debug("Shit testing comment by user: {0} ({1}) [id: {2}] on submission '{3}' [id: {4}]".format(name, author_flair, comment.id, comment.submission.title, comment.submission.id))
- SHIT_TESTS[shit_test](users[name], comment)
- else:
- scrabble_test(users[name], comment)
- if dirty:
- user_data[name] = users[name].data
- else:
- log.debug("Skipping comment {0} on submission '{1}' [id: {2}], author no longer exists".format(comment.id, comment.submission.title, comment.submission.id))
- seen_comments.insert(0, comment.id)
- if len(seen_comments) > 100:
- seen_comments.pop()
- global_data["seen_comments"] = seen_comments
- with shelve.open("user_data") as user_data, shelve.open("global_data") as global_data:
- users = {}
- for user in user_data.keys():
- users[user] = Tracker(user, False)
- try:
- botloop()
- except prawcore.PrawcoreException:
- praw_fucked_up()
- botloop()
- except KeyboardInterrupt:
- log.info("VirtuteTron going off-line")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement