Merge branch 'frost' of https://github.com/Aevann1/Drama into frost

remotes/1693045480750635534/spooky-22
Aevann1 2022-06-10 14:48:14 +02:00
commit 2db414f12d
20 changed files with 342 additions and 361 deletions

View File

@ -47,4 +47,9 @@ class Sub(Base):
@property @property
@lazy @lazy
def block_num(self): def block_num(self):
return len(self.blocks) return len(self.blocks)
@property
@lazy
def follow_num(self):
return len(self.followers)

View File

@ -90,6 +90,7 @@ class User(Base):
unmutable = Column(Boolean) unmutable = Column(Boolean)
eye = Column(Boolean) eye = Column(Boolean)
alt = Column(Boolean) alt = Column(Boolean)
offsitementions = Column(Boolean, default=False, nullable=False)
frontsize = Column(Integer, default=25) frontsize = Column(Integer, default=25)
controversial = Column(Boolean, default=False) controversial = Column(Boolean, default=False)
bio = deferred(Column(String)) bio = deferred(Column(String))
@ -228,7 +229,11 @@ class User(Base):
if self.has_badge(badge): discount -= discounts[badge] if self.has_badge(badge): discount -= discounts[badge]
return discount return discount
@property
@lazy
def can_view_offsitementions(self):
return self.offsitementions or self.admin_level >= REDDIT_NOTIFS_JL_MIN
@property @property
@lazy @lazy
@ -446,22 +451,44 @@ class User(Base):
@property @property
@lazy @lazy
def post_notifications_count(self): def post_notifications_count(self):
return g.db.query(Notification).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.author_id == AUTOJANNY_ID).count() return g.db.query(Notification).join(Comment).filter(
Notification.user_id == self.id, Notification.read == False,
Comment.author_id == AUTOJANNY_ID).count()
@property
@lazy
def modaction_notifications_count(self):
return g.db.query(Notification).join(Comment).filter(
Notification.user_id == self.id, Notification.read == False,
Comment.is_banned == False, Comment.deleted_utc == 0,
Comment.body_html.like(f'%<p>{NOTIF_MODACTION_PREFIX}%'),
Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).count()
@property @property
@lazy @lazy
def reddit_notifications_count(self): def reddit_notifications_count(self):
return g.db.query(Notification).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.is_banned == False, Comment.deleted_utc == 0, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).count() return g.db.query(Notification).join(Comment).filter(
Notification.user_id == self.id, Notification.read == False,
Comment.is_banned == False, Comment.deleted_utc == 0,
Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'),
Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).count()
@property @property
@lazy @lazy
def normal_count(self): def normal_count(self):
return self.notifications_count - self.post_notifications_count - self.reddit_notifications_count return self.notifications_count \
- self.post_notifications_count \
- self.modaction_notifications_count \
- self.reddit_notifications_count
@property @property
@lazy @lazy
def do_posts(self): def do_posts(self):
return self.post_notifications_count and self.notifications_count-self.reddit_notifications_count == self.post_notifications_count return self.post_notifications_count and \
self.post_notifications_count == (
self.notifications_count
- self.modaction_notifications_count
- self.reddit_notifications_count)
@property @property
@lazy @lazy

View File

@ -1,4 +1,4 @@
from .__main__ import app, db_session from .__main__ import app, db_session, cache
from flask import g from flask import g
import files.helpers.cron import files.helpers.cron

View File

@ -5,12 +5,12 @@ from files.classes.badges import Badge, BadgeDef
# TODO: More sanity checks on passed parameters. # TODO: More sanity checks on passed parameters.
# TODO: Add `replace=False` parameter which, when set true, removes any # TODO: Add `replace=False` parameter which, when set true, removes any
# existing badge with identical id & user and replaces with new one. # existing badge with identical id & user and replaces with new one.
def badge_grant(user_id, badge_id, desc='', url=''): def badge_grant(user_id, badge_id, desc='', url='', commit=True):
user = g.db.query(User).filter(User.id == int(user_id)).one_or_none() user = g.db.query(User).filter(User.id == int(user_id)).one_or_none()
if not user: if not user:
return False return None
elif user.has_badge(badge_id): elif user.has_badge(badge_id):
return True return None
badge = Badge( badge = Badge(
badge_id=int(badge_id), badge_id=int(badge_id),
@ -20,5 +20,8 @@ def badge_grant(user_id, badge_id, desc='', url=''):
) )
g.db.add(badge) g.db.add(badge)
g.db.commit() if commit:
return True g.db.commit()
else:
g.db.flush()
return badge

View File

@ -114,4 +114,22 @@ def NOTIFY_USERS(text, v):
user = get_user(i.group(2), graceful=True) user = get_user(i.group(2), graceful=True)
if user and v.id != user.id and not v.any_block_exists(user): notify_users.add(user.id) if user and v.id != user.id and not v.any_block_exists(user): notify_users.add(user.id)
return notify_users - bots return notify_users - bots
def notify_mod_action(by_id, msg):
body_html = sanitize(NOTIF_MODACTION_PREFIX + msg)
new_comment = Comment(
author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
distinguish_level=6)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
send_to = g.db.query(User).filter(
User.admin_level >= NOTIF_MODACTION_JL_MIN, User.id != by_id).all()
for admin in send_to:
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)

View File

@ -0,0 +1,83 @@
from flask import g
import time
from files.helpers.alerts import send_repeatable_notification
from files.helpers.const import bots
from files.helpers.discord import remove_role
from files.classes.badges import Badge
from files.classes.user import User
def award_timers(v, bot=False):
now = time.time()
dirty = False
def notify_if_not_bot(msg):
if not bot:
send_repeatable_notification(v.id,msg)
if v.patron_utc and v.patron_utc < now:
v.patron = 0
v.patron_utc = 0
notify_if_not_bot("Your paypig status has expired!")
if not bot and v.discord_id: remove_role(v, "1")
dirty = True
if v.unban_utc and v.unban_utc < now:
v.is_banned = 0
v.unban_utc = 0
v.ban_evade = 0
notify_if_not_bot("You have been unbanned!")
dirty = True
if v.agendaposter and v.agendaposter < now:
v.agendaposter = 0
notify_if_not_bot("Your chud theme has expired!")
badge = v.has_badge(28)
if badge: g.db.delete(badge)
dirty = True
if v.flairchanged and v.flairchanged < now:
v.flairchanged = None
notify_if_not_bot("Your flair lock has expired. You can now change your flair!")
badge = v.has_badge(96)
if badge: g.db.delete(badge)
dirty = True
if v.marseyawarded and v.marseyawarded < now:
v.marseyawarded = None
notify_if_not_bot("Your marsey award has expired!")
badge = v.has_badge(98)
if badge: g.db.delete(badge)
dirty = True
if v.longpost and v.longpost < now:
v.longpost = None
notify_if_not_bot("Your pizzashill award has expired!")
badge = v.has_badge(97)
if badge: g.db.delete(badge)
dirty = True
if v.bird and v.bird < now:
v.bird = None
notify_if_not_bot("Your bird site award has expired!")
badge = v.has_badge(95)
if badge: g.db.delete(badge)
dirty = True
if v.progressivestack and v.progressivestack < now:
v.progressivestack = None
notify_if_not_bot("Your progressive stack has expired!")
badge = v.has_badge(94)
if badge: g.db.delete(badge)
dirty = True
if v.rehab and v.rehab < now:
v.rehab = None
notify_if_not_bot("Your rehab has finished!")
badge = v.has_badge(109)
if badge: g.db.delete(badge)
dirty = True
if v.deflector and v.deflector < now:
v.deflector = None
notify_if_not_bot("Your deflector has expired!")
dirty = True
if dirty:
g.db.add(v)
g.db.commit()
def award_timers_bots_task():
accs = g.db.query(User).filter(User.id.in_(bots))
for u in accs:
award_timers(u, bot=True)

View File

@ -132,9 +132,11 @@ PIN_LIMIT = 3
POST_RATE_LIMIT = "1/second;2/minute;10/hour;50/day" POST_RATE_LIMIT = "1/second;2/minute;10/hour;50/day"
USER_TITLE_COST = 0 USER_TITLE_COST = 0
HOLE_INACTIVITY_DELETION = False
if SITE in {'rdrama.net','devrama.xyz'}: if SITE in {'rdrama.net','devrama.xyz'}:
HOLE_COST = 50000 HOLE_COST = 50000
HOLE_INACTIVITY_DELETION = True
USER_TITLE_COST = 25 USER_TITLE_COST = 25
NOTIFICATIONS_ID = 1046 NOTIFICATIONS_ID = 1046
AUTOJANNY_ID = 2360 AUTOJANNY_ID = 2360
@ -660,6 +662,14 @@ AWARDS = {
"color": "text-silver", "color": "text-silver",
"price": 10000 "price": 10000
}, },
"offsitementions": {
"kind": "offsitementions",
"title": "Y'all Seein' Eye",
"description": "Gives the recipient access to notifications when people off-site talk about us.",
"icon": "fas fa-eyes",
"color": "text-orange",
"price": 10000,
},
"unblockable": { "unblockable": {
"kind": "unblockable", "kind": "unblockable",
"title": "Unblockable", "title": "Unblockable",
@ -799,8 +809,12 @@ FACTCHECK_REPLIES = ('<b style="color:#6023f8">Factcheck: This claim has been co
if SITE_NAME == 'rDrama': patron = 'Paypig' if SITE_NAME == 'rDrama': patron = 'Paypig'
else: patron = 'Patron' else: patron = 'Patron'
NOTIF_MODACTION_PREFIX = '[Modaction] '
NOTIF_MODACTION_JL_MIN = 2
REDDIT_NOTIFS_SITE = [] REDDIT_NOTIFS_SITE = []
REDDIT_NOTIFS_USERS = {} REDDIT_NOTIFS_USERS = {}
REDDIT_NOTIFS_JL_MIN = 1
if SITE_NAME == 'rDrama': if SITE_NAME == 'rDrama':
REDDIT_NOTIFS_SITE = ['rdrama', 'marsey',] REDDIT_NOTIFS_SITE = ['rdrama', 'marsey',]
@ -814,6 +828,7 @@ if SITE_NAME == 'rDrama':
} }
elif SITE_NAME == 'PCM': elif SITE_NAME == 'PCM':
REDDIT_NOTIFS_SITE = ['pcmemes.net',] REDDIT_NOTIFS_SITE = ['pcmemes.net',]
REDDIT_NOTIFS_JL_MIN = 3
discounts = { discounts = {
# Big Spender badges, 2pp additive discount each # Big Spender badges, 2pp additive discount each
@ -1035,17 +1050,16 @@ linefeeds_regex = re.compile("([^\n])\n([^\n])", flags=re.A)
def make_name(*args, **kwargs): return request.base_url def make_name(*args, **kwargs): return request.base_url
# Lottery # Lottery
LOTTERY_ENABLED = False
LOTTERY_TICKET_COST = 0
LOTTERY_SINK_RATE = 0
LOTTERY_ROYALTY_RATE = 0
LOTTERY_ROYALTY_ACCOUNT_ID = 0
LOTTERY_MANAGER_ACCOUNT_ID = 0
if SITE_NAME == 'rDrama': if SITE_NAME == 'rDrama':
LOTTERY_ENABLED = True LOTTERY_ENABLED = True
LOTTERY_TICKET_COST = 12 LOTTERY_TICKET_COST = 12
LOTTERY_SINK_RATE = 3 LOTTERY_SINK_RATE = 3
LOTTERY_ROYALTY_RATE = 1 LOTTERY_ROYALTY_RATE = 0
LOTTERY_ROYALTY_ACCOUNT_ID = 1387 # (Chapose) LOTTERY_ROYALTY_ACCOUNT_ID = 1387 # (Chapose)
LOTTERY_MANAGER_ACCOUNT_ID = 11651 # (Lottershe) LOTTERY_MANAGER_ACCOUNT_ID = 11651 # (Lottershe)
else:
LOTTERY_ENABLED = False
LOTTERY_TICKET_COST = 0
LOTTERY_SINK_RATE = 0
LOTTERY_ROYALTY_RATE = 0
LOTTERY_ROYALTY_ACCOUNT_ID = 0
LOTTERY_MANAGER_ACCOUNT_ID = 0

View File

@ -5,6 +5,9 @@ import files.helpers.const as const
import files.helpers.lottery as lottery import files.helpers.lottery as lottery
import files.helpers.offsitementions as offsitementions import files.helpers.offsitementions as offsitementions
import files.helpers.stats as stats import files.helpers.stats as stats
import files.helpers.awards as awards
import files.routes.static as route_static
from files.routes.subs import sub_inactive_purge_task
@app.cli.command('cron', help='Run scheduled tasks.') @app.cli.command('cron', help='Run scheduled tasks.')
@click.option('--every-5m', is_flag=True, help='Call every 5 minutes.') @click.option('--every-5m', is_flag=True, help='Call every 5 minutes.')
@ -22,4 +25,8 @@ def cron(every_5m, every_1h, every_1d):
if every_1d: if every_1d:
stats.generate_charts_task(const.SITE) stats.generate_charts_task(const.SITE)
route_static.stats_cached()
awards.award_timers_bots_task()
sub_inactive_purge_task()

View File

@ -92,6 +92,8 @@ def check_if_end_lottery_task():
start_new_lottery_session() start_new_lottery_session()
return True return True
def lottery_ticket_net_value():
return LOTTERY_TICKET_COST - LOTTERY_SINK_RATE - LOTTERY_ROYALTY_RATE
def purchase_lottery_tickets(v, quantity=1): def purchase_lottery_tickets(v, quantity=1):
if quantity < 1: if quantity < 1:
@ -107,7 +109,7 @@ def purchase_lottery_tickets(v, quantity=1):
v.currently_held_lottery_tickets += quantity v.currently_held_lottery_tickets += quantity
v.total_held_lottery_tickets += quantity v.total_held_lottery_tickets += quantity
net_ticket_value = (LOTTERY_TICKET_COST - LOTTERY_SINK_RATE - LOTTERY_ROYALTY_RATE) * quantity net_ticket_value = lottery_ticket_net_value() * quantity
most_recent_lottery.prize += net_ticket_value most_recent_lottery.prize += net_ticket_value
most_recent_lottery.tickets_sold += quantity most_recent_lottery.tickets_sold += quantity
@ -115,7 +117,7 @@ def purchase_lottery_tickets(v, quantity=1):
beneficiary = g.db.query(User).get(LOTTERY_ROYALTY_ACCOUNT_ID) beneficiary = g.db.query(User).get(LOTTERY_ROYALTY_ACCOUNT_ID)
if beneficiary: if beneficiary and LOTTERY_ROYALTY_RATE:
beneficiary.coins += LOTTERY_ROYALTY_RATE * quantity beneficiary.coins += LOTTERY_ROYALTY_RATE * quantity
g.db.commit() g.db.commit()
@ -128,16 +130,16 @@ def grant_lottery_proceeds_to_manager(prize_value):
if manager: if manager:
manager.coins += prize_value manager.coins += prize_value
def grant_lottery_tickets_to_user(v, amount): def grant_lottery_tickets_to_user(v, quantity):
active_lottery = get_active_lottery() active_lottery = get_active_lottery()
prize_value = amount * LOTTERY_TICKET_COST prize_value = lottery_ticket_net_value() * quantity
if active_lottery: if active_lottery:
v.currently_held_lottery_tickets += amount v.currently_held_lottery_tickets += quantity
v.total_held_lottery_tickets += amount v.total_held_lottery_tickets += quantity
active_lottery.prize += prize_value active_lottery.prize += prize_value
active_lottery.tickets_sold += amount active_lottery.tickets_sold += quantity
grant_lottery_proceeds_to_manager(prize_value) grant_lottery_proceeds_to_manager(prize_value)

View File

@ -1,6 +1,7 @@
from flask import g from flask import g
import itertools import itertools
import requests import requests
from sqlalchemy import _or
import files.helpers.const as const import files.helpers.const as const
from files.classes.user import User from files.classes.user import User
from files.classes.comment import Comment from files.classes.comment import Comment
@ -13,9 +14,9 @@ from files.classes.notifications import Notification
def offsite_mentions_task(): def offsite_mentions_task():
if const.REDDIT_NOTIFS_SITE: if const.REDDIT_NOTIFS_SITE:
# Site-specific logic: send to JL1+, except on PCM JL3+ row_send_to = g.db.query(User.id)
jl_min = 3 if const.SITE_NAME == 'PCM' else 1 .filter(_or(User.admin_level >= const.REDDIT_NOTIFS_JL_MIN,
row_send_to = g.db.query(User.id).filter(User.admin_level >= jl_min).all() User.offsitementions == True)).all()
send_to = [x[0] for x in row_send_to] send_to = [x[0] for x in row_send_to]
site_mentions = get_mentions(const.REDDIT_NOTIFS_SITE) site_mentions = get_mentions(const.REDDIT_NOTIFS_SITE)

View File

@ -2,10 +2,14 @@ from flask import g
import time import time
import calendar import calendar
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from sqlalchemy import *
from files.classes.user import User from files.classes.user import User
from files.classes.submission import Submission from files.classes.submission import Submission
from files.classes.comment import Comment from files.classes.comment import Comment
from files.classes.votes import Vote, CommentVote
from files.classes.marsey import Marsey
from files.classes.award import AwardRelationship
from files.helpers.const import * from files.helpers.const import *
def generate_charts_task(site): def generate_charts_task(site):
@ -85,3 +89,58 @@ def chart(kind, site):
def chart_path(kind, site): def chart_path(kind, site):
return f'/{site}_{kind}.png' return f'/{site}_{kind}.png'
def stats(site=None):
day = int(time.time()) - 86400
week = int(time.time()) - 604800
posters = g.db.query(Submission.author_id).distinct(Submission.author_id).filter(Submission.created_utc > week).all()
commenters = g.db.query(Comment.author_id).distinct(Comment.author_id).filter(Comment.created_utc > week).all()
voters = g.db.query(Vote.user_id).distinct(Vote.user_id).filter(Vote.created_utc > week).all()
commentvoters = g.db.query(CommentVote.user_id).distinct(CommentVote.user_id).filter(CommentVote.created_utc > week).all()
active_users = set(posters) | set(commenters) | set(voters) | set(commentvoters)
stats = {
"marseys": g.db.query(Marsey).count(),
"users": g.db.query(User).count(),
"private users": g.db.query(User).filter_by(is_private=True).count(),
"banned users": g.db.query(User).filter(User.is_banned > 0).count(),
"verified email users": g.db.query(User).filter_by(is_activated=True).count(),
"coins in circulation": g.db.query(func.sum(User.coins)).scalar(),
"total shop sales": g.db.query(func.sum(User.coins_spent)).scalar(),
"signups last 24h": g.db.query(User).filter(User.created_utc > day).count(),
"total posts": g.db.query(Submission).count(),
"posting users": g.db.query(Submission.author_id).distinct().count(),
"listed posts": g.db.query(Submission).filter_by(is_banned=False).filter(Submission.deleted_utc == 0).count(),
"removed posts (by admins)": g.db.query(Submission).filter_by(is_banned=True).count(),
"deleted posts (by author)": g.db.query(Submission).filter(Submission.deleted_utc > 0).count(),
"posts last 24h": g.db.query(Submission).filter(Submission.created_utc > day).count(),
"total comments": g.db.query(Comment).filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"commenting users": g.db.query(Comment.author_id).distinct().count(),
"removed comments (by admins)": g.db.query(Comment).filter_by(is_banned=True).count(),
"deleted comments (by author)": g.db.query(Comment).filter(Comment.deleted_utc > 0).count(),
"comments last_24h": g.db.query(Comment).filter(Comment.created_utc > day, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"post votes": g.db.query(Vote).count(),
"post voting users": g.db.query(Vote.user_id).distinct().count(),
"comment votes": g.db.query(CommentVote).count(),
"comment voting users": g.db.query(CommentVote.user_id).distinct().count(),
"total upvotes": g.db.query(Vote).filter_by(vote_type=1).count() + g.db.query(CommentVote.comment_id).filter_by(vote_type=1).count(),
"total downvotes": g.db.query(Vote).filter_by(vote_type=-1).count() + g.db.query(CommentVote.comment_id).filter_by(vote_type=-1).count(),
"total awards": g.db.query(AwardRelationship).count(),
"awards given": g.db.query(AwardRelationship).filter(or_(AwardRelationship.submission_id != None, AwardRelationship.comment_id != None)).count(),
"users who posted, commented, or voted in the past 7 days": len(active_users),
}
if site == 'rDrama':
stats2 = {
"House furry members": g.db.query(User).filter(User.house.like('Furry%')).count(),
"House femboy members": g.db.query(User).filter(User.house.like('Femboy%')).count(),
"House vampire members": g.db.query(User).filter(User.house.like('Vampire%')).count(),
"House racist members": g.db.query(User).filter(User.house.like('Racist%')).count(),
"House furry total truescore": g.db.query(func.sum(User.truecoins)).filter(User.house.like('Furry%')).scalar(),
"House femboy total truescore": g.db.query(func.sum(User.truecoins)).filter(User.house.like('Femboy%')).scalar(),
"House vampire total truescore": g.db.query(func.sum(User.truecoins)).filter(User.house.like('Vampire%')).scalar(),
"House racist total truescore": g.db.query(func.sum(User.truecoins)).filter(User.house.like('Racist%')).scalar(),
}
stats.update(stats2)
return stats

View File

@ -527,25 +527,7 @@ def change_settings(v, setting):
if site_settings[setting]: word = 'enable' if site_settings[setting]: word = 'enable'
else: word = 'disable' else: word = 'disable'
body = f"@{v.username} has {word}d `{setting}` in the [admin dashboard](/admin)!" notify_mod_action(v.id, f"@{v.username} has {word}d `{setting}` in the [admin dashboard](/admin)!")
body_html = sanitize(body)
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
sentto=2,
distinguish_level=6
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
for admin in g.db.query(User).filter(User.admin_level > 2, User.id != v.id).all():
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)
ma = ModAction( ma = ModAction(
kind=f"{word}_{setting}", kind=f"{word}_{setting}",
@ -1018,29 +1000,7 @@ def shadowban(user_id, v):
g.db.add(ma) g.db.add(ma)
cache.delete_memoized(frontlist) cache.delete_memoized(frontlist)
notify_mod_action(v.id, f"@{v.username} has shadowbanned @{user.username}")
body = f"@{v.username} has shadowbanned @{user.username}"
body_html = sanitize(body)
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
distinguish_level=6
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
for admin in g.db.query(User).filter(User.admin_level > 2, User.id != v.id).all():
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)
g.db.commit() g.db.commit()
return {"message": "User shadowbanned!"} return {"message": "User shadowbanned!"}
@ -1176,28 +1136,7 @@ def ban_user(user_id, v):
g.db.add(comment) g.db.add(comment)
except: pass except: pass
notify_mod_action(v.id, f"@{v.username} has banned @{user.username} ({note})")
body = f"@{v.username} has banned @{user.username} ({note})"
body_html = sanitize(body)
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
distinguish_level=6
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
for admin in g.db.query(User).filter(User.admin_level > 2, User.id != v.id).all():
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)
g.db.commit() g.db.commit()
if 'redir' in request.values: return redirect(user.url) if 'redir' in request.values: return redirect(user.url)
@ -1273,23 +1212,10 @@ def ban_post(post_id, v):
g.db.add(v) g.db.add(v)
if v.id != post.author_id: if v.id != post.author_id:
body = f"@{v.username} has removed [{post.title}]({post.shortlink})" notify_mod_action(v.id, f"@{v.username} has removed [{post.title}]({post.shortlink})")
body_html = sanitize(body)
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
distinguish_level=6
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
for admin in g.db.query(User).filter(User.admin_level > 2, User.id != v.id).all():
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)
requests.post(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS, json={'files': [f"{SITE_FULL}/logged_out/"]}, timeout=5)
requests.post(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/purge_cache',
headers=CF_HEADERS, json={'files': [f"{SITE_FULL}/logged_out/"]}, timeout=5)
g.db.commit() g.db.commit()
return {"message": "Post removed!"} return {"message": "Post removed!"}
@ -1492,20 +1418,7 @@ def api_ban_comment(c_id, v):
g.db.add(ma) g.db.add(ma)
if v.id != comment.author_id: if v.id != comment.author_id:
body = f"@{v.username} has removed [comment]({comment.shortlink})" notify_mod_action(v.id, f"@{v.username} has removed [comment]({comment.shortlink})")
body_html = sanitize(body)
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
level=1,
body_html=body_html,
distinguish_level=6
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id
for admin in g.db.query(User).filter(User.admin_level > 2, User.id != v.id).all():
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
g.db.add(notif)
g.db.commit() g.db.commit()
return {"message": "Comment removed!"} return {"message": "Comment removed!"}

View File

@ -4,6 +4,7 @@ from files.helpers.alerts import *
from files.helpers.get import * from files.helpers.get import *
from files.helpers.const import * from files.helpers.const import *
from files.helpers.discord import * from files.helpers.discord import *
from files.helpers.actions import badge_grant
from files.classes.award import * from files.classes.award import *
from .front import frontlist from .front import frontlist
from flask import g, request from flask import g, request
@ -301,6 +302,10 @@ def award_thing(v, thing_type, id):
g.db.add(new_badge) g.db.add(new_badge)
g.db.flush() g.db.flush()
send_notification(author.id, f"@AutoJanny has given you the following profile badge:\n\n![]({new_badge.path})\n\n{new_badge.name}") send_notification(author.id, f"@AutoJanny has given you the following profile badge:\n\n![]({new_badge.path})\n\n{new_badge.name}")
elif kind == "offsitementions":
author.offsitementions = True
new_badge = badge_grant(user_id=author.id, badge_id=140, commit=False)
send_notification(author.id, f"@AutoJanny has given you the following profile badge:\n\n![]({new_badge.path})\n\n{new_badge.name}")
elif kind == "alt": elif kind == "alt":
author.alt = True author.alt = True
if not author.has_badge(84): if not author.has_badge(84):
@ -443,3 +448,4 @@ def admin_userawards_post(v):
if v.admin_level != 3: return render_template("admin/awards.html", awards=list(AWARDS3.values()), v=v) if v.admin_level != 3: return render_template("admin/awards.html", awards=list(AWARDS3.values()), v=v)
return render_template("admin/awards.html", awards=list(AWARDS.values()), v=v) return render_template("admin/awards.html", awards=list(AWARDS.values()), v=v)

View File

@ -3,6 +3,7 @@ from files.helpers.get import *
from files.helpers.discord import * from files.helpers.discord import *
from files.__main__ import app, cache, limiter from files.__main__ import app, cache, limiter
from files.classes.submission import Submission from files.classes.submission import Submission
from files.helpers.awards import award_timers
defaulttimefilter = environ.get("DEFAULT_TIME_FILTER", "all").strip() defaulttimefilter = environ.get("DEFAULT_TIME_FILTER", "all").strip()
@ -43,9 +44,10 @@ def notifications(v):
messages = request.values.get('messages') messages = request.values.get('messages')
modmail = request.values.get('modmail') modmail = request.values.get('modmail')
modactions = request.values.get('modactions')
posts = request.values.get('posts') posts = request.values.get('posts')
reddit = request.values.get('reddit') reddit = request.values.get('reddit')
if modmail and v.admin_level > 1: if modmail and v.admin_level >= 2:
comments = g.db.query(Comment).filter(Comment.sentto==2).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all() comments = g.db.query(Comment).filter(Comment.sentto==2).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists = (len(comments) > 25) next_exists = (len(comments) > 25)
listing = comments[:25] listing = comments[:25]
@ -72,7 +74,25 @@ def notifications(v):
if n.created_utc > 1620391248: c.notif_utc = n.created_utc if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c) listing.append(c)
g.db.commit() next_exists = (len(notifications) > len(listing))
elif modactions:
notifications = g.db.query(Notification, Comment) \
.join(Comment, Notification.comment_id == Comment.id) \
.filter(Notification.user_id == v.id,
Comment.body_html.like(f'%<p>{NOTIF_MODACTION_PREFIX}%'),
Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID) \
.order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing)) next_exists = (len(notifications) > len(listing))
elif reddit: elif reddit:
@ -90,8 +110,6 @@ def notifications(v):
if n.created_utc > 1620391248: c.notif_utc = n.created_utc if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c) listing.append(c)
g.db.commit()
next_exists = (len(notifications) > len(listing)) next_exists = (len(notifications) > len(listing))
else: else:
comments = g.db.query(Comment, Notification).join(Notification, Notification.comment_id == Comment.id).filter( comments = g.db.query(Comment, Notification).join(Notification, Notification.comment_id == Comment.id).filter(
@ -99,7 +117,8 @@ def notifications(v):
Comment.is_banned == False, Comment.is_banned == False,
Comment.deleted_utc == 0, Comment.deleted_utc == 0,
Comment.author_id != AUTOJANNY_ID, Comment.author_id != AUTOJANNY_ID,
Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%') Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%'),
Comment.body_html.notlike(f'%<p>{NOTIF_MODACTION_PREFIX}%')
).order_by(Notification.created_utc.desc()) ).order_by(Notification.created_utc.desc())
if not (v and (v.shadowbanned or v.admin_level > 2)): if not (v and (v.shadowbanned or v.admin_level > 2)):
@ -153,7 +172,8 @@ def notifications(v):
next_exists=next_exists, next_exists=next_exists,
page=page, page=page,
standalone=True, standalone=True,
render_replies=True render_replies=True,
NOTIF_MODACTION_JL_MIN=NOTIF_MODACTION_JL_MIN,
) )
@ -219,85 +239,7 @@ def front_all(v, sub=None, subdomain=None):
if v: if v:
if v.hidevotedon: posts = [x for x in posts if not hasattr(x, 'voted') or not x.voted] if v.hidevotedon: posts = [x for x in posts if not hasattr(x, 'voted') or not x.voted]
award_timers(v)
if v.patron_utc and v.patron_utc < time.time():
v.patron = 0
v.patron_utc = 0
send_repeatable_notification(v.id, "Your paypig status has expired!")
if v.discord_id: remove_role(v, "1")
g.db.add(v)
g.db.commit()
if v.unban_utc and v.unban_utc < time.time():
v.is_banned = 0
v.unban_utc = 0
v.ban_evade = 0
send_repeatable_notification(v.id, "You have been unbanned!")
g.db.add(v)
g.db.commit()
if v.agendaposter and v.agendaposter < time.time():
v.agendaposter = 0
send_repeatable_notification(v.id, "Your chud theme has expired!")
g.db.add(v)
badge = v.has_badge(28)
if badge: g.db.delete(badge)
g.db.commit()
if v.flairchanged and v.flairchanged < time.time():
v.flairchanged = None
send_repeatable_notification(v.id, "Your flair lock has expired. You can now change your flair!")
g.db.add(v)
badge = v.has_badge(96)
if badge: g.db.delete(badge)
g.db.commit()
if v.marseyawarded and v.marseyawarded < time.time():
v.marseyawarded = None
send_repeatable_notification(v.id, "Your marsey award has expired!")
g.db.add(v)
badge = v.has_badge(98)
if badge: g.db.delete(badge)
g.db.commit()
if v.longpost and v.longpost < time.time():
v.longpost = None
send_repeatable_notification(v.id, "Your pizzashill award has expired!")
g.db.add(v)
badge = v.has_badge(97)
if badge: g.db.delete(badge)
g.db.commit()
if v.bird and v.bird < time.time():
v.bird = None
send_repeatable_notification(v.id, "Your bird site award has expired!")
g.db.add(v)
badge = v.has_badge(95)
if badge: g.db.delete(badge)
g.db.commit()
if v.progressivestack and v.progressivestack < time.time():
v.progressivestack = None
send_repeatable_notification(v.id, "Your progressive stack has expired!")
g.db.add(v)
badge = v.has_badge(94)
if badge: g.db.delete(badge)
g.db.commit()
if v.rehab and v.rehab < time.time():
v.rehab = None
send_repeatable_notification(v.id, "Your rehab has finished!")
g.db.add(v)
badge = v.has_badge(109)
if badge: g.db.delete(badge)
g.db.commit()
if v.deflector and v.deflector < time.time():
v.deflector = None
send_repeatable_notification(v.id, "Your deflector has expired!")
g.db.add(v)
g.db.commit()
if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists} if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists}
return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, ccmode=ccmode, sub=sub, home=True) return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, ccmode=ccmode, sub=sub, home=True)

View File

@ -69,151 +69,12 @@ def sidebar(v):
@app.get("/stats") @app.get("/stats")
@auth_required @auth_required
def participation_stats(v): def participation_stats(v):
return render_template("admin/content_stats.html",
return render_template("admin/content_stats.html", v=v, title="Content Statistics", data=stats(site=SITE)) v=v, title="Content Statistics", data=stats_cached())
@cache.memoize(timeout=86400) @cache.memoize(timeout=86400)
def stats(site=None): def stats_cached():
day = int(time.time()) - 86400 return statshelper.stats(SITE_NAME)
week = int(time.time()) - 604800
posters = g.db.query(Submission.author_id).distinct(Submission.author_id).filter(Submission.created_utc > week).all()
commenters = g.db.query(Comment.author_id).distinct(Comment.author_id).filter(Comment.created_utc > week).all()
voters = g.db.query(Vote.user_id).distinct(Vote.user_id).filter(Vote.created_utc > week).all()
commentvoters = g.db.query(CommentVote.user_id).distinct(CommentVote.user_id).filter(CommentVote.created_utc > week).all()
active_users = set(posters) | set(commenters) | set(voters) | set(commentvoters)
stats = {"marseys": g.db.query(Marsey).count(),
"users": g.db.query(User).count(),
"private users": g.db.query(User).filter_by(is_private=True).count(),
"banned users": g.db.query(User).filter(User.is_banned > 0).count(),
"verified email users": g.db.query(User).filter_by(is_activated=True).count(),
"coins in circulation": g.db.query(func.sum(User.coins)).scalar(),
"total shop sales": g.db.query(func.sum(User.coins_spent)).scalar(),
"signups last 24h": g.db.query(User).filter(User.created_utc > day).count(),
"total posts": g.db.query(Submission).count(),
"posting users": g.db.query(Submission.author_id).distinct().count(),
"listed posts": g.db.query(Submission).filter_by(is_banned=False).filter(Submission.deleted_utc == 0).count(),
"removed posts (by admins)": g.db.query(Submission).filter_by(is_banned=True).count(),
"deleted posts (by author)": g.db.query(Submission).filter(Submission.deleted_utc > 0).count(),
"posts last 24h": g.db.query(Submission).filter(Submission.created_utc > day).count(),
"total comments": g.db.query(Comment).filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"commenting users": g.db.query(Comment.author_id).distinct().count(),
"removed comments (by admins)": g.db.query(Comment).filter_by(is_banned=True).count(),
"deleted comments (by author)": g.db.query(Comment).filter(Comment.deleted_utc > 0).count(),
"comments last_24h": g.db.query(Comment).filter(Comment.created_utc > day, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"post votes": g.db.query(Vote).count(),
"post voting users": g.db.query(Vote.user_id).distinct().count(),
"comment votes": g.db.query(CommentVote).count(),
"comment voting users": g.db.query(CommentVote.user_id).distinct().count(),
"total upvotes": g.db.query(Vote).filter_by(vote_type=1).count() + g.db.query(CommentVote.comment_id).filter_by(vote_type=1).count(),
"total downvotes": g.db.query(Vote).filter_by(vote_type=-1).count() + g.db.query(CommentVote.comment_id).filter_by(vote_type=-1).count(),
"total awards": g.db.query(AwardRelationship).count(),
"awards given": g.db.query(AwardRelationship).filter(or_(AwardRelationship.submission_id != None, AwardRelationship.comment_id != None)).count(),
"users who posted, commented, or voted in the past 7 days": len(active_users),
}
if SITE_NAME == 'rDrama':
furries1 = g.db.query(User).filter(User.house.like('Furry%')).count()
femboys1 = g.db.query(User).filter(User.house.like('Femboy%')).count()
vampires1 = g.db.query(User).filter(User.house.like('Vampire%')).count()
racists1 = g.db.query(User).filter(User.house.like('Racist%')).count()
furries2 = g.db.query(func.sum(User.truecoins)).filter(User.house.like('Furry%')).scalar()
femboys2 = g.db.query(func.sum(User.truecoins)).filter(User.house.like('Femboy%')).scalar()
vampires2 = g.db.query(func.sum(User.truecoins)).filter(User.house.like('Vampire%')).scalar()
racists2 = g.db.query(func.sum(User.truecoins)).filter(User.house.like('Racist%')).scalar()
stats2 = {"House furry members": furries1,
"House femboy members": femboys1,
"House vampire members": vampires1,
"House racist members": racists1,
"House furry total truescore": furries2,
"House femboy total truescore": femboys2,
"House vampire total truescore": vampires2,
"House racist total truescore": racists2,
}
stats.update(stats2)
accs = g.db.query(User).filter(User.id.in_(bots))
for u in accs:
g.db.add(u)
if u.patron_utc and u.patron_utc < time.time():
u.patron = 0
u.patron_utc = 0
if u.unban_utc and u.unban_utc < time.time():
u.is_banned = 0
u.unban_utc = 0
u.ban_evade = 0
if u.agendaposter and u.agendaposter < time.time():
u.agendaposter = 0
badge = u.has_badge(28)
if badge: g.db.delete(badge)
if u.flairchanged and u.flairchanged < time.time():
u.flairchanged = None
badge = u.has_badge(96)
if badge: g.db.delete(badge)
if u.marseyawarded and u.marseyawarded < time.time():
u.marseyawarded = None
badge = u.has_badge(98)
if badge: g.db.delete(badge)
if u.longpost and u.longpost < time.time():
u.longpost = None
badge = u.has_badge(97)
if badge: g.db.delete(badge)
if u.bird and u.bird < time.time():
u.bird = None
badge = u.has_badge(95)
if badge: g.db.delete(badge)
if u.progressivestack and u.progressivestack < time.time():
u.progressivestack = None
badge = u.has_badge(94)
if badge: g.db.delete(badge)
if u.rehab and u.rehab < time.time():
u.rehab = None
badge = u.has_badge(109)
if badge: g.db.delete(badge)
if u.deflector and u.deflector < time.time():
u.deflector = None
one_week_ago = time.time() - 604800
active_holes = [x[0] for x in g.db.query(Submission.sub).distinct().filter(Submission.sub != None, Submission.created_utc > one_week_ago).all()]
dead_holes = g.db.query(Sub).filter(Sub.name.notin_(active_holes)).all()
names = [x.name for x in dead_holes]
posts = g.db.query(Submission).filter(Submission.sub.in_(names)).all()
for post in posts:
post.sub = None
g.db.add(post)
to_delete = g.db.query(Mod).filter(Mod.sub.in_(names)).all() + g.db.query(Exile).filter(Exile.sub.in_(names)).all() + g.db.query(SubBlock).filter(SubBlock.sub.in_(names)).all() + g.db.query(SubSubscription).filter(SubSubscription.sub.in_(names)).all()
for x in to_delete:
g.db.delete(x)
g.db.flush()
for x in dead_holes:
g.db.delete(x)
g.db.commit()
return stats
@app.get("/chart") @app.get("/chart")
def chart(): def chart():

View File

@ -448,4 +448,35 @@ def sub_sidebar(v, sub):
@auth_desired @auth_desired
def subs(v): def subs(v):
subs = g.db.query(Sub, func.count(Submission.sub)).outerjoin(Submission, Sub.name == Submission.sub).group_by(Sub.name).order_by(func.count(Submission.sub).desc()).all() subs = g.db.query(Sub, func.count(Submission.sub)).outerjoin(Submission, Sub.name == Submission.sub).group_by(Sub.name).order_by(func.count(Submission.sub).desc()).all()
return render_template('sub/subs.html', v=v, subs=subs) return render_template('sub/subs.html', v=v, subs=subs)
def sub_inactive_purge_task():
if not HOLE_INACTIVITY_DELETION:
return False
one_week_ago = time.time() - 604800
active_holes = [x[0] for x in g.db.query(Submission.sub).distinct() \
.filter(Submission.sub != None, Submission.created_utc > one_week_ago).all()]
dead_holes = g.db.query(Sub).filter(Sub.name.notin_(active_holes)).all()
names = [x.name for x in dead_holes]
posts = g.db.query(Submission).filter(Submission.sub.in_(names)).all()
for post in posts:
post.sub = None
g.db.add(post)
to_delete = g.db.query(Mod).filter(Mod.sub.in_(names)).all() \
+ g.db.query(Exile).filter(Exile.sub.in_(names)).all() \
+ g.db.query(SubBlock).filter(SubBlock.sub.in_(names)).all() \
+ g.db.query(SubSubscription).filter(SubSubscription.sub.in_(names)).all()
for x in to_delete:
g.db.delete(x)
g.db.flush()
for x in dead_holes:
g.db.delete(x)
g.db.commit()
return True

View File

@ -14,7 +14,7 @@
<div class="row border-bottom bg-white w-200 pr-0" style="overflow: visible;"> <div class="row border-bottom bg-white w-200 pr-0" style="overflow: visible;">
<div class="col p-0 w-100"> <div class="col p-0 w-100">
<ul class="nav settings-nav" style="padding:0 0 0 20px"> <ul class="nav settings-nav" style="padding:0 0 0 20px" id="notifications--nav-list">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link py-3{% if not '=true' in request.full_path %} active{% endif %}" href="/notifications"> <a class="nav-link py-3{% if not '=true' in request.full_path %} active{% endif %}" href="/notifications">
All {% if v.normal_count %} <span class="font-weight-bold" style="color:red">({{v.normal_count}})</span>{% endif %} All {% if v.normal_count %} <span class="font-weight-bold" style="color:red">({{v.normal_count}})</span>{% endif %}
@ -30,14 +30,21 @@
Messages Messages
</a> </a>
</li> </li>
{% if v.admin_level > 1 %} {% if v.admin_level >= NOTIF_MODACTION_JL_MIN %}
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?modactions=true' in request.full_path %} active{% endif %}" href="/notifications?modactions=true">
Modactions {% if v.modaction_notifications_count %}<span class="font-weight-bold" style="color:#e5990d">({{v.modaction_notifications_count}})</span>{% endif %}
</a>
</li>
{% endif %}
{% if v.admin_level >= 2 %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link py-3{% if '/notifications?modmail=true' in request.full_path %} active{% endif %}" href="/notifications?modmail=true"> <a class="nav-link py-3{% if '/notifications?modmail=true' in request.full_path %} active{% endif %}" href="/notifications?modmail=true">
Modmail Modmail
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if v.admin_level %} {% if v.can_view_offsitementions %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link py-3{% if '/notifications?reddit=true' in request.full_path %} active{% endif %}" href="/notifications?reddit=true"> <a class="nav-link py-3{% if '/notifications?reddit=true' in request.full_path %} active{% endif %}" href="/notifications?reddit=true">
Reddit {% if v.reddit_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.reddit_notifications_count}})</span>{% endif %} Reddit {% if v.reddit_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.reddit_notifications_count}})</span>{% endif %}

View File

@ -15,8 +15,8 @@
<th>#</th> <th>#</th>
<th>Name</th> <th>Name</th>
<th role="button" onclick="sort_table(2)">Posts</th> <th role="button" onclick="sort_table(2)">Posts</th>
<th role="button" onclick="sort_table(3)">Blockers</th> <th role="button" onclick="sort_table(3)">Followers</th>
<th role="button" onclick="sort_table(4)">Blockers</th>
</tr> </tr>
</thead> </thead>
{% for sub, count in subs %} {% for sub, count in subs %}
@ -24,6 +24,7 @@
<td>{{loop.index}}</td> <td>{{loop.index}}</td>
<td><a href="/h/{{sub.name}}">{{sub.name}}</a></td> <td><a href="/h/{{sub.name}}">{{sub.name}}</a></td>
<td><a href="/h/{{sub.name}}">{{count}}</a></td> <td><a href="/h/{{sub.name}}">{{count}}</a></td>
<td><a href="/h/{{sub.name}}/followers">{{sub.follow_num}}</a></td>
<td><a href="/h/{{sub.name}}/blockers">{{sub.block_num}}</a></td> <td><a href="/h/{{sub.name}}/blockers">{{sub.block_num}}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -1,6 +1,6 @@
{%- {%-
set CACHE_VER = { set CACHE_VER = {
'css/main.css': 297, 'css/main.css': 299,
'css/4chan.css': 59, 'css/4chan.css': 59,
'css/classic.css': 59, 'css/classic.css': 59,

View File

@ -1984,6 +1984,7 @@ COPY public.badge_defs (id, name, description) FROM stdin;
137 Lottershe Winner This user won the Lottershe grand prize. 137 Lottershe Winner This user won the Lottershe grand prize.
138 Dan This user is a Certified Dan Enthusiast. 138 Dan This user is a Certified Dan Enthusiast.
139 Kristallmopt This user was fired from a volunteer position 139 Kristallmopt This user was fired from a volunteer position
140 Y'all Seein' Eye Gets notified when other sites talk about us
4 White Hat Discreetly reported an exploit 4 White Hat Discreetly reported an exploit
1 Alpha User Joined during open alpha 1 Alpha User Joined during open alpha
2 Verified Email Verified Email 2 Verified Email Verified Email
@ -2087,7 +2088,7 @@ COPY public.badge_defs (id, name, description) FROM stdin;
-- Name: badge_defs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - -- Name: badge_defs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
-- --
SELECT pg_catalog.setval('public.badge_defs_id_seq', 139, true); SELECT pg_catalog.setval('public.badge_defs_id_seq', 140, true);
-- --