Advertisement
CrashARuntimeToday

VirtueTron9000 v0.3.9_pre5

Jul 10th, 2018
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 24.25 KB | None | 0 0
  1. #!/usr/bin/python
  2. LICENCE = "WTFPL", "http://www.wtfpl.net/about/"
  3. VERSION = "v0.3.9_pre5"
  4.  
  5. import logging
  6. import pickle
  7. import praw
  8. import prawcore
  9. import random
  10. import shelve
  11. import string
  12. import sys
  13.  
  14. from datetime import datetime, timedelta
  15. from math import ceil
  16. from time import sleep
  17. from random import randint
  18. from statistics import mean
  19.  
  20. log = logging.getLogger("VirtueTron")
  21. log.setLevel(logging.DEBUG)
  22. formatter = logging.Formatter(fmt="%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y/%m/%d %I:%M:%S%p")
  23. file_log = logging.FileHandler(filename="VirtueTron.log", mode="a")
  24. file_log.setLevel(logging.INFO)
  25. file_log.setFormatter(formatter)
  26. log.addHandler(file_log)
  27. console_log = logging.StreamHandler(stream=sys.stdout)
  28. console_log.setLevel(logging.DEBUG)
  29. console_log.setFormatter(formatter)
  30. log.addHandler(console_log)
  31.  
  32. log.info("VirtueTron® 9000™ {0} © CrashARuntimeToday@outlook.com".format(VERSION))
  33.  
  34. credentials = pickle.load(open("credentials.pickle", "rb")) # { client_id: "VirtueTron9000", client_secret: "🤖🤡🍆💯™", username: "SignalAVirtueToday", password: "https://youtu.be/RCVJ7bujnSc"}
  35. credentials["user_agent"] = "VirtueTron 9000 {0}".format(VERSION)
  36. reddit = praw.Reddit(**credentials)
  37. tbp = reddit.subreddit("TheBluePill")
  38. log.info("Hello, Reddit!")
  39.  
  40. NICE_LIST = []
  41. NAUGHTY_LIST = ["theredpill", "marriedredpill", "mgtow", "braincels", "asktrp", "askmrp", "redpillwomen", "redpillwives", "cringeanarchy", "the_donald", "rpchristians", "pussypassdenied", "mensrights", "milliondollarextreme", "4chan", "whereareallthegoodmen"]
  42. IMMUTABLE_FLAIRS = ["vanguard", "vexatious", "endorsedflair", "alpha", "betaasfuck", "feeemale", "purged"]
  43. BAD_FLAIRS = ["purged", "vexatious"]
  44. GOOD_FLAIRS = ["endorsedflair", "vanguard", "alpha", "betaasfuck", "feeemale"]
  45. TODAYS_THREAT_LEVEL = {"tlsevere":"Severe", "tlhigh":"High", "tlelevated":"Elevated", "tlguarded":"Guarded", "tllow":"Low"}
  46. THREAT_MATRIX = {"tllow": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,7)],
  47.                  "tlguarded": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,5)],
  48.                  "tlelevated": BAD_FLAIRS + ["hb{0}".format(x) for x in range(1,3)],
  49.                  "tlhigh": BAD_FLAIRS,
  50.                  "tlsevere": []}
  51. FLAIR_EXEMPT = []
  52.  
  53. # Helper functions
  54. delay = 2
  55. last_fuckup = None
  56. def praw_fucked_up():
  57.     global delay
  58.     global last_fuckup
  59.     log.warning("Reddit API error: waiting {0} seconds".format(delay))
  60.     try:
  61.         if delay > 128 or datetime.now() > last_fuckup + timedelta(minutes=2): delay = 2
  62.     except TypeError:
  63.         delay = 2
  64.     sleep(delay)
  65.     delay *= 2
  66.     last_fuckup = datetime.now()
  67.  
  68. def is_user_valid(name):
  69.     try:
  70.         for comment in reddit.redditor(name).new(limit=1):
  71.             log.debug("User: {0} still exists (most recent comment id is {1})".format(name, comment.id))
  72.         return True
  73.     except (prawcore.exceptions.NotFound, prawcore.exceptions.Forbidden):
  74.         return False
  75.  
  76. def drop_user(name):
  77.     log.info("User: {0} is deleted or suspended, dropping from userlist".format(name))
  78.     del users[name]
  79.     del user_data[name]
  80.  
  81. def get_flair(name):
  82.     if is_user_valid(name):
  83.         while True:
  84.             try:
  85.                 for flair in tbp.flair(redditor=name):
  86.                     return flair["flair_css_class"]
  87.             except prawcore.PrawcoreException:
  88.                 praw_fucked_up()
  89.     else:
  90.         return False
  91.  
  92. def set_flair(name, flair_text, flair_css):
  93.     if is_user_valid(name):
  94.         while True:
  95.             try:
  96.                 tbp.flair.set(name, flair_text, flair_css)
  97.                 return True
  98.             except prawcore.PrawcoreException:
  99.                 praw_fucked_up()
  100.     else:
  101.         return False
  102.  
  103. def set_link_flair(submission, flair_text, flair_css):
  104.     while True:
  105.         try:
  106.             submission.mod.flair(css_class=flair_css, text=flair_text)
  107.         except prawcore.PrawcoreException:
  108.             praw_fucked_up()
  109.  
  110. def add_approved(name):
  111.     while True:
  112.         try:
  113.             tbp.contributor.add(name)
  114.         except praw.exceptions.APIException as e:
  115.             if e.error_type == "USER_DOESNT_EXIST":
  116.                 drop_user(name)
  117.         except prawcore.PrawcoreException:
  118.             praw_fucked_up()
  119.  
  120. def del_approved(name):
  121.     while True:
  122.         try:
  123.             tbp.contributor.remove(name)
  124.         except praw.exceptions.APIException as e:
  125.             if e.error_type == "USER_DOESNT_EXIST":
  126.                 drop_user(name)
  127.         except prawcore.PrawcoreException:
  128.             praw_fucked_up()
  129.  
  130. #Shit tests
  131. def scrabble_test(user, comment):
  132.     if "scrabble" not in user.data["comment_eater"].keys():
  133.         scrabble = {"start": datetime.now(), "letters": [], "eaten": []}
  134.         while len(scrabble["letters"]) < 3:
  135.             letter = random.choice(string.ascii_lowercase)
  136.             if letter not in scrabble["letters"]:
  137.                 scrabble["letters"].append(letter)
  138.         scrabble["letters"] = sorted(scrabble["letters"])
  139.         reply = comment.reply("You have been identified as an unmutual member of our community. As such, you've been chosen for a shit test:\n\n Your comments are no longer allowed to contain the letters: {0}.\n\n This shit test will be failed (and you will be banned) if you manage to run out of letters within a week.\n\n**Good luck!**".format(scrabble["letters"]))
  140.         reply.mod.distinguish()
  141.         log.info("User: {0} now playing scrabble! Banned letters are {1}".format(user.name, scrabble["letters"]))
  142.         user.data["comment_eater"]["scrabble"] = scrabble
  143.         user_data[user.name] = user.data
  144.         return
  145.     scrabble = user.data["comment_eater"]["scrabble"]
  146.     body = comment.body.lower()
  147.     post_failed = False
  148.     for letter in scrabble["letters"]:
  149.         if letter in body:
  150.             new_length = len(scrabble["letters"]) + 3
  151.             post_failed = True
  152.             while len(scrabble["letters"]) < new_length:
  153.                 bonus_letter = random.choice(string.ascii_lowercase)
  154.                 if bonus_letter not in scrabble["letters"]:
  155.                     scrabble["letters"].append(bonus_letter)
  156.             scrabble["letters"] = sorted(scrabble["letters"])
  157.             break
  158.     if len(scrabble["letters"]) > 25:
  159.         mesg = "Oh, no! It looks like you've run out of letters. You have failed your shit test, and will be hard nexted.\n\n**Thanks for playing!**"
  160.         reply = comment.reply(mesg)
  161.         tbp.banned.add(name, ban_reason="AUTOBANNED: failed at scrabble", ban_message=mesg)
  162.         set_flair(name, "PURGED", "purged")
  163.         del user.data["comment_eater"]["scrabble"]
  164.         user_data[user.name] = user.data
  165.         return
  166.     elif post_failed and comment.id not in scrabble["eaten"]:
  167.         reply = comment.reply("Whoops! It looks like you used a forbidden letter. Your banned letters are now: {0}\n\n**Good luck!**".format(scrabble["letters"]))
  168.         reply.mod.distinguish()
  169.         if not comment.approved or comment.id in scrabble["eaten"]:
  170.             scrabble["eaten"].append(comment.id)
  171.             comment.mod.remove()
  172.             log.info("User: {0} failed scrabble test. Banned letters are now {1}".format(user.name, scrabble["letters"]))
  173.     else:
  174.         if datetime.now() > scrabble["start"] + timedelta(weeks=1):
  175.             replay = 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!**")
  176.             reply.mod.distinguish()
  177.             del user.data["comment_eater"]["scrabble"]
  178.     user_data[user.name] = user.data
  179.  
  180.  
  181. def brevity_test(user, comment):
  182.     if "brevity" not in user.data["comment_eater"].keys():
  183.         brevity = {"start": datetime.now(), "length": 160, "eaten": []}
  184.         #MESSAGE USER ABOUT TEST
  185.         user.data["comment_eater"]["brevity"] = brevity
  186.         user_data[user.name] = user.data
  187.         return
  188.     brevity = user.data["comment_eater"]["brevity"]
  189.     if len(comment.body) > brevity["length"]:
  190.         brevity["length"] /= 2
  191.         if brevity["length"] < 2:
  192.             #BREVITY TEST FAILED MESSAGE, BAN USER (MAYBE DO SOME CLEANUP)
  193.             return
  194.         #WARN USER ABOUT FAILURE/ESCALATION, remove offending comment
  195.         user_data[user.name] = user.data
  196. SHIT_TESTS = {"scrabble": scrabble_test, "brevity": brevity_test}    
  197.  
  198. class Tracker:
  199.     SCAN_INTERVAL = timedelta(minutes=5)
  200.     # For some reason VSCode eats the indents on blank lines and this gives the python interpreter fits when I copy/paste, so empty comments at the correct indent level it is!
  201.     def __init__(self, name, new_user=True):
  202.         self.name = name
  203.         if new_user:
  204.             self.data = {   "name": name,
  205.                             "scan_count": 0,
  206.                             "last_seen": None,
  207.                             "next_scan": None,
  208.                             "smv": 0,
  209.                             "flair": None,
  210.                             "good_score": [],
  211.                             "bad_score": [],
  212.                             "manual_score_bias": 0,
  213.                             "best_post": { "score": 0, "id": None },
  214.                             "worst_post": { "score": 0, "id": None },
  215.                             "comment_eater": {}
  216.                         }  
  217.             self.update()
  218.             log.info("Adding new user: {0}".format(name))
  219.         else:
  220.             self.data = user_data[name]
  221.     # Some simple helper functions
  222.     @property
  223.     def good_score(self):
  224.         if len(self.data["good_score"]) > 0:
  225.             return mean(self.data["good_score"]) + self.data["manual_score_bias"]
  226.         else:
  227.             #log.warn("User: {0} has no tracked good points?".format(self.name))
  228.             return 0
  229.     @property
  230.     def bad_score(self):
  231.         if len(self.data["bad_score"]) > 0:
  232.             return mean(self.data["bad_score"])
  233.         else:
  234.             #log.warn("User: {0} has no tracked bad points?".format(self.name))
  235.             return 0
  236.     @property
  237.     def score(self):
  238.         return self.good_score + self.bad_score
  239.     def update(self):
  240.         self.data["last_seen"] = datetime.now()
  241.         try:
  242.             if self.data["last_seen"] < self.data["next_scan"]:
  243.                 log.debug("Skipping user: {0} (next scan on {1:%Y/%m/%d %I:%M:%S%p})".format(self.name, self.data["next_scan"]))
  244.                 return True
  245.         except TypeError:
  246.             pass
  247.         good, bad, good_count, bad_count = 0, 0, 0, 0
  248.         try:
  249.             for comment in reddit.redditor(self.name).comments.new(limit=100):
  250.                 sub = comment.subreddit.display_name.lower()
  251.                 karma = comment.score - 1
  252.                 if sub == "thebluepill":
  253.                     good += karma
  254.                     good_count += 1
  255.                     try:
  256.                         if karma > self.data["best_post"]["score"]:
  257.                             self.data["best_post"] = {"score": karma, "id": comment.id}
  258.                     except TypeError:
  259.                         self.data["best_post"] = {"score": karma, "id": comment.id}
  260.                     try:
  261.                         if karma < self.data["worst_post"]["score"]:
  262.                             self.data["worst_post"] = {"score": karma, "id": comment.id}
  263.                     except TypeError:
  264.                         self.data["worst_post"] = {"score": karma, "id": comment.id}
  265.                 elif sub in NICE_LIST:
  266.                     good += karma / 2
  267.                 elif sub in NAUGHTY_LIST and karma > 1:
  268.                     bad -= karma
  269.                     bad_count += 1
  270.             if good_count > 0:
  271.                 good /= good_count
  272.                 good *= good_count / 10 # Every 20 posts in r/TheBluePill doubles your good karma
  273.             if bad_count > 0:
  274.                 bad /= bad_count
  275.                 bad *= bad_count / 5 # Every 10 posts in NAUGHTY_LIST doubles your bad karma
  276.                 # Are they vexatious? (Of course they are!)
  277.                 if self.data["flair"] != "vexatious" and bad_count > 10 and abs(bad) > good:
  278.                     log.info("User: {0} is vexatious ({1} posts in NAUGHTY_LIST)".format(self.name, bad_count))
  279.                     set_flair(self.name, "VEXATIOUS LITIGANT", "vexatious")
  280.                     self.data["flair"] = "vexatious"
  281.             self.data["good_score"].insert(0, good)
  282.             if len(self.data["good_score"]) > 8:
  283.                 self.data["good_score"].pop()
  284.                 log.debug("Dropping oldest good karma sample for user: {0}".format(self.name))
  285.             self.data["bad_score"].insert(0, bad)
  286.             if len(self.data["bad_score"]) > 8:
  287.                 self.data["bad_score"].pop()
  288.                 log.debug("Dropping oldest bad karma sample for user: {0}".format(self.name))
  289.             self.data["scan_count"] += 1
  290.             self.data["next_scan"] = self.data["last_seen"] + Tracker.SCAN_INTERVAL
  291.             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))
  292.             user_data[self.name] = self.data
  293.             return True
  294.         except (prawcore.exceptions.NotFound, prawcore.exceptions.Forbidden):
  295.             return False
  296.  
  297. def update_flairs():
  298.     log.info("Recalculating SMV")
  299.     i = 0
  300.     total = len(users)
  301.     # I'm so fuckin' dumb, we should've been doing it this way from the beginning
  302.     purged_users = list(tbp.banned(limit=None))
  303.     approved_users = list(tbp.contributor(limit=None))
  304.     #
  305.     for user in sorted(users.values(), key=lambda user: user.score):
  306.         dirty = False
  307.         flair = user.data["flair"]
  308.         # Check if user should be marked/unmarked as purged
  309.         if flair != "purged" and user.name in purged_users:
  310.             log.info("Marking user: {0} purged".format(user.name))
  311.             set_flair(user.name, "PURGED", "purged")
  312.             flair = "purged"
  313.             user.data["flair"] = "purged"
  314.             dirty = True
  315.         elif flair == "purged" and user.name not in purged_users:
  316.             log.info("User: {0} was unbanned, correcting flair".format(user.name))
  317.             set_flair(user.name, "", "")
  318.             flair = None
  319.             user.data["flair"] = None
  320.             dirty = True
  321.         # Calculate SMV
  322.         i += 1
  323.         smv = ceil((i / total) * 10)
  324.         if not flair:
  325.             log.debug("No cached flair for user: {0}, checking Reddit".format(user.name))
  326.             flair = get_flair(user.name)
  327.             if flair == False:
  328.                 drop_user(user.name)
  329.                 continue
  330.             user.data["flair"] = flair
  331.             log.debug("Reddit says user: {0}'s flair is {1}".format(user.name, flair))
  332.             dirty = True
  333.         log.debug("User: {0}, SMV: {1}, score: {2:.2f} (current flair {3})".format(user.name, smv, user.score, flair))
  334.         # Update flairs
  335.         if flair in IMMUTABLE_FLAIRS:
  336.             if "flair" == "vexatious" and smv > 2 and user.good_score > abs(user.bad_score):
  337.                 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))
  338.             else:
  339.                 log.debug("Not changing user: {0} (immutable flair {1})".format(user.name, flair))
  340.         elif flair != "hb{0}".format(smv):
  341.             flair = "hb{0}".format(smv)
  342.             user.data["flair"] = flair
  343.             user.data["smv"] = smv
  344.             if set_flair(user.name, "Hβ{0}".format(smv), "hb{0}".format(smv)) == False:
  345.                 drop_user(user.name)
  346.             log.info("Updating user: {0} flair to hb{1}".format(user.name, smv))
  347.             dirty = True
  348.         else:
  349.             log.debug("User: {0} still an Hβ{1}".format(user.name, smv))
  350.         # Add/remove approved contributors
  351.         if ((smv > 7 or flair in GOOD_FLAIRS) and flair not in BAD_FLAIRS) and user.name not in approved_users:
  352.             log.debug("Adding approved contributor: {0}".format(user.name))
  353.             add_approved(user.name)
  354.         elif ((smv < 4 or flair in BAD_FLAIRS) and flair not in GOOD_FLAIRS) and user.name in approved_users:
  355.             del_approved(user.name)
  356.             log.info("Removing approved contributor: {0}".format(user.name))
  357.         if dirty:
  358.             user_data[user.name] = user.data
  359.  
  360. BETA_TESTERS = "FailAShitTestToday"
  361. def botloop(first_update=datetime.now()):
  362.     DEAD_USER_INTERVAL = timedelta(days=1)
  363.     FLAIR_UPDATE_INTERVAL = timedelta(minutes=15)
  364.     next_flair_update = first_update
  365.     next_dead_user_update = first_update + DEAD_USER_INTERVAL
  366.     # Loop over comment stream indefinitely
  367.     for comment in tbp.stream.comments():
  368.         if datetime.now() > next_dead_user_update:
  369.             for user in users.values():
  370.                 if not is_user_valid(user.name):
  371.                     drop_user(user.name)
  372.         if datetime.now() > next_flair_update:
  373.             update_flairs()
  374.             next_flair_update = datetime.now() + FLAIR_UPDATE_INTERVAL
  375.             log.debug("Flair update complete (next update due on/after: {0:%Y/%m/%d %I:%M:%S%p})".format(next_flair_update))
  376.         # Grab submission link flair, assign one randomly if blank
  377.         threat_level = comment.submission.link_flair_css_class
  378.         if threat_level == None:
  379.             if randint(0,1) == 0: # 50-50 shot of getting "Severe"
  380.                 threat_selector = 0
  381.             else:
  382.                 threat_selector = randint(1, len(TODAYS_THREAT_LEVEL) - 3) # Won't randomly assign the last two link flairs
  383.             threat_level = list(TODAYS_THREAT_LEVEL.keys())[threat_selector]
  384.             set_link_flair(comment.submission, TODAYS_THREAT_LEVEL[threat_level], threat_level)
  385.             log.info("Set threat level for submission '{0}' [id: {1}] to {2}".format(comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
  386.         else:
  387.             log.debug("Threat level for submission '{0}' [id: {1}] is {2})".format(comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
  388.         # Skip over comments from deleted users
  389.         if comment.author:
  390.             dirty = False
  391.             name = comment.author.name
  392.             author_flair = comment.author_flair_css_class
  393.             # Update tracker
  394.             if not name in users.keys():
  395.                 users[name] = Tracker(name)
  396.             else:
  397.                 if not users[name].update():
  398.                     log.info("User: {0} is deleted or suspended, dropping from userlist".format(name))
  399.                     del users[name]
  400.                     del user_data[name]
  401.                     continue
  402.             # Update tracked flair if comment flair doesn't match tracked flair
  403.             user_flair = users[name].data["flair"]
  404.             if author_flair and user_flair != author_flair:
  405.                 dirty = True
  406.                 log.info("Updating tracked flair for user: {0} -- was {1}, now {2}".format(name, user_flair, author_flair))
  407.                 user_flair = author_flair
  408.                 users[name].data["flair"] = user_flair
  409.             # Handle unflaired users and their comments
  410.             if not author_flair and users[name].data["scan_count"] > 3:
  411.                 dirty = True
  412.                 log.debug("User: {0} should have flair: {1}, but comment flair is {2}".format(name, user_flair, author_flair))
  413.                 if "unflaired" not in users[name].data["comment_eater"].keys():
  414.                     users[name].data["comment_eater"]["unflaired"] = {"start":datetime.now(), "eaten": []}
  415.                     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!*")
  416.                     reply.mod.distinguish()
  417.                     log.info("Warned user: {0}, flairs are mandatory.".format(name))
  418.                 else:
  419.                     unflaired = users[name].data["comment_eater"]["unflaired"]
  420.                     if name in FLAIR_EXEMPT:
  421.                         log.info("Not removing comment {0} by unflaired user: {1} (exempt)".format(comment.id, name))
  422.                     elif datetime.now() < unflaired["start"] + timedelta(minutes=15):
  423.                         log.info("Not removing comment {0} by unflaired user: {1} (still in grace period after warning)".format(comment.id, name))
  424.                     elif users[name].data["smv"] > 3 or comment.score > 0 or user_flair in GOOD_FLAIRS:
  425.                         log.info("Not removing comment {0} by unflaired user: {1} (too high value)".format(comment.id, name))
  426.                     elif comment.approved:
  427.                         log.info("Not removing comment {0} by unflaired user: {1} (manually approved)".format(comment.id, name))
  428.                     elif comment.id in unflaired["eaten"] or comment.removed:
  429.                         log.info("Not removing comment {0} by unflaired user: {1} (already removed)".format(comment.id, name))
  430.                     else:
  431.                         unflaired["eaten"].append(comment.id)
  432.                         comment.mod.remove()
  433.                         log.info("Removed comment {0} by unflaired user: {1}".format(comment.id, name))
  434.             elif author_flair and "unflaired" in users[name].data["comment_eater"].keys() and len(users[name].data["comment_eater"]["unflaired"]["eaten"]) > 0:
  435.                 dirty = True
  436.                 for comment in users[name].data["comment_eater"]["unflaired"]["eaten"]:
  437.                     reddit.comment(id=comment).approve()
  438.                     log.info("Restoring comment {0} for user: {1} (is wearing flair now)".format(comment, name))
  439.                 users[name].data["comment_eater"]["unflaired"]["eaten"] = []
  440.             # Remove comments if user's SMV is too low for submission's threat level
  441.             if author_flair in THREAT_MATRIX[threat_level] and name != comment.submission.author and not comment.approved:
  442.                 dirty = True
  443.                 if "low_smv" not in users[name].data["comment_eater"].keys():
  444.                     users[name].data["comment_eater"]["low_smv"] = {"eaten": [comment.id]}
  445.                 else:
  446.                     users[name].data["comment_eater"]["low_smv"]["eaten"].append(comment.id)
  447.                 comment.mod.remove()
  448.                 log.info("Removing comment by user: {0} ({1}) on submission '{2}' [id: {3}] ({4}), SMV too low!".format(name, author_flair, comment.submission.title, comment.id, TODAYS_THREAT_LEVEL[threat_level]))
  449.             if name in BETA_TESTERS:
  450.                 has_shit_test = False
  451.                 for shit_test in SHIT_TESTS.keys():
  452.                     if shit_test in users[name].data["comment_eater"]:
  453.                         SHIT_TESTS[shit_test](users[name], comment)
  454.                         has_shit_test = True
  455.                         break
  456.                 if not has_shit_test:
  457.                     scrabble_test(users[name], comment)
  458.             if dirty:
  459.                 user_data[name] = users[name].data
  460.         else:
  461.             log.debug("Skipping comment {0}, author no longer exists".format(comment.id))
  462.  
  463. with shelve.open("user_data") as user_data:
  464.     users = {}
  465.     for user in user_data.keys():
  466.         users[user] = Tracker(user, False)
  467.     try:
  468.         botloop()
  469.     except prawcore.PrawcoreException:
  470.         praw_fucked_up()
  471.         botloop()
  472.     except KeyboardInterrupt:
  473.         log.info("VirtuteTron going off-line")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement