|
BAD_WORDS = ['jap', 'chink', 'shill', 'asshole', 'cunt', 'fuck', 'idiot', 'retard', 'twat', 'douche', 'cringe', 'jerk', 'boner', 'puna', 'sperm', 'jews', 'gay', 'pedophile', 'pedant', 'stupid', 'cuck', 'whore', 'retard', 'fedora', 'good sir', 'cakeday', 'tree fiddy', 'stupid', 'wank', 'wanc', 'hooker', 'testicl', 'rape', 'rapist', 'penis', 'gangbang', 'slut', 'apefrican', 'ape', 'fuck', 'mod', 'moderator', 'nigga', 'nigger', 'bitch', 'shit', 'cunt', 'dick', 'fag', 'trans', 'dyke', 'kyke', 'kike', 'nig', 'faggot', 'brave', 'fuck you', 'kill', 'ass', 'asshole', 'arse', 'scum', 'stormfront', 'vagina', 'homo'] |
|
|
|
|
|
def run(workbookname, usernamecolletter=None, subreddit=None, badwords=[], notifyme=False, runbackgroundcheck=True, |
|
useragent=None, username=None, password=None): |
|
""" |
|
Main instance |
|
The user that is OAuthed or LoginAuthed must be an approved contributor to the sub to post apps |
|
and must be an approved submitter to the sub. Just making it a mod won't work. |
|
:param usernamecolletter: the letter of the column that denotes the applicant's username. |
|
If not specified, it will make the title have the timestamp instead of the user's name. This |
|
script assumes that the timestamp row is the first row, so it uses appropiate grammar to comply. |
|
:param notifyme: whether or not to send inbox replies to the account that is making the posts. default is False. |
|
:param subreddit: the sub to post to (string or praw.objects.Subreddit object). While it is a |
|
keyword argument, that's only cause technicalities. IT IS REQUIRED. |
|
:param badwords: list of badwords for background checks. Default is BAD_WORDS in this file. |
|
:param runbackgroundcheck: Boolean, whether or not to add a background check to the post. |
|
BACKGROUND CHECKS CAN TAKE SOME TIME. If there is no username found from the spreadsheet, it will be skipped |
|
:param useragent: optional useragent to specify. The default is |
|
'Mod application to subreddit poster posting to /r/{subreddit} by /u/13steinj' |
|
:params username, password: A username and password to specify if you wish to use LoginAuth. |
|
If OAuth is available, OAuth will have priority. If it fails, login auth will be used. |
|
IN PRAW4 LOGIN AUTH WILL BE DEPRECATED, JUST AS A REMINDER IN CASE YOU SAY THIS SCRIPT DOESN'T WORK WHEN IT'S OUT |
|
|
|
:returns: The amount of posts made and the sub they were made to. |
|
""" |
|
if subreddit == None: |
|
raise TypeError("run() missing required keyword argument subreddit, dipshit") |
|
import praw |
|
import openpyxl |
|
badwords = badwords or BAD_WORDS |
|
useragent = (useragent or |
|
("Mod application to subreddit " |
|
"poster posting to /r/{0} by /u/13steinj".format(subreddit))) |
|
r = praw.Reddit(useragent) |
|
try: |
|
import OAuth2Util |
|
o = OAuth2Util.OAuth2Util(r) |
|
o.refresh(force=True) |
|
except Exception as e: |
|
if e.__class__ == ImportError and username and password: |
|
print("OAuth2Util not installed, using login") |
|
r.login(username, password) |
|
elif username and password: |
|
print("OAuth2Util failure, attempting to use login") |
|
r.login(username, password) |
|
print("Success") |
|
else: |
|
raise |
|
workbook = openpyxl.load_workbook('{0}.xlsx'.format(workbookname)) |
|
sheets = workbook.get_sheet_names() |
|
if len(sheets) > 1: |
|
sheetlist = '\n'.join(['{0}: {1}'.format((sheets.index(i) + 1), i) for i in sheets]) |
|
num = input("Which sheet would you like?\n{0}\n".format(sheetlist)) |
|
try: |
|
num = int(num) |
|
if num <= 0: |
|
raise IndexError("What a dipshit this user is. If only we could replace them with more code...") |
|
sheet = sheets[num - 1] |
|
except ValueError: |
|
raise ValueError("You had to choose a number, dipshit") |
|
except IndexError: |
|
raise IndexError("That sheet doesn't exist, dipshit.") |
|
else: |
|
sheet = sheets[0] |
|
spreadsheet = workbook[sheet] |
|
rows = list(spreadsheet.rows) |
|
questionrow = rows.pop(0) |
|
questionvals = {cell.column: cell.value for cell in questionrow if cell.value is not None} |
|
if usernamecolletter and usernamecolletter not in questionvals: |
|
raise KeyError("That usernamecolumnletter doesn't exist, dipshit") |
|
rownum = 0 |
|
for row in rows: |
|
# clear out username, title, and body for next run |
|
rownum += 1 |
|
username = None |
|
body = '' |
|
# make body |
|
for cell in row: |
|
if cell.column not in questionvals: |
|
continue |
|
body += "#{0}\n\n".format(questionvals[cell.column]) |
|
body += "{0}\n\n---\n\n".format(cell.value if cell.value else "*No Answer*") |
|
if username: |
|
continue |
|
else: |
|
username = cell.value if cell.column == usernamecolletter else None |
|
if username and username.startswith("/u/"): |
|
username = username[3:] |
|
elif username and username.startswith("u/"): |
|
username = username[2:] |
|
if username and runbackgroundcheck: |
|
body += background_check(r, username, badwords=badwords) |
|
if len(body) > 40000: |
|
bodies = list(_util_bodies(40000, body)) |
|
else: |
|
bodies = [body] |
|
titleinfo = "at /u/{0}".format(row[0].value) if not username else "by /u/{0}".format(username) |
|
maintitle = "Moderator Application #{0} {1}".format(rownum, titleinfo) |
|
for body in bodies: |
|
if bodies.index(body) > 0: |
|
title = "{0} Part {1}".format(maintitle, (bodies.index(body) + 1)) |
|
else: |
|
title = maintitle |
|
post = r.submit(subreddit, title, text=body, send_replies=notifyme) |
|
print("Submitted {0}".format(post.permalink)) |
|
print( |
|
"I've just posted {0} applications, dipshit. I'm done now. Call me when you want me again. But you could at least ask me out to dinner first, you rascal!".format( |
|
rownum)) |
|
return rownum, subreddit |
|
|
|
|
|
def background_check(reddit_session, username, badwords=[], post_sub=None): |
|
"""Run a background check on a username |
|
:param reddit_session: a reddit session via praw, usually r |
|
:param username: username to check |
|
:param badwords: list of badwords to run in the profanitychecker |
|
:param post_sub: if you want to post this check to a sub, this |
|
should be the sub name or the subreddit object of this sub name |
|
:returns: post if post_sub is defined, else a background check string. |
|
""" |
|
from praw.errors import NotFound, Forbidden |
|
import datetime |
|
cannot_submit = reddit_session.user == None and not reddit_session.is_oauth_session() |
|
if cannot_submit and post_sub != None: |
|
raise ValueError("Can't post without being logged in, dipshit") |
|
title = "Background Check: {0}".format(username) if post_sub else None |
|
if username.lower().startswith('/u/'): |
|
username = username[3:] |
|
elif username.lower().startswith('u/'): |
|
username = username[2:] |
|
body = "#Background Check -- /u/{0}:\n\n".format(username) if not title else "/u/{0}:\n\n".format(username) |
|
if title: |
|
print("Running background check on /u/{0}".format(username)) |
|
user = reddit_session.get_redditor(username) |
|
try: |
|
dumblist = list(user.get_overview()) |
|
except NotFound: |
|
body += "Account doesn't exist or deleted or shadowbanned" |
|
return body |
|
except Forbidden: |
|
body += "Account permanently suspended" |
|
return body |
|
ucreated = datetime.datetime.fromtimestamp(user.created_utc) |
|
utimeago = "Account made {0} ago".format(str(datetime.datetime.now() - ucreated)) |
|
utimeon = "on " + str(ucreated) |
|
utimetext = " ".join([utimeago, utimeon]) |
|
body += utimetext + "\n\n" |
|
posts = list(user.get_submitted(limit=None)) |
|
try: |
|
timepost = posts[100] |
|
timepostnum = 100 |
|
timepostord = _util_ordinal(timepostnum) |
|
timepostpre = timepostord + " post" |
|
timepostdate = datetime.datetime.fromtimestamp(timepost.created_utc) |
|
timeago = "made {0} ago".format(str(datetime.datetime.now() - timepostdate)) |
|
timeon = "on " + str(timepostdate) |
|
timeposttext = " ".join([timepostpre, timeago, timeon]) |
|
except IndexError: |
|
try: |
|
timepost = posts[-1] |
|
timepostnum = (posts.index(timepost) + 1) |
|
timepostord = _util_ordinal(timepostnum) |
|
timepostpre = "last({0}) post".format(timepostord) |
|
timepostdate = datetime.datetime.fromtimestamp(timepost.created_utc) |
|
timeago = "made {0} ago".format(str(datetime.datetime.now() - timepostdate)) |
|
timeon = "on " + str(timepostdate) |
|
timeposttext = " ".join([timepostpre, timeago, timeon]) |
|
except IndexError: |
|
timeposttext = "User has not made a single post, or all are deleted" |
|
body += timeposttext + "\n\n" |
|
comments = list(user.get_comments(limit=None)) |
|
totalprofanities, specificprofanities = profanitycheck(badwords, posts, comments) |
|
orderedprofanities = sorted(specificprofanities.keys(), key=str.lower) |
|
profanitytable = "Profanity | Times Used\n---|---" |
|
for word in orderedprofanities: |
|
profanitytable += "\n" |
|
profanitytable += "{0} | {1}".format(word, specificprofanities[word]) |
|
profanitytable += "\n**Total** | {0}".format(totalprofanities) |
|
if specificprofanities: |
|
body += "Profanities used in the last 1000 posts and comments:\n\n" |
|
body += profanitytable + "\n\n" |
|
totalsubs, subhistory = historycheck(posts, comments) |
|
orderedsubs = sorted(subhistory.keys(), key=str.lower) |
|
historytable = "Subreddit | Times Used\n---|---" |
|
for subreddit in orderedsubs: |
|
historytable += '\n' |
|
historytable += "/r/{0} | {1}".format(subreddit, subhistory[subreddit]) |
|
historytable += "\n**Total** | {0}".format(totalsubs) |
|
body += "Subreddit history over last 1000 posts and comments:\n\n" |
|
body += historytable + "\n\n" |
|
if title: |
|
post = reddit_session.submit(post_sub, title, text=body) |
|
return post |
|
return body |
|
|
|
|
|
def _util_ordinal(value): |
|
""" |
|
Converts zero or a *postive* integer (or their string |
|
representations) to an ordinal value. |
|
http://code.activestate.com/recipes/576888-format-a-number-as-an-ordinal/ |
|
""" |
|
try: |
|
value = int(value) |
|
except ValueError: |
|
return value |
|
|
|
if value % 100 // 10 != 1: |
|
if value % 10 == 1: |
|
ordval = u"%d%s" % (value, "st") |
|
elif value % 10 == 2: |
|
ordval = u"%d%s" % (value, "nd") |
|
elif value % 10 == 3: |
|
ordval = u"%d%s" % (value, "rd") |
|
else: |
|
ordval = u"%d%s" % (value, "th") |
|
else: |
|
ordval = u"%d%s" % (value, "th") |
|
|
|
return ordval |
|
|
|
|
|
def _util_bodies(num, string): |
|
"""Produce `num`-character chunks from `string`.""" |
|
for start in range(0, len(string), num): |
|
yield string[start:start + num] |
|
|
|
|
|
def profanitycheck(badwords, *args): |
|
""" |
|
Profanity Checker |
|
:param badwords: list of badwords. Recommended to not contain endings i.e "ing", |
|
"ed" for best results, as containing those may make for duplicates. |
|
:param args: comma delimited list of iters of posts or comments |
|
:returns: dict of word: usagecount for each word in badwords |
|
""" |
|
import re |
|
from bs4 import BeautifulSoup |
|
obj_list = [] |
|
html_list = [] |
|
check_list = [] |
|
profanity_usage = {} |
|
for l in args: |
|
obj_list += l |
|
html_list += [obj.selftext_html for obj in obj_list if hasattr(obj, 'selftext_html')] |
|
html_list += [obj.body_html for obj in obj_list if hasattr(obj, 'body_html')] |
|
for html in html_list: |
|
if html == None: |
|
continue |
|
check_list += BeautifulSoup(html).get_text().split() |
|
badwordtotalusage = 0 |
|
for word in badwords: |
|
badwordusage = 0 |
|
for check in check_list: |
|
if re.search(word.lower(), check.lower()): |
|
badwordusage += 1 |
|
if badwordusage: |
|
profanity_usage[word] = badwordusage |
|
badwordtotalusage += badwordusage |
|
return badwordtotalusage, profanity_usage |
|
|
|
|
|
def historycheck(*args): |
|
""" |
|
Subreddit History Checker |
|
:param args: comma delimited list of iters of posts or comments |
|
""" |
|
obj_list = [] |
|
subreddit_history = {} |
|
for l in args: |
|
obj_list += l |
|
for Thing in obj_list: |
|
subtotal = subreddit_history.get(Thing.subreddit.display_name, 0) |
|
subreddit_history.update({Thing.subreddit.display_name: (subtotal + 1)}) |
|
totalsubs = 0 |
|
for num in subreddit_history.values(): |
|
totalsubs += num |
|
return totalsubs, subreddit_history |
|
|
|
def main(): |
|
import getpass |
|
global BAD_WORDS |
|
filename = "input" |
|
usercolumn=None |
|
subredditName=None |
|
notifyme=False |
|
runbackgroundcheck=False |
|
username=None |
|
password=None |
|
|
|
print("Mod Application Checker") |
|
newbadwords=input("Bad Words (["+','.join(BAD_WORDS)+"]):") |
|
if(newbadwords): |
|
newbadwordsList=newbadwords.split(',') |
|
if(newbadwordsList is not None): |
|
BAD_WORDS=newbadwordsList |
|
print("Bad words is now: "+','.join(BAD_WORDS)) |
|
|
|
fileloc=input("What is the xlsx name, without the xlsx extention ("+filename+")") |
|
if(fileloc): |
|
filename=fileloc |
|
print("Input file name is: "+filename) |
|
userColumnInput=input("What Column number is the user name? (None)") |
|
if(userColumnInput): |
|
usercolumn=userColumnInput |
|
print("User Name column is: "+str(usercolumn)) |
|
while(subredditName==None): |
|
subredditInput=input("Subreddit name, without the leading r/") |
|
if(subredditInput): |
|
subredditName=subredditInput |
|
print("Subreddit Name r/"+subredditName) |
|
notifyInput=input("Should user be notified? (false)") |
|
if(notifyInput and not notifyInput=="false"): |
|
notifyme=True |
|
print("Notify me? "+str(notifyme)) |
|
backgroundInput = input("Should background check be run? (false)") |
|
if (backgroundInput and not backgroundInput == "false"): |
|
runbackgroundcheck = True |
|
print("run Background check? " + str(runbackgroundcheck)) |
|
while(username==None): |
|
usernameInput=input("Username to login: ") |
|
if(usernameInput): |
|
username=usernameInput |
|
print("Username is: "+username) |
|
while(password==None): |
|
passwordInput=input("Password: ") |
|
if(passwordInput): |
|
password=passwordInput |
|
print("Running") |
|
run(fileloc,usercolumn,subredditName,BAD_WORDS,notifyme,runbackgroundcheck,None,username,password) |
|
|
|
if __name__ == "__main__": |
|
main() |