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
@lazy
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)
eye = Column(Boolean)
alt = Column(Boolean)
offsitementions = Column(Boolean, default=False, nullable=False)
frontsize = Column(Integer, default=25)
controversial = Column(Boolean, default=False)
bio = deferred(Column(String))
@ -228,7 +229,11 @@ class User(Base):
if self.has_badge(badge): discount -= discounts[badge]
return discount
@property
@lazy
def can_view_offsitementions(self):
return self.offsitementions or self.admin_level >= REDDIT_NOTIFS_JL_MIN
@property
@lazy
@ -446,22 +451,44 @@ class User(Base):
@property
@lazy
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
@lazy
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
@lazy
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
@lazy
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
@lazy

View File

@ -1,4 +1,4 @@
from .__main__ import app, db_session
from .__main__ import app, db_session, cache
from flask import g
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: Add `replace=False` parameter which, when set true, removes any
# 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()
if not user:
return False
return None
elif user.has_badge(badge_id):
return True
return None
badge = Badge(
badge_id=int(badge_id),
@ -20,5 +20,8 @@ def badge_grant(user_id, badge_id, desc='', url=''):
)
g.db.add(badge)
g.db.commit()
return True
if commit:
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)
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"
USER_TITLE_COST = 0
HOLE_INACTIVITY_DELETION = False
if SITE in {'rdrama.net','devrama.xyz'}:
HOLE_COST = 50000
HOLE_INACTIVITY_DELETION = True
USER_TITLE_COST = 25
NOTIFICATIONS_ID = 1046
AUTOJANNY_ID = 2360
@ -660,6 +662,14 @@ AWARDS = {
"color": "text-silver",
"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": {
"kind": "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'
else: patron = 'Patron'
NOTIF_MODACTION_PREFIX = '[Modaction] '
NOTIF_MODACTION_JL_MIN = 2
REDDIT_NOTIFS_SITE = []
REDDIT_NOTIFS_USERS = {}
REDDIT_NOTIFS_JL_MIN = 1
if SITE_NAME == 'rDrama':
REDDIT_NOTIFS_SITE = ['rdrama', 'marsey',]
@ -814,6 +828,7 @@ if SITE_NAME == 'rDrama':
}
elif SITE_NAME == 'PCM':
REDDIT_NOTIFS_SITE = ['pcmemes.net',]
REDDIT_NOTIFS_JL_MIN = 3
discounts = {
# 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
# 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':
LOTTERY_ENABLED = True
LOTTERY_TICKET_COST = 12
LOTTERY_SINK_RATE = 3
LOTTERY_ROYALTY_RATE = 1
LOTTERY_ROYALTY_RATE = 0
LOTTERY_ROYALTY_ACCOUNT_ID = 1387 # (Chapose)
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.offsitementions as offsitementions
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.')
@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:
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()
return True
def lottery_ticket_net_value():
return LOTTERY_TICKET_COST - LOTTERY_SINK_RATE - LOTTERY_ROYALTY_RATE
def purchase_lottery_tickets(v, quantity=1):
if quantity < 1:
@ -107,7 +109,7 @@ def purchase_lottery_tickets(v, quantity=1):
v.currently_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.tickets_sold += quantity
@ -115,7 +117,7 @@ def purchase_lottery_tickets(v, quantity=1):
beneficiary = g.db.query(User).get(LOTTERY_ROYALTY_ACCOUNT_ID)
if beneficiary:
if beneficiary and LOTTERY_ROYALTY_RATE:
beneficiary.coins += LOTTERY_ROYALTY_RATE * quantity
g.db.commit()
@ -128,16 +130,16 @@ def grant_lottery_proceeds_to_manager(prize_value):
if manager:
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()
prize_value = amount * LOTTERY_TICKET_COST
prize_value = lottery_ticket_net_value() * quantity
if active_lottery:
v.currently_held_lottery_tickets += amount
v.total_held_lottery_tickets += amount
v.currently_held_lottery_tickets += quantity
v.total_held_lottery_tickets += quantity
active_lottery.prize += prize_value
active_lottery.tickets_sold += amount
active_lottery.tickets_sold += quantity
grant_lottery_proceeds_to_manager(prize_value)

View File

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

View File

@ -2,10 +2,14 @@ from flask import g
import time
import calendar
import matplotlib.pyplot as plt
from sqlalchemy import *
from files.classes.user import User
from files.classes.submission import Submission
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 *
def generate_charts_task(site):
@ -85,3 +89,58 @@ def chart(kind, site):
def chart_path(kind, site):
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'
else: word = 'disable'
body = 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)
notify_mod_action(v.id, f"@{v.username} has {word}d `{setting}` in the [admin dashboard](/admin)!")
ma = ModAction(
kind=f"{word}_{setting}",
@ -1018,29 +1000,7 @@ def shadowban(user_id, v):
g.db.add(ma)
cache.delete_memoized(frontlist)
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)
notify_mod_action(v.id, f"@{v.username} has shadowbanned @{user.username}")
g.db.commit()
return {"message": "User shadowbanned!"}
@ -1176,28 +1136,7 @@ def ban_user(user_id, v):
g.db.add(comment)
except: pass
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)
notify_mod_action(v.id, f"@{v.username} has banned @{user.username} ({note})")
g.db.commit()
if 'redir' in request.values: return redirect(user.url)
@ -1273,23 +1212,10 @@ def ban_post(post_id, v):
g.db.add(v)
if v.id != post.author_id:
body = 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)
notify_mod_action(v.id, f"@{v.username} has removed [{post.title}]({post.shortlink})")
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()
return {"message": "Post removed!"}
@ -1492,20 +1418,7 @@ def api_ban_comment(c_id, v):
g.db.add(ma)
if v.id != comment.author_id:
body = 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)
notify_mod_action(v.id, f"@{v.username} has removed [comment]({comment.shortlink})")
g.db.commit()
return {"message": "Comment removed!"}

View File

@ -4,6 +4,7 @@ from files.helpers.alerts import *
from files.helpers.get import *
from files.helpers.const import *
from files.helpers.discord import *
from files.helpers.actions import badge_grant
from files.classes.award import *
from .front import frontlist
from flask import g, request
@ -301,6 +302,10 @@ def award_thing(v, thing_type, id):
g.db.add(new_badge)
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}")
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":
author.alt = True
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)
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.__main__ import app, cache, limiter
from files.classes.submission import Submission
from files.helpers.awards import award_timers
defaulttimefilter = environ.get("DEFAULT_TIME_FILTER", "all").strip()
@ -43,9 +44,10 @@ def notifications(v):
messages = request.values.get('messages')
modmail = request.values.get('modmail')
modactions = request.values.get('modactions')
posts = request.values.get('posts')
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()
next_exists = (len(comments) > 25)
listing = comments[:25]
@ -72,7 +74,25 @@ def notifications(v):
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
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))
elif reddit:
@ -90,8 +110,6 @@ def notifications(v):
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
g.db.commit()
next_exists = (len(notifications) > len(listing))
else:
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.deleted_utc == 0,
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())
if not (v and (v.shadowbanned or v.admin_level > 2)):
@ -153,7 +172,8 @@ def notifications(v):
next_exists=next_exists,
page=page,
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.hidevotedon: posts = [x for x in posts if not hasattr(x, 'voted') or not x.voted]
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()
award_timers(v)
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)

View File

@ -69,151 +69,12 @@ def sidebar(v):
@app.get("/stats")
@auth_required
def participation_stats(v):
return render_template("admin/content_stats.html", v=v, title="Content Statistics", data=stats(site=SITE))
return render_template("admin/content_stats.html",
v=v, title="Content Statistics", data=stats_cached())
@cache.memoize(timeout=86400)
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_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
def stats_cached():
return statshelper.stats(SITE_NAME)
@app.get("/chart")
def chart():

View File

@ -448,4 +448,35 @@ def sub_sidebar(v, sub):
@auth_desired
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()
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="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">
<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 %}
@ -30,14 +30,21 @@
Messages
</a>
</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">
<a class="nav-link py-3{% if '/notifications?modmail=true' in request.full_path %} active{% endif %}" href="/notifications?modmail=true">
Modmail
</a>
</li>
{% endif %}
{% if v.admin_level %}
{% if v.can_view_offsitementions %}
<li class="nav-item">
<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 %}

View File

@ -15,8 +15,8 @@
<th>#</th>
<th>Name</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>
</thead>
{% for sub, count in subs %}
@ -24,6 +24,7 @@
<td>{{loop.index}}</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}}/followers">{{sub.follow_num}}</a></td>
<td><a href="/h/{{sub.name}}/blockers">{{sub.block_num}}</a></td>
</tr>
{% endfor %}

View File

@ -1,6 +1,6 @@
{%-
set CACHE_VER = {
'css/main.css': 297,
'css/main.css': 299,
'css/4chan.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.
138 Dan This user is a Certified Dan Enthusiast.
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
1 Alpha User Joined during open alpha
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: -
--
SELECT pg_catalog.setval('public.badge_defs_id_seq', 139, true);
SELECT pg_catalog.setval('public.badge_defs_id_seq', 140, true);
--