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.2.9h"
- import logging
- import pickle
- import praw
- import prawcore
- import sys
- from datetime import datetime, timedelta
- from math import ceil
- from time import sleep
- from random import randint
- 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("Hello, Reddit!")
- log.info("VirtueTron® 9000™ {0} © CrashARuntimeToday@outlook.com".format(VERSION))
- 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")
- class Tracker:
- def __init__(self, name, good_karma=0, bad_karma=0, new_user=True, scan_count=0, next_refresh=None, is_approved=False, is_purged=False, current_flair=None, warned=None, comments_eaten=[]):
- self.name = name
- self.smv = 0
- self.scan_count = scan_count
- self.next_refresh = next_refresh
- self.is_approved = is_approved
- self.is_purged = is_purged
- self.current_flair = None
- self.warned = warned
- self.comments_eaten = comments_eaten
- if new_user:
- self.update(good_karma, bad_karma)
- else:
- self.good_karma = good_karma
- self.bad_karma = bad_karma
- def update(self, good_karma, bad_karma):
- self.good_karma = good_karma
- self.bad_karma = bad_karma
- self.scan_count += 1
- self.next_refresh = datetime.now() + timedelta(minutes=15)
- delay = 2
- last_fuckup = None
- def praw_fucked_up():
- global delay
- global last_fuckup
- log.warning("Reddit API errored: 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()
- NICE_LIST = []
- NAUGHTY_LIST = "TheRedPill", "MarriedRedPill", "ChristianRedPill", "MGTOW", "Braincels", "AskTRP", "AskMRP", "RedPillWomen", "RedPillWives", "CringeAnarchy", "The_Donald", "RPChristians", "PussyPassDenied", "MensRights", "MillionDollarExtreme"
- def calc_score(name):
- good_karma, bad_karma, good_count, bad_count = 0, 0, 0, 0
- for comment in reddit.redditor(name).comments.new(limit=100):
- sub = comment.subreddit.display_name
- karma = comment.score
- if sub == "TheBluePill":
- good_karma += karma - 1
- good_count += 1
- elif sub in NICE_LIST:
- good_karma += (karma - 1) / 2
- elif sub in NAUGHTY_LIST and karma > 1:
- bad_karma -= karma - 1
- bad_count += 1
- if good_count > 0:
- good_karma /= good_count
- if bad_count > 0:
- bad_karma /= bad_count
- if bad_count > 5 and bad_karma > good_karma and name in users.keys() and not users[name].is_purged:
- log.info("User: {0} is vexatious ({1} posts in NAUGHTY_LIST)".format(name, bad_count))
- tbp.flair.set(name, "VEXATIOUS LITIGANT", "vexatious")
- users[name].current_flair = "vexatious"
- log.info("Scanned user: {0}, good_karma: {1} ({2} comments), bad_karma: {3} ({4} comments)".format(name, good_karma, good_count, bad_karma, bad_count))
- return good_karma, bad_karma
- IMMUTABLE_FLAIRS = "vanguard", "vexatious", "endorsedflair", "alpha", "betaasfuck", "feeemale", "purged"
- def update_flairs():
- log.info("Recalculating SMV")
- i = 0
- total = len(users)
- for user in sorted(users.values(), key=lambda x: x.good_karma + x.bad_karma):
- i += 1
- user.smv = ceil((i / total) * 10)
- # Not sure why Reddit doesn't like this bit and too lazy to run Wireshark
- if user.current_flair == None:
- log.debug("No cached flair for user: {0}, checking Reddit".format(user.name))
- try:
- for flair in tbp.flair(redditor=user.name):
- user.current_flair = flair["flair_css_class"]
- except prawcore.PrawcoreException:
- praw_fucked_up()
- pass
- if user.current_flair == "purged" and not any(tbp.banned(redditor=user.name)): #Should only be needed transitionally since I fucked up and marked everyone PURGED (again!)
- log.warn("Clearing false purged flair on user: {0} you fucking idiot".format(user.name))
- user.is_purged = False
- user.current_flair = "hb5"
- tbp.flair.set(user.name, "Hβ5", "hb5")
- if not user.is_purged and any(tbp.banned(redditor=user.name)):
- if user.current_flair != "purged":
- log.info("Marking user: {0} purged".format(user.name))
- tbp.flair.set(user.name, "PURGED", "purged")
- user.current_flair = "purged"
- user.is_purged = True
- else:
- log.info("User: {0} is purged".format(user.name))
- if not user.is_purged: #Only needed for format update, since I wasn't tracking that locally before
- user.is_purged = True
- log.debug("User: {0} is purged and flaired, but not marked locally".format(user.name))
- else:
- log.debug("User: {0}, SMV: {1}, score: {2} (current flair {3})".format(user.name, user.smv, user.good_karma + user.bad_karma, user.current_flair))
- if user.current_flair in IMMUTABLE_FLAIRS:
- if user.current_flair == "vexatious" and user.smv > 2 and user.good_karma > abs(user.bad_karma):
- log.warn("Should user: {0} be vexatious? SMV: {1}, good_karma: {2}, bad_karma: {3}".format(user.name, user.smv, user.good_karma, user.bad_karma))
- else:
- log.debug("Not changing user: {0} (immutable flair {1})".format(user.name, user.current_flair))
- elif user.current_flair != "hb{0}".format(user.smv):
- log.info("Updating user: {0} flair to hb{1}".format(user.name, user.smv))
- tbp.flair.set(user.name, "Hβ{0}".format(user.smv), "hb{0}".format(user.smv))
- user.current_flair = "hb{0}".format(user.smv)
- if user.smv > 7 and not user.is_approved:
- if user.name not in tbp.contributor():
- log.info("Adding approved contributor: {0}".format(user.name))
- tbp.contributor.add(user.name)
- user.is_approved = True
- if not user.is_approved: #Only needed for format update, since I wasn't tracking that locally before
- user.is_approved = True
- log.debug("User: {0} is approved, but not marked locally".format(user.name))
- elif user.smv < 4 and user.is_approved:
- log.info("Removing approved contributor: {0}".format(user.name))
- tbp.contributor.remove(user.name)
- else:
- log.debug("User: {0} still an HB{1}".format(user.name, user.smv))
- pickle.dump(users, open("users.pickle", "wb"))
- BAD_FLAIRS = ["purged", "vexatious"]
- 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,3)],
- "tlhigh": BAD_FLAIRS,
- "tlsevere": []}
- TODAYS_THREAT_LEVEL = {"tlsevere":"Severe", "tlhigh":"High", "tlelevated":"Elevated", "tlguarded":"Guarded", "tllow":"Low"}
- def botloop(first_recalc):
- next_recalc = first_recalc
- for comment in tbp.stream.comments():
- if datetime.now() > next_recalc:
- update_flairs()
- next_recalc = datetime.now() + timedelta(minutes=15)
- log.debug("Next refresh: {0}".format(next_recalc))
- threat_level = comment.submission.link_flair_css_class
- if threat_level == None or comment.submission.link_flair_text == None or comment.submission.link_flair_text not in TODAYS_THREAT_LEVEL.values():
- threat_level = list(TODAYS_THREAT_LEVEL.keys())[randint(0, len(TODAYS_THREAT_LEVEL) - 3)] # Won't randomly assign the last two link flairs
- log.info("Setting threat level for submission '{0}' [id: {1}] to {2}".format(comment.submission.title, comment.submission.id, TODAYS_THREAT_LEVEL[threat_level]))
- comment.submission.mod.flair(css_class=threat_level, text=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]))
- if comment.author != None:
- name = comment.author.name
- author_rank = comment.author_flair_css_class
- if not name in users.keys():
- good_karma, bad_karma = calc_score(name)
- users[name] = Tracker(name, good_karma, bad_karma)
- log.info("New user: {0}".format(name))
- elif datetime.now() > users[name].next_refresh:
- good_karma, bad_karma = calc_score(name)
- users[name].update(good_karma, bad_karma)
- log.debug("User: {0} scanned {1} times".format(name, users[name].scan_count))
- else:
- log.debug("Skipping user: {0}, next refresh {1}".format(name, users[name].next_refresh))
- if users[name].current_flair != author_rank:
- log.info("Updating tracked flair for user: {0} - was {1}, now {2}".format(name, users[name].current_flair, author_rank))
- users[name].current_flair = author_rank
- if (author_rank == None or author_rank == "") and users[name].scan_count > 2:
- if bool(users[name].warned):
- # if datetime.now() > users[name].warned + timedelta(hours=1)
- if name == "FailAShitTestToday":
- users[name].comments_eaten.append(comment.id)
- comment.mod.remove()
- log.info("Removed comment {0} by unflaired user: {1}".format(comment.id, name))
- else:
- reply = comment.reply("Apologies for the inconvenience, but user flairs are mandatory on /r/TheBluePill. Please toggle your user flair back on at your earliest convenience (if you've opted out of the redesign, you'll need to disable this Subreddit's custom CSS to do so.) Your comments will be automatically filtered until you've re-enabled your flair, but the filtered comments will be restored when you're properly wearing your flair.\n\nThanks for choosing r/TheBluePill!\n\netc. etc., -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()
- users[name].warned = datetime.now()
- log.info("Warned user: {0}, flairs are mandatory.".format(name))
- else:
- if bool(users[name].warned) and len(users[name].comments_eaten) > 0:
- for comment in users[name].comments_eaten:
- reddit.comment(id=comment).mod.approve()
- log.info("Restoring comment {0} for user: {1} (is wearing flair now)")
- users[name].comments_eaten = []
- if author_rank in THREAT_MATRIX[threat_level] and name != comment.submission.author:
- if name == "FailAShitTestToday" or name == "geneticwaste43":
- comment.mod.remove()
- log.info("Removing comment by user: {0} ({1}) on submission '{2}' [id: {3}] ({4}), SMV too low!".format(name, author_rank, comment.submission.title, comment.id, TODAYS_THREAT_LEVEL[threat_level]))
- log.info("Theoretically removing comment by user: {0} ({1}) on submission '{2}' [id: {3}] ({4}), SMV too low!".format(name, author_rank, comment.submission.title, comment.id, TODAYS_THREAT_LEVEL[threat_level]))
- NEED_TO_UPDATE_OBJCLASS = False
- try:
- users = pickle.load(open("users.pickle", "rb"))
- log.info("Re-loading database")
- if NEED_TO_UPDATE_OBJCLASS:
- log.debug("Updating object format")
- for user in users.values():
- users[user.name] = Tracker(user.name, user.good_karma, user.bad_karma, False, user.scan_count, user.next_refresh, user.is_approved, user.is_purged, user.current_flair, user.warned, user.comments_eaten)
- except IOError:
- users = {}
- log.info("I/O error accessing database, starting fresh")
- try:
- botloop(datetime.now()) #+ timedelta(minutes=15))
- except prawcore.PrawcoreException:
- praw_fucked_up()
- botloop(datetime.now()) #+ timedelta(minutes=15))
- except KeyboardInterrupt:
- log.info("VirtuteTron going off-line")
- pickle.dump(users, open("users.pickle", "wb"))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement