Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Tree: 44ebdeb378
Fetching contributors…

Cannot retrieve contributors at this time

429 lines (341 sloc) 14.3 KB
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
# The Original Code is Reddit.
# The Original Developer is the Initial Developer. The Initial Developer of the
# Original Code is CondeNet, Inc.
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
# CondeNet, Inc. All Rights Reserved.
from r2.lib.utils import tup, fetch_things2
from r2.lib.filters import websafe
from r2.lib.log import log_text
from r2.models import Report, Account, Subreddit
from pylons import g
from datetime import datetime, timedelta
from copy import copy
class AdminTools(object):
def spam(self, things, auto=True, moderator_banned=False,
banner=None, date=None, train_spam=True, **kw):
from r2.lib.db import queries
all_things = tup(things)
new_things = [x for x in all_things if not x._spam]
# No need to accept reports on things with _spam=True,
# since nobody can report them in the first place.
Report.accept(new_things, True)
for t in all_things:
if getattr(t, "promoted", None) is not None:
g.log.debug("Refusing to mark promotion %r as spam" % t)
if not t._spam and train_spam:
note = 'spam'
elif not t._spam and not train_spam:
note = 'remove not spam'
elif t._spam and not train_spam:
note = 'confirm spam'
elif t._spam and train_spam:
note = 'reinforce spam'
t._spam = True
ban_info = copy(getattr(t, 'ban_info', {}))
if isinstance(banner, dict):
ban_info['banner'] = banner[t._fullname]
ban_info['banner'] = banner
banned_at=date or,
ban_info['note'] = note
t.ban_info = ban_info
if not auto:
self.author_spammer(new_things, True)
def unspam(self, things, unbanner=None, train_spam=True, insert=True):
from r2.lib.db import queries
things = tup(things)
# We want to make unban-all moderately efficient, so when
# mass-unbanning, we're going to skip the code below on links that
# are already not banned. However, when someone manually clicks
# "approve" on an unbanned link, and there's just one, we want do
# want to run the code below. That way, the little green checkmark
# will have the right mouseover details, the reports will be
# cleared, etc.
if len(things) > 1:
things = [x for x in things if x._spam]
Report.accept(things, False)
for t in things:
ban_info = copy(getattr(t, 'ban_info', {}))
ban_info['unbanned_at'] =
if unbanner:
ban_info['unbanner'] = unbanner
if ban_info.get('reset_used', None) == None:
ban_info['reset_used'] = False
ban_info['reset_used'] = True
t.ban_info = ban_info
t._spam = False
self.author_spammer(things, False)
queries.unban(things, insert)
def author_spammer(self, things, spam):
"""incr/decr the 'spammer' field for the author of every
passed thing"""
by_aid = {}
for thing in things:
if (hasattr(thing, 'author_id')
and not getattr(thing, 'ban_info', {}).get('auto',True)):
# only decrement 'spammer' for items that were not
# autobanned
by_aid.setdefault(thing.author_id, []).append(thing)
if by_aid:
authors = Account._byID(by_aid.keys(), data=True, return_dict=True)
for aid, author_things in by_aid.iteritems():
author = authors[aid]
author._incr('spammer', len(author_things) if spam else -len(author_things))
def email_attrs(self, account_ids, return_dict=True):
account_ids, single = tup(account_ids, True)
accounts = Account._byID(account_ids, data=True, return_dict=False)
rv = {}
canons = {}
aids_by_canon = {} # sounds terrifying
for a in accounts:
attrs = []
rv[a._id] = attrs
if not getattr(a, "email", None):
attrs.append(("gray", "no email specified", "X"))
canon = a.canonical_email()
aids_by_canon.setdefault(canon, [])
verify_str = "verified email: " + canon
if getattr(a, "email_verified", None):
attrs.append(("green", verify_str, "V"))
attrs.append(("gray", "un" + verify_str, "@"))
ban_reasons = Account.which_emails_are_banned(aids_by_canon.keys())
for canon, ban_reason in ban_reasons.iteritems():
if ban_reason:
for aid in aids_by_canon[canon]:
rv[aid].append(("wrong", "banned email " + ban_reason, "B"))
if single:
return rv[account_ids[0]]
elif return_dict:
return rv
return filter(None, (rv.get(i) for i in account_ids))
def set_last_sr_ban(self, things):
by_srid = {}
for thing in things:
if getattr(thing, 'sr_id', None) is not None:
by_srid.setdefault(thing.sr_id, []).append(thing)
if by_srid:
srs = Subreddit._byID(by_srid.keys(), data=True, return_dict=True)
for sr_id, sr_things in by_srid.iteritems():
sr = srs[sr_id]
sr.last_mod_action =
sr._incr('mod_actions', len(sr_things))
def engolden(self, account, days): = True
now =
existing_expiration = getattr(account, "gold_expiration", None)
if existing_expiration is None or existing_expiration < now:
existing_expiration = now
account.gold_expiration = existing_expiration + timedelta(days)
description = "Since " + now.strftime("%B %Y")
trophy = Award.give_if_needed("reddit_gold", account,
if trophy and trophy.description.endswith("Member Emeritus"):
trophy.description = description
if g.lounge_reddit:
sr = Subreddit._by_name(g.lounge_reddit)
def degolden(self, account, severe=False):
if severe:
account.gold_charter = False
Award.take_away("charter_subscriber", account)
Award.take_away("reddit_gold", account) = False
if g.lounge_reddit and not getattr(account, "gold_charter", False):
sr = Subreddit._by_name(g.lounge_reddit)
def admin_list(self):
return list(g.admins)
admintools = AdminTools()
def cancel_subscription(subscr_id):
q = Account._query(Account.c.gold_subscr_id == subscr_id, data=True)
l = list(q)
if len(l) != 1:
g.log.warning("Found %d matches for canceled subscription %s"
% (len(l), subscr_id))
for account in l:
account.gold_subscr_id = None
account._commit()"%s canceled their recurring subscription %s" %
(, subscr_id))
def all_gold_users():
q = Account._query( == True, data=True,
return fetch_things2(q)
def accountid_from_paypalsubscription(subscr_id):
if subscr_id is None:
return None
q = Account._query(Account.c.gold_subscr_id == subscr_id,
l = list(q)
if l:
return l[0]._id
return None
def update_gold_users(verbose=False):
now =
minimum = None
count = 0
expiration_dates = {}
for account in all_gold_users():
if not hasattr(account, "gold_expiration"):
g.log.error("%s has no gold_expiration" %
delta = account.gold_expiration - now
days_left = delta.days
hc_key = "gold_expiration_notice-" +
if days_left < 0:
if verbose:
print "%s just expired" %
send_system_message(account, "Your reddit gold subscription has expired. :(",
"Your subscription to reddit gold has expired. [Click here for details on how to renew, or to set up an automatically-renewing subscription.]( Or, if you don't want to, please write to us at and tell us where we let you down, so we can work on fixing the problem.")
count += 1
if verbose:
exp_date = account.gold_expiration.strftime('%Y-%m-%d')
expiration_dates.setdefault(exp_date, 0)
expiration_dates[exp_date] += 1
# print "%s expires in %d days" % (, days_left)
if minimum is None or delta < minimum[0]:
minimum = (delta, account)
if days_left <= 3 and not g.hardcache.get(hc_key):
if verbose:
print "%s expires soon: %s days" % (, days_left)
if getattr(account, "gold_subscr_id", None):
if verbose:
print "Not sending notice to %s (%s)" % (,
if verbose:
print "Sending notice to %s" %
g.hardcache.set(hc_key, True, 86400 * 10)
send_system_message(account, "Your reddit gold subscription is about to expire!",
"Your subscription to reddit gold will be expiring soon. [Click here for details on how to renew, or to set up an automatically-renewing subscription.]( Or, if you think we suck, just let your subscription lapse and go back to being a regular user.\n\nIf you have any questions, please write to")
if verbose:
for exp_date in sorted(expiration_dates.keys()):
num_expiring = expiration_dates[exp_date]
print '%s %3d %s' % (exp_date, num_expiring, '*' * num_expiring)
print "%s goldmembers" % count
if minimum is None:
print "Nobody found."
delta, account = minimum
print "Next expiration is %s, in %d days" % (, delta.days)
def admin_ratelimit(user):
return True
def is_banned_IP(ip):
return False
def is_banned_domain(dom, ip):
return None
def is_shamed_domain(dom, ip):
return False, None, None
def valid_thing(v, karma, *a, **kw):
return not v._thing1._spam
def valid_user(v, sr, karma, *a, **kw):
return True
# Returns whether this person is being suspicious
def login_throttle(username, wrong_password):
return False
def apply_updates(user):
def update_score(obj, up_change, down_change, vote, old_valid_thing):
obj._incr('_ups', up_change)
obj._incr('_downs', down_change)
def compute_votes(wrapper, item):
wrapper.upvotes = item._ups
wrapper.downvotes = item._downs
def ip_span(ip):
ip = websafe(ip)
return '<!-- %s -->' % ip
def filter_quotas(unfiltered):
from r2.lib.utils.trial_utils import trial_info
trials = trial_info(unfiltered)
now =
baskets = {
'hour': [],
'day': [],
'week': [],
'month': [],
new_quotas = []
quotas_changed = False
for item in unfiltered:
delta = now - item._date
age = delta.days * 86400 + delta.seconds
# First, select a basket or abort if item is too old
if age < 3600:
basket = 'hour'
elif age < 86400:
basket = 'day'
elif age < 7 * 86400:
basket = 'week'
elif age < 30 * 86400:
basket = 'month'
quotas_changed = True
verdict = getattr(item, "verdict", None)
approved = verdict and verdict in (
'admin-approved', 'mod-approved')
# Then, make sure it's worthy of quota-clogging
if trials.get(item._fullname):
elif item._spam:
elif item._deleted:
elif item._score <= 0:
elif age < 86400 and item._score <= g.QUOTA_THRESHOLD and not approved:
quotas_changed = True
if quotas_changed:
return baskets, new_quotas
return baskets, None
def check_request(end_time):
from r2admin.models.admintools import *
except ImportError:
Jump to Line
Something went wrong with that request. Please try again.