Created
July 3, 2019 18:09
-
-
Save sjnovick/964120942605bbbfa517312228adfa02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# flairbot.py | |
# | |
# Script to automate the process of awarding user feedback (aka "rep" or "flair") for | |
# positive transactions conducted on a reddit-based buying and selling community. | |
# | |
# sjnovick 2019 | |
# | |
# | |
# Constraints that must be met before flair is awarded: | |
# | |
# Scenario 1 - The top level comment... | |
# 1 - has a target user | |
# 2 - is not authored by the target user | |
# 3 - has a feedback keyword | |
# 4 - has a permalink | |
# 5 - is authored by the buyer | |
# | |
# Scenario 2 - The top level comment... | |
# 1 - has a target user | |
# 2 - is not authored by the target user | |
# 3 - has a feedback keyword | |
# 4 - has a permalink | |
# 5 - is confirmed by the target user | |
# | |
# Scenario 3 - The top level comment... | |
# 1 - has a target user | |
# 2 - is not authored by the target user | |
# 3 - has a feedback keyword | |
# 4 - has a discord keyword | |
# 5 - is confirmed by the target user | |
# | |
# If contraint scenario is met: | |
# 1 - Get both users original flair | |
# 2 - Determine both users new flair (increment by 1) | |
# 3 - Set both users new flair | |
# 4 - Reply to top level comment | |
# 5 - Remove top level comment | |
import praw | |
import pprint | |
import re | |
import datetime | |
import sqlite3 | |
threadID = 'redacted' | |
reddit = praw.Reddit('bot') | |
subreddit = reddit.subreddit('redacted') | |
submission = reddit.submission(id=threadID) | |
submission.comments.replace_more(limit=None) | |
db = sqlite3.connect('/home/user/flairbot/db/flairbot.db') | |
def user_exists(user): # verifies that a legit reddit account exists | |
exists = True | |
try: | |
id = reddit.redditor(user).id | |
except: | |
exists = False | |
return exists | |
def check_target_user(top_level_comment): # looks for a username in a comment. if a user is found, returns redditor object targetUser | |
author = top_level_comment.author | |
#old: u\/[A-Za-z0-9\\\_-]+ | |
#new: (?i)(?<=u/)[a-z0-9\\\_-]+(?=\\n\\n) | |
userMatch = re.search('(?i)(?<=u/)[a-z0-9\\\_-]+', top_level_comment.body) | |
if userMatch: | |
userMatch = userMatch.group(0) | |
#userMatch = userMatch.strip('/u ') | |
userMatch = re.sub(r'\r?\n.*', '',userMatch) | |
translation_table = dict.fromkeys(map(ord, '\\'), None) | |
userMatch = userMatch.translate(translation_table) | |
exists = user_exists(userMatch) | |
if exists: | |
targetUser = reddit.redditor(userMatch) | |
return targetUser | |
else: | |
return False | |
return False | |
return False | |
def check_author_is_not_target_user(author,targetUser): # checks that the author of the top level comment, and the target user are not the same | |
if author != targetUser: | |
return True | |
return False | |
def check_feedback(top_level_comment): # looks for "positive" keyword in a comment. if found, returns "positive" | |
feedback="" | |
flairMatch = re.search("(?i)positive",top_level_comment.body) | |
if flairMatch: | |
feedback = flairMatch.group(0) | |
return feedback | |
return False | |
def check_buyer(top_level_comment): # looks for "buyer" keyword in a comment. if found, returns authorIsBuyer = True | |
#old: ((im|i'm|Im|I'm)\s)the\s[bB]uyer|(?<=[bB]uyer)\.] | |
#new: (?i)^((im|i'm|i’m|i\sam|i\swas)\s)the\sbuyer | |
buyerMatch = re.search("(?i)((im|i'm|i’m|i\sam|i\swas)\s)the\sbuyer",top_level_comment.body) | |
if buyerMatch: | |
authorIsBuyer=True | |
return authorIsBuyer | |
return False | |
def check_permalink(top_level_comment): # looks for a permalink in comment. if found, returns the permalink | |
permaMatch = re.search("https:\/\/(www.|old.|)reddit.com\/r\/(?i)redacted\/.*",top_level_comment.body) | |
if permaMatch: | |
permaMatch = permaMatch.group(0) | |
return permaMatch | |
return False | |
def check_discord(top_level_comment): # looks for "discord" keyword in a comment. if found, returns discord = True | |
discordMatch = re.search("(?i)discord",top_level_comment.body) | |
if discordMatch: | |
discordMatch = discordMatch.group(0) | |
return discordMatch | |
return False | |
def check_confirmation(comment,targetUser): # looks thru all replies to a top level comment for a reply from # targetUser with a "confirmed" keyword. | |
confirmation = False # if found, returns confirmation = True | |
if comment.replies: | |
comment.replies.replace_more(limit=None) | |
for reply in comment.replies: | |
if reply.author == targetUser: | |
#old: (?i)([Cc]onfirm|(?<=[Cc]onfirm)ed)|([P|p]ositive) | |
#new: (?i)^confirm|positive | |
confirmMatch = re.search("(?i)^confirm|positive",reply.body) | |
if confirmMatch: | |
confirmation = True | |
break | |
return confirmation | |
return confirmation | |
def check_removed(comment): # check if comment has been removed by a mod, if so return True | |
if comment.banned_at_utc == None: return False | |
return True | |
def check_mod_replied(comment): # check if comment has been replied to by a mod, if so return True | |
comment.replies.replace_more(limit=None) | |
if comment.replies: | |
for reply in comment.replies: | |
mods=["redacted"] | |
if reply.author in mods: return True | |
return False | |
def check_mod_authored(comment): # check if comment is authored by certain excluded mods, if so return True | |
mods=["redacted"] | |
if comment.author in mods: return True | |
return False | |
def get_og_flair(user): # gets current subreddit flair for user. if none, returns 0. if it starts with "+" or "-", returns int | |
ogFlair = None | |
success = False | |
ogFlair = next(subreddit.flair(user)) | |
ogFlair = ogFlair['flair_text'] | |
if ogFlair == None or ogFlair == '0 Trades' or ogFlair == 'New Account': | |
ogFlair = 0 | |
success = True | |
return ogFlair,success | |
posMatch = re.search('^\+\d*', ogFlair) | |
if posMatch: | |
ogFlair = posMatch.group(0) | |
ogFlair = ogFlair[1:] | |
ogFlair = int(ogFlair) | |
success = True | |
return ogFlair,success | |
negMatch = re.search('^\-\d*', ogFlair) | |
if negMatch: | |
ogFlair = negMatch.group(0) | |
ogFlair = int(ogFlair) | |
success = True | |
return ogFlair,success | |
return ogFlair,success | |
def do_math(ogFlair): # adds +1 to ints, if >30 changes flair to ">30 Trusted Trader" | |
newFlair = None | |
newSuccess = False | |
if type(ogFlair) is int: | |
newFlair = ogFlair + 1 | |
if newFlair > 0 and newFlair < 31: | |
newFlair = str(newFlair) | |
newFlair = "+" + newFlair | |
newSuccess = True | |
return newFlair,newSuccess | |
elif newFlair > 30: | |
newFlair = str(newFlair) | |
newFlair = ">30 Trusted Trader" | |
newSuccess = True | |
return newFlair,newSuccess | |
return newFlair,newSuccess | |
return newFlair,newSuccess | |
def set_flair(user): # edits the flair for user | |
ogFlair,ogSuccess = get_og_flair(user) | |
newFlair,newSuccess = do_math(ogFlair) | |
if ogSuccess and newSuccess: | |
print (" ! Flairing:", user.name) | |
subreddit.flair.set(user,newFlair) | |
overallSuccess = True | |
return overallSuccess,ogFlair,newFlair | |
else: | |
overallSuccess = False | |
return overallSuccess,ogFlair,newFlair | |
def remove_comment(top_level_comment): # removes the top level comment | |
print (" * Removing:", top_level_comment.id) | |
top_level_comment.mod.remove() | |
def reply(top_level_comment): # replies to the lop level comment, states flaired | |
print (" * Replying:", top_level_comment.id) | |
top_level_comment.reply("flaired") | |
def insert_flairLog(dateParsed, dateSubmitted, threadID, commentID, author, targetUser, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision, authorOgFlair, authorNewFlair, targetOgFlair, targetNewFlair): | |
print (" * Writing to flairLog:", top_level_comment.id) | |
cursor = db.cursor() | |
cursor.execute('''INSERT INTO flairLog (date_parsed, date_submitted, thread_id, comment_id, author, targetUser, feedback, permalink, notTargetUser, authorIsBuyer, confirmed, decision, authorOld, authorNew, targetOld, targetNew) | |
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );''', (dateParsed, dateSubmitted, threadID, commentID, author, targetUser, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision, authorOgFlair, authorNewFlair, targetOgFlair, targetNewFlair)) | |
db.commit() | |
def insert_skipLog(dateParsed, dateSubmitted, threadID, commentID, author, targetUser, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision): | |
print (" Writing to skipLog:", top_level_comment.id) | |
cursor = db.cursor() | |
cursor.execute('''INSERT INTO skipLog (date_parsed, date_submitted, thread_id, comment_id, author, targetUser, feedback, permalink, notTargetUser, authorIsBuyer, confirmed, decision) | |
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );''', (dateParsed, dateSubmitted, threadID, commentID, author, targetUser, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision)) | |
db.commit() | |
# main loop | |
# loops thru all top level comments in the submission | |
dateParsed = datetime.datetime.utcnow().isoformat(' ', 'seconds') | |
print(">>>>> Starting run at", dateParsed) | |
i=0 | |
for top_level_comment in submission.comments: | |
removed = check_removed(top_level_comment) | |
modAuthored = check_mod_authored(top_level_comment) | |
#modReplied = check_mod_replied(top_level_comment) | |
dateSubmitted = datetime.datetime.utcfromtimestamp(top_level_comment.created_utc).isoformat(' ', 'seconds') | |
if not removed and not modAuthored: | |
#and not modReplied: | |
author = top_level_comment.author | |
authorIsBuyer = check_buyer(top_level_comment) | |
targetUser = check_target_user(top_level_comment) | |
authorIsNotTargetUser = check_author_is_not_target_user(author,targetUser) | |
feedback = check_feedback(top_level_comment) | |
permalink = check_permalink(top_level_comment) | |
discord = check_discord(top_level_comment) | |
confirmation = check_confirmation(top_level_comment,targetUser) | |
decision = 0 | |
if targetUser and authorIsNotTargetUser and feedback and permalink and authorIsBuyer: # Constraint Scenario 1 | |
decision = 1 | |
authorFlairSuccess,authorOgFlair,authorNewFlair = set_flair(author) | |
targetFlairSuccess,targetOgFlair,targetNewFlair = set_flair(targetUser) | |
reply(top_level_comment) | |
remove_comment(top_level_comment) | |
insert_flairLog(dateParsed, dateSubmitted, threadID, top_level_comment.id, author.name, targetUser.name, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision, authorOgFlair, authorNewFlair, targetOgFlair, targetNewFlair) | |
elif targetUser and authorIsNotTargetUser and feedback and permalink and confirmation: # Constraint Scenario 2 | |
decision = 2 | |
authorFlairSuccess,authorOgFlair,authorNewFlair = set_flair(author) | |
targetFlairSuccess,targetOgFlair,targetNewFlair = set_flair(targetUser) | |
reply(top_level_comment) | |
remove_comment(top_level_comment) | |
insert_flairLog(dateParsed, dateSubmitted, threadID, top_level_comment.id, author.name, targetUser.name, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision, authorOgFlair, authorNewFlair, targetOgFlair, targetNewFlair) | |
elif targetUser and authorIsNotTargetUser and feedback and discord and confirmation: # Constraint Scenario 3 | |
decision = 3 | |
authorFlairSuccess,authorOgFlair,authorNewFlair = set_flair(author) | |
targetFlairSuccess,targetOgFlair,targetNewFlair = set_flair(targetUser) | |
reply(top_level_comment) | |
remove_comment(top_level_comment) | |
insert_flairLog(dateParsed, dateSubmitted, threadID, top_level_comment.id, author.name, targetUser.name, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision, authorOgFlair, authorNewFlair, targetOgFlair, targetNewFlair) | |
else: # Constraints not met | |
decision = 0 | |
if author != None: | |
author = author.name | |
if targetUser != False: | |
targetUser = targetUser.name | |
insert_skipLog(dateParsed, dateSubmitted, threadID, top_level_comment.id, author, targetUser, feedback, permalink, authorIsNotTargetUser, authorIsBuyer, confirmation, decision) | |
i=i+1 | |
db.close() | |
date = datetime.datetime.utcnow().isoformat(' ', 'seconds') | |
print(">>>>> Finished at", date) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment