Skip to content

Instantly share code, notes, and snippets.

@aperson
Created July 14, 2012 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aperson/3112716 to your computer and use it in GitHub Desktop.
Save aperson/3112716 to your computer and use it in GitHub Desktop.
Script to purge bans in a subreddit
#!/usr/bin env python3
import json
import urllib.request
import time
import sys
import signal
from urllib.parse import urlencode
import http.cookiejar
def sigint_handler(signal, frame):
'''Handles ^c'''
print('Recieved SIGINT! Exiting...')
sys.exit(0)
class Reddit(object):
"""Base class to perform the tasks of a redditor."""
def __init__(self, username, password):
self.username = username
self.password = password
self.cj = http.cookiejar.CookieJar()
self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cj))
self.opener.addheaders = [('User-agent', 'prune_bans.py')]
self._login()
def _request(self, url, body=None):
if body is not None:
body = urlencode(body).encode('utf-8')
with self.opener.open(url, data=body) as w:
time.sleep(2)
return json.loads(w.read().decode('utf-8'))
def _login(self):
print("Logging in as {}.".format(self.username))
body = {'user' :self.username, 'passwd' : self.password, 'api_type' : 'json'}
resp = self._request('https://www.reddit.com/api/login', body)
self.modhash = resp['json']['data']['modhash']
def post(self, url, body):
"""Sends a POST to the url and returns the json as a dict."""
if 'api_type' not in body: body['api_type'] = 'json'
body['uh'] = self.modhash
return self._request(url, body)
def get(self, url):
if not url.endswith('.json'): url += '.json'
return self._request(url)
def process_subreddit(redditor, subreddit, months=None, dry=False):
print("Processing the bans in /r/{}.".format(subreddit))
processed = 0
found = 0
r = redditor
banned_list = r.get('http://www.reddit.com/r/{}/about/banned/'.format(subreddit))
try:
with open('banned_list.json') as f:
cache = json.loads(f.read())
except IOError:
cache = dict()
for i in banned_list['data']['children']:
found += 1
if i['name'] not in cache:
print('Getting info on: ' + i['name'])
try:
u = r.get('http://www.reddit.com/user/' + i['name'])
# since they're not banned, we'll cache the last activity time
data = u['data']['children']
if data:
cache[i['name']] = data[0]['data']['created_utc']
else:
cache[i['name']] = 0
except urllib.error.HTTPError:
# since they are banned, we'll just add that as such
cache[i['name']] = 'banned'
finally:
with open('banned_list.json', 'w') as f:
f.write(json.dumps(cache))
if cache[i['name']] == 'banned':
print("{} is shadowbanned or deleted, removing from /r/{}'s ban list.".format(
i['name'], subreddit))
body = {'executed' : 'removed', 'type' : 'banned', 'r' : subreddit, 'id' : i['id']}
if not dry:
r.post('http://www.reddit.com/api/unfriend', body)
processed +=1
elif months:
months_secs = months * 2592000
if cache[i['name']] < (time.time() - months_secs):
print("{} is inactive over {} month[s], removing from /r/{}'s ban list.".format(
i['name'], months, subreddit))
body = {'executed' : 'removed', 'type' : 'banned',
'r' : subreddit, 'id' : i['id']}
if not dry:
r.post('http://www.reddit.com/api/unfriend', body)
processed += 1
print("Found {} bans; processed {}".format(found, processed))
if __name__ == '__main__':
signal.signal(signal.SIGINT, sigint_handler)
args = sys.argv[1:]
if len(args) >= 3:
if args[0] in ('-d', '--dry-run'):
print("Performing a dry run.")
dry_run = True
args = args[1:]
else:
dry_run = False
redditor = Reddit(args[0], args[1])
try:
months = int(args[2])
for i in args[3:]:
process_subreddit(redditor, i, months=months, dry=dry_run)
except ValueError:
for i in args[2:]:
process_subreddit(redditor, i, dry=dry_run)
else:
print("USAGE:\npython3 prune_bans.py <username> <password> [subreddit,]",
"\n-OR-\npython3 prune_bans.py <username> <password> <months> [subreddit,]",
"\nMonths can be in any amount as long as it's a number. Subreddits",
"\ncan be ran in batches by listing multiples or just singly.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment