2022-05-04 23:09:46 +00:00
import time
2022-05-05 08:46:20 +00:00
import re
2022-05-04 23:09:46 +00:00
from os import remove
from PIL import Image as IMAGE
from files . helpers . wrappers import *
from files . helpers . alerts import *
from files . helpers . sanitize import *
from files . helpers . security import *
from files . helpers . get import *
2022-05-22 16:13:19 +00:00
from files . helpers . media import *
2022-05-04 23:09:46 +00:00
from files . helpers . const import *
2022-06-15 19:33:21 +00:00
from files . helpers . actions import *
2022-10-13 07:27:56 +00:00
from files . helpers . cloudflare import *
2022-05-04 23:09:46 +00:00
from files . classes import *
from flask import *
from files . __main__ import app , cache , limiter
from . front import frontlist
2022-09-23 12:33:58 +00:00
from . login import check_for_alts
2022-06-10 20:09:59 +00:00
import datetime
2022-05-04 23:09:46 +00:00
import requests
from urllib . parse import quote , urlencode
2022-06-18 00:35:27 +00:00
@app.post ( ' /kippy ' )
2022-10-06 03:26:39 +00:00
@admin_level_required ( PERMS [ ' PRINT_MARSEYBUX_FOR_KIPPY_ON_PCMEMES ' ] )
2022-06-18 00:35:27 +00:00
def kippy ( v ) :
2022-10-20 20:53:52 +00:00
if SITE != ' pcmemes.net ' : abort ( 404 )
2022-06-24 13:19:53 +00:00
kippy = get_account ( KIPPY_ID )
2022-06-18 00:35:27 +00:00
kippy . procoins + = 10000
g . db . add ( kippy )
2022-10-20 20:53:52 +00:00
return ' 10k marseybux printed! '
2022-06-18 00:35:27 +00:00
2022-05-25 20:16:26 +00:00
@app.get ( ' /admin/loggedin ' )
2022-10-06 03:29:14 +00:00
@admin_level_required ( PERMS [ ' VIEW_ACTIVE_USERS ' ] )
2022-05-25 20:16:26 +00:00
def loggedin_list ( v ) :
2022-06-20 20:25:03 +00:00
ids = [ x for x , val in cache . get ( f ' { SITE } _loggedin ' ) . items ( ) if time . time ( ) - val < LOGGEDIN_ACTIVE_TIME ]
2022-05-26 20:31:08 +00:00
users = g . db . query ( User ) . filter ( User . id . in_ ( ids ) ) . order_by ( User . admin_level . desc ( ) , User . truecoins . desc ( ) ) . all ( )
2022-05-25 21:25:23 +00:00
return render_template ( " loggedin.html " , v = v , users = users )
2022-05-25 20:16:26 +00:00
2022-05-26 20:31:08 +00:00
@app.get ( ' /admin/loggedout ' )
2022-10-06 03:29:14 +00:00
@admin_level_required ( PERMS [ ' VIEW_ACTIVE_USERS ' ] )
2022-05-26 20:31:08 +00:00
def loggedout_list ( v ) :
2022-06-20 20:25:03 +00:00
users = sorted ( [ val [ 1 ] for x , val in cache . get ( f ' { SITE } _loggedout ' ) . items ( ) if time . time ( ) - val [ 0 ] < LOGGEDIN_ACTIVE_TIME ] )
2022-05-26 20:31:08 +00:00
return render_template ( " loggedout.html " , v = v , users = users )
2022-05-04 23:09:46 +00:00
@app.get ( ' /admin/merge/<id1>/<id2> ' )
2022-10-06 07:55:54 +00:00
@admin_level_required ( PERMS [ ' USER_MERGE ' ] )
2022-05-04 23:09:46 +00:00
def merge ( v , id1 , id2 ) :
if v . id != AEVANN_ID : abort ( 403 )
if time . time ( ) - session . get ( ' verified ' , 0 ) > 3 :
session . pop ( " lo_user " , None )
path = request . path
qs = urlencode ( dict ( request . values ) )
argval = quote ( f " { path } ? { qs } " , safe = ' ' )
return redirect ( f " /login?redirect= { argval } " )
user1 = get_account ( id1 )
user2 = get_account ( id2 )
awards = g . db . query ( AwardRelationship ) . filter_by ( user_id = user2 . id )
comments = g . db . query ( Comment ) . filter_by ( author_id = user2 . id )
submissions = g . db . query ( Submission ) . filter_by ( author_id = user2 . id )
badges = g . db . query ( Badge ) . filter_by ( user_id = user2 . id )
mods = g . db . query ( Mod ) . filter_by ( user_id = user2 . id )
exiles = g . db . query ( Exile ) . filter_by ( user_id = user2 . id )
for award in awards :
award . user_id = user1 . id
g . db . add ( award )
for comment in comments :
comment . author_id = user1 . id
g . db . add ( comment )
for submission in submissions :
submission . author_id = user1 . id
g . db . add ( submission )
for badge in badges :
if not user1 . has_badge ( badge . badge_id ) :
badge . user_id = user1 . id
g . db . add ( badge )
2022-08-25 15:04:33 +00:00
g . db . flush ( )
2022-05-04 23:09:46 +00:00
for mod in mods :
if not user1 . mods ( mod . sub ) :
mod . user_id = user1 . id
g . db . add ( mod )
2022-08-25 15:04:33 +00:00
g . db . flush ( )
2022-05-04 23:09:46 +00:00
for exile in exiles :
if not user1 . exiled_from ( exile . sub ) :
exile . user_id = user1 . id
g . db . add ( exile )
2022-08-25 15:04:33 +00:00
g . db . flush ( )
2022-05-04 23:09:46 +00:00
2022-06-10 12:48:11 +00:00
for kind in ( ' comment_count ' , ' post_count ' , ' winnings ' , ' received_award_count ' , ' coins_spent ' , ' lootboxes_bought ' , ' coins ' , ' truecoins ' , ' procoins ' ) :
2022-05-04 23:09:46 +00:00
amount = getattr ( user1 , kind ) + getattr ( user2 , kind )
setattr ( user1 , kind , amount )
setattr ( user2 , kind , 0 )
g . db . add ( user1 )
g . db . add ( user2 )
2022-09-01 23:59:56 +00:00
online = cache . get ( ONLINE_STR )
2022-05-04 23:09:46 +00:00
cache . clear ( )
2022-09-01 23:59:56 +00:00
cache . set ( ONLINE_STR , online )
2022-05-04 23:09:46 +00:00
return redirect ( user1 . url )
@app.get ( ' /admin/merge_all/<id> ' )
2022-10-06 07:55:54 +00:00
@admin_level_required ( PERMS [ ' USER_MERGE ' ] )
2022-05-04 23:09:46 +00:00
def merge_all ( v , id ) :
if v . id != AEVANN_ID : abort ( 403 )
if time . time ( ) - session . get ( ' verified ' , 0 ) > 3 :
session . pop ( " lo_user " , None )
path = request . path
qs = urlencode ( dict ( request . values ) )
argval = quote ( f " { path } ? { qs } " , safe = ' ' )
return redirect ( f " /login?redirect= { argval } " )
user = get_account ( id )
alt_ids = [ x . id for x in user . alts_unique ]
things = g . db . query ( AwardRelationship ) . filter ( AwardRelationship . user_id . in_ ( alt_ids ) ) . all ( ) + g . db . query ( Mod ) . filter ( Mod . user_id . in_ ( alt_ids ) ) . all ( ) + g . db . query ( Exile ) . filter ( Exile . user_id . in_ ( alt_ids ) ) . all ( )
for thing in things :
thing . user_id = user . id
g . db . add ( thing )
things = g . db . query ( Submission ) . filter ( Submission . author_id . in_ ( alt_ids ) ) . all ( ) + g . db . query ( Comment ) . filter ( Comment . author_id . in_ ( alt_ids ) ) . all ( )
for thing in things :
thing . author_id = user . id
g . db . add ( thing )
badges = g . db . query ( Badge ) . filter ( Badge . user_id . in_ ( alt_ids ) ) . all ( )
for badge in badges :
if not user . has_badge ( badge . badge_id ) :
badge . user_id = user . id
g . db . add ( badge )
2022-08-25 15:04:33 +00:00
g . db . flush ( )
2022-05-04 23:09:46 +00:00
for alt in user . alts_unique :
2022-06-10 12:48:11 +00:00
for kind in ( ' comment_count ' , ' post_count ' , ' winnings ' , ' received_award_count ' , ' coins_spent ' , ' lootboxes_bought ' , ' coins ' , ' truecoins ' , ' procoins ' ) :
2022-05-04 23:09:46 +00:00
amount = getattr ( user , kind ) + getattr ( alt , kind )
setattr ( user , kind , amount )
setattr ( alt , kind , 0 )
g . db . add ( alt )
g . db . add ( user )
2022-09-01 23:59:56 +00:00
online = cache . get ( ONLINE_STR )
2022-05-04 23:09:46 +00:00
cache . clear ( )
2022-09-01 23:59:56 +00:00
cache . set ( ONLINE_STR , online )
2022-05-04 23:09:46 +00:00
return redirect ( user . url )
@app.post ( " /@<username>/make_admin " )
2022-10-06 03:37:18 +00:00
@admin_level_required ( PERMS [ ' ADMIN_ADD ' ] )
2022-05-04 23:09:46 +00:00
def make_admin ( v , username ) :
2022-05-29 18:36:51 +00:00
if SITE == ' rdrama.net ' : abort ( 403 )
2022-05-04 23:09:46 +00:00
user = get_user ( username )
2022-09-01 21:44:40 +00:00
2022-10-06 03:37:18 +00:00
user . admin_level = PERMS [ ' ADMIN_ADD_PERM_LEVEL ' ]
2022-05-04 23:09:46 +00:00
g . db . add ( user )
ma = ModAction (
kind = " make_admin " ,
user_id = v . id ,
target_user_id = user . id
)
g . db . add ( ma )
2022-09-11 14:32:00 +00:00
return { " message " : f " @ { user . username } has been made admin! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /@<username>/remove_admin " )
2022-10-06 03:37:18 +00:00
@admin_level_required ( PERMS [ ' ADMIN_REMOVE ' ] )
2022-05-04 23:09:46 +00:00
def remove_admin ( v , username ) :
user = get_user ( username )
user . admin_level = 0
g . db . add ( user )
ma = ModAction (
kind = " remove_admin " ,
user_id = v . id ,
target_user_id = user . id
)
g . db . add ( ma )
2022-09-11 14:32:00 +00:00
return { " message " : f " @ { user . username } has been removed as admin! " }
2022-05-04 23:09:46 +00:00
2022-08-26 21:53:17 +00:00
@app.post ( " /distribute/<option_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 05:59:27 +00:00
@admin_level_required ( PERMS [ ' POST_BETS_DISTRIBUTE ' ] )
2022-08-26 21:53:17 +00:00
def distribute ( v , option_id ) :
autojanny = get_account ( AUTOJANNY_ID )
2022-10-11 13:01:39 +00:00
if autojanny . coins == 0 : abort ( 400 , " @AutoJanny has 0 coins " )
2022-08-26 21:53:17 +00:00
try : option_id = int ( option_id )
except : abort ( 400 )
try : option = g . db . get ( SubmissionOption , option_id )
except : abort ( 404 )
2022-09-08 18:25:45 +00:00
if option . exclusive != 2 : abort ( 403 )
option . exclusive = 3
g . db . add ( option )
2022-08-26 21:53:17 +00:00
post = option . post
pool = 0
for o in post . options :
2022-09-30 15:38:47 +00:00
if o . exclusive > = 2 : pool + = o . upvotes
2022-10-05 06:07:53 +00:00
pool * = POLL_BET_COINS
2022-08-26 21:53:17 +00:00
2022-10-12 16:33:00 +00:00
autojanny . charge_account ( ' coins ' , pool )
2022-08-26 21:53:17 +00:00
if autojanny . coins < 0 : autojanny . coins = 0
g . db . add ( autojanny )
votes = option . votes
coinsperperson = int ( pool / len ( votes ) )
cid = notif_comment ( f " You won { coinsperperson } coins betting on [ { post . title } ]( { post . shortlink } ) :marseyparty: " )
for vote in votes :
u = vote . user
u . coins + = coinsperperson
add_notif ( cid , u . id )
2022-10-05 06:07:53 +00:00
cid = notif_comment ( f " You lost the { POLL_BET_COINS } coins you bet on [ { post . title } ]( { post . shortlink } ) :marseylaugh: " )
2022-08-26 21:53:17 +00:00
losing_voters = [ ]
for o in post . options :
2022-09-23 12:09:33 +00:00
if o . exclusive == 2 :
2022-08-26 21:53:17 +00:00
losing_voters . extend ( [ x . user_id for x in o . votes ] )
for uid in losing_voters :
add_notif ( cid , uid )
2022-05-04 23:09:46 +00:00
2022-08-26 21:53:17 +00:00
ma = ModAction (
kind = " distribute " ,
user_id = v . id ,
target_submission_id = post . id
)
g . db . add ( ma )
return { " message " : f " Each winner has received { coinsperperson } coins! " }
2022-05-04 23:09:46 +00:00
2022-10-08 04:07:44 +00:00
@app.post ( " /@<username>/revert_actions " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
@admin_level_required ( PERMS [ ' ADMIN_ACTIONS_REVERT ' ] )
def revert_actions ( v , username ) :
user = get_user ( username )
ma = ModAction (
kind = " revert " ,
user_id = v . id ,
target_user_id = user . id
)
g . db . add ( ma )
cutoff = int ( time . time ( ) ) - 86400
posts = [ x [ 0 ] for x in g . db . query ( ModAction . target_submission_id ) . filter ( ModAction . user_id == user . id , ModAction . created_utc > cutoff , ModAction . kind == ' ban_post ' ) . all ( ) ]
posts = g . db . query ( Submission ) . filter ( Submission . id . in_ ( posts ) ) . all ( )
comments = [ x [ 0 ] for x in g . db . query ( ModAction . target_comment_id ) . filter ( ModAction . user_id == user . id , ModAction . created_utc > cutoff , ModAction . kind == ' ban_comment ' ) . all ( ) ]
comments = g . db . query ( Comment ) . filter ( Comment . id . in_ ( comments ) ) . all ( )
for item in posts + comments :
item . is_banned = False
item . ban_reason = None
item . is_approved = v . id
g . db . add ( item )
users = ( x [ 0 ] for x in g . db . query ( ModAction . target_user_id ) . filter ( ModAction . user_id == user . id , ModAction . created_utc > cutoff , ModAction . kind . in_ ( ( ' shadowban ' , ' ban_user ' ) ) ) . all ( ) )
users = g . db . query ( User ) . filter ( User . id . in_ ( users ) ) . all ( )
for user in users :
user . shadowbanned = None
user . unban_utc = 0
user . ban_reason = None
if user . is_banned :
user . is_banned = 0
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( user . id , f " @ { v . username } (Admin) has unbanned you! " )
2022-10-08 04:07:44 +00:00
g . db . add ( user )
for u in user . alts :
u . shadowbanned = None
u . unban_utc = 0
u . ban_reason = None
if u . is_banned :
u . is_banned = 0
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( u . id , f " @ { v . username } (Admin) has unbanned you! " )
2022-10-08 04:07:44 +00:00
g . db . add ( u )
2022-10-08 06:08:45 +00:00
return { " message " : f " @ { user . username } ' s admin actions have been reverted! " }
2022-10-08 04:07:44 +00:00
2022-05-04 23:09:46 +00:00
@app.post ( " /@<username>/club_allow " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:47:42 +00:00
@admin_level_required ( PERMS [ ' USER_CLUB_ALLOW_BAN ' ] )
2022-05-04 23:09:46 +00:00
def club_allow ( v , username ) :
u = get_user ( username , v = v )
2022-10-11 13:01:39 +00:00
if u . admin_level > = v . admin_level : abort ( 403 , ' noob ' )
2022-05-04 23:09:46 +00:00
u . club_allowed = True
g . db . add ( u )
for x in u . alts_unique :
x . club_allowed = True
g . db . add ( x )
ma = ModAction (
kind = " club_allow " ,
user_id = v . id ,
target_user_id = u . id
)
g . db . add ( ma )
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( u . id , f " @ { v . username } (Admin) has inducted you into the { CC_TITLE } ! " )
2022-10-07 02:51:56 +00:00
2022-09-11 14:32:00 +00:00
return { " message " : f " @ { u . username } has been allowed into the { CC_TITLE } ! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /@<username>/club_ban " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:47:42 +00:00
@admin_level_required ( PERMS [ ' USER_CLUB_ALLOW_BAN ' ] )
2022-05-04 23:09:46 +00:00
def club_ban ( v , username ) :
u = get_user ( username , v = v )
2022-10-11 13:01:39 +00:00
if u . admin_level > = v . admin_level : abort ( 403 , ' noob ' )
2022-05-04 23:09:46 +00:00
u . club_allowed = False
for x in u . alts_unique :
u . club_allowed = False
g . db . add ( x )
ma = ModAction (
kind = " club_ban " ,
user_id = v . id ,
target_user_id = u . id
)
g . db . add ( ma )
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( u . id , f " @ { v . username } (Admin) has disallowed you from the { CC_TITLE } ! " )
2022-10-07 02:51:56 +00:00
return { " message " : f " @ { u . username } has been disallowed from the { CC_TITLE } . Deserved. " }
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/shadowbanned " )
2022-10-12 06:53:32 +00:00
@admin_level_required ( PERMS [ ' USER_SHADOWBAN ' ] )
2022-05-04 23:09:46 +00:00
def shadowbanned ( v ) :
2022-05-25 20:16:26 +00:00
users = g . db . query ( User ) . filter ( User . shadowbanned != None ) . order_by ( User . shadowbanned ) . all ( )
2022-05-04 23:09:46 +00:00
return render_template ( " shadowbanned.html " , v = v , users = users )
@app.get ( " /admin/image_posts " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def image_posts_listing ( v ) :
try : page = int ( request . values . get ( ' page ' , 1 ) )
except : page = 1
posts = g . db . query ( Submission ) . order_by ( Submission . id . desc ( ) )
2022-10-29 03:20:48 +00:00
firstrange = PAGE_SIZE * ( page - 1 )
secondrange = firstrange + PAGE_SIZE + 1
2022-05-04 23:09:46 +00:00
posts = [ x . id for x in posts if x . is_image ] [ firstrange : secondrange ]
2022-10-29 03:20:48 +00:00
next_exists = ( len ( posts ) > PAGE_SIZE )
posts = get_posts ( posts [ : PAGE_SIZE ] , v = v )
2022-05-04 23:09:46 +00:00
return render_template ( " admin/image_posts.html " , v = v , listing = posts , next_exists = next_exists , page = page , sort = " new " )
@app.get ( " /admin/reported/posts " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def reported_posts ( v ) :
page = max ( 1 , int ( request . values . get ( " page " , 1 ) ) )
listing = g . db . query ( Submission ) . filter_by (
is_approved = None ,
2022-10-23 16:24:17 +00:00
is_banned = False ,
deleted_utc = 0
2022-10-29 03:20:48 +00:00
) . join ( Submission . flags ) . order_by ( Submission . id . desc ( ) ) . offset ( PAGE_SIZE * ( page - 1 ) ) . limit ( PAGE_SIZE + 1 )
2022-05-04 23:09:46 +00:00
listing = [ p . id for p in listing ]
2022-10-29 03:20:48 +00:00
next_exists = len ( listing ) > PAGE_SIZE
listing = listing [ : PAGE_SIZE ]
2022-05-04 23:09:46 +00:00
listing = get_posts ( listing , v = v )
return render_template ( " admin/reported_posts.html " ,
2022-09-04 23:15:37 +00:00
next_exists = next_exists , listing = listing , page = page , v = v )
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/reported/comments " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def reported_comments ( v ) :
page = max ( 1 , int ( request . values . get ( " page " , 1 ) ) )
listing = g . db . query ( Comment
2022-09-04 23:15:37 +00:00
) . filter_by (
2022-05-04 23:09:46 +00:00
is_approved = None ,
2022-10-23 16:24:17 +00:00
is_banned = False ,
deleted_utc = 0
2022-10-29 03:20:48 +00:00
) . join ( Comment . flags ) . order_by ( Comment . id . desc ( ) ) . offset ( PAGE_SIZE * ( page - 1 ) ) . limit ( PAGE_SIZE + 1 ) . all ( )
2022-05-04 23:09:46 +00:00
listing = [ c . id for c in listing ]
2022-10-29 03:20:48 +00:00
next_exists = len ( listing ) > PAGE_SIZE
listing = listing [ : PAGE_SIZE ]
2022-05-04 23:09:46 +00:00
listing = get_comments ( listing , v = v )
return render_template ( " admin/reported_comments.html " ,
2022-09-04 23:15:37 +00:00
next_exists = next_exists ,
listing = listing ,
page = page ,
v = v ,
standalone = True )
2022-05-04 23:09:46 +00:00
@app.get ( " /admin " )
2022-10-06 05:54:48 +00:00
@admin_level_required ( PERMS [ ' ADMIN_HOME_VISIBLE ' ] )
2022-05-04 23:09:46 +00:00
def admin_home ( v ) :
2022-07-01 18:19:21 +00:00
under_attack = False
2022-10-13 06:12:35 +00:00
if v . admin_level > = PERMS [ ' SITE_SETTINGS_UNDER_ATTACK ' ] :
2022-10-13 07:27:56 +00:00
under_attack = ( get_security_level ( ) or ' high ' ) == ' under_attack '
2022-05-04 23:09:46 +00:00
2022-05-05 08:46:20 +00:00
gitref = admin_git_head ( )
return render_template ( " admin/admin_home.html " , v = v ,
2022-07-01 18:19:21 +00:00
under_attack = under_attack ,
2022-05-05 08:46:20 +00:00
gitref = gitref )
def admin_git_head ( ) :
short_len = 12
# Note: doing zero sanitization. Git branch names are extremely permissive.
# However, they forbid '..', so I don't see an obvious dir traversal attack.
# Also, a malicious branch name would mean someone already owned the server
# or repo, so I think this isn't a weak link.
try :
2022-06-17 22:19:18 +00:00
with open ( ' .git/HEAD ' , encoding = ' utf_8 ' ) as head_f :
2022-05-05 08:46:20 +00:00
head_txt = head_f . read ( )
2022-07-06 11:49:13 +00:00
head_path = git_regex . match ( head_txt ) . group ( 1 )
2022-06-17 22:19:18 +00:00
with open ( ' .git/ ' + head_path , encoding = ' utf_8 ' ) as ref_f :
2022-05-05 08:46:20 +00:00
gitref = ref_f . read ( ) [ 0 : short_len ]
except :
return ' <unable to read> '
return gitref
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/site_settings/<setting> " )
2022-10-06 04:26:15 +00:00
@admin_level_required ( PERMS [ ' SITE_SETTINGS ' ] )
2022-05-04 23:09:46 +00:00
def change_settings ( v , setting ) :
site_settings = app . config [ ' SETTINGS ' ]
2022-06-10 21:52:32 +00:00
site_settings [ setting ] = not site_settings [ setting ]
2022-07-29 22:14:25 +00:00
with open ( " /site_settings.json " , " w " , encoding = ' utf_8 ' ) as f :
2022-06-10 21:52:32 +00:00
json . dump ( site_settings , f )
2022-05-04 23:09:46 +00:00
if site_settings [ setting ] : word = ' enable '
else : word = ' disable '
ma = ModAction (
kind = f " { word } _ { setting } " ,
user_id = v . id ,
)
g . db . add ( ma )
return { ' message ' : f " { setting } { word } d successfully! " }
@app.post ( " /admin/purge_cache " )
2022-10-06 06:21:04 +00:00
@admin_level_required ( PERMS [ ' SITE_CACHE_PURGE_CDN ' ] )
2022-05-04 23:09:46 +00:00
def purge_cache ( v ) :
2022-09-01 23:59:56 +00:00
online = cache . get ( ONLINE_STR )
2022-05-04 23:09:46 +00:00
cache . clear ( )
2022-09-01 23:59:56 +00:00
cache . set ( ONLINE_STR , online )
2022-10-13 07:27:56 +00:00
if not purge_entire_cache ( ) :
abort ( 400 , ' Failed to purge cache ' )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " purge_cache " ,
user_id = v . id
)
g . db . add ( ma )
2022-10-13 07:27:56 +00:00
return { " message " : " Cache purged! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/under_attack " )
2022-10-06 04:26:15 +00:00
@admin_level_required ( PERMS [ ' SITE_SETTINGS_UNDER_ATTACK ' ] )
2022-05-04 23:09:46 +00:00
def under_attack ( v ) :
2022-10-13 07:27:56 +00:00
response = get_security_level ( )
if not response :
abort ( 400 , ' Could not retrieve the current security level ' )
old_under_attack_mode = response == ' under_attack '
enable_disable_str = ' disable ' if old_under_attack_mode else ' enable '
new_security_level = ' high ' if old_under_attack_mode else ' under_attack '
if not set_security_level ( new_security_level ) :
abort ( 400 , f ' Failed to { enable_disable_str } under attack mode ' )
ma = ModAction (
kind = f " { enable_disable_str } _under_attack " ,
user_id = v . id ,
)
g . db . add ( ma )
2022-10-13 07:54:30 +00:00
return { " message " : f " Under attack mode { enable_disable_str } d! " }
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/badge_grant " )
2022-10-29 06:08:29 +00:00
@app.get ( " /admin/badge_remove " )
2022-10-06 03:50:02 +00:00
@admin_level_required ( PERMS [ ' USER_BADGES ' ] )
2022-10-11 07:29:24 +00:00
@feature_required ( ' BADGES ' )
2022-05-04 23:09:46 +00:00
def badge_grant_get ( v ) :
2022-10-29 06:08:29 +00:00
grant = request . url . endswith ( " grant " )
2022-05-04 23:09:46 +00:00
badges = g . db . query ( BadgeDef ) . order_by ( BadgeDef . id ) . all ( )
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = grant )
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/badge_grant " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:50:02 +00:00
@admin_level_required ( PERMS [ ' USER_BADGES ' ] )
2022-10-11 07:29:24 +00:00
@feature_required ( ' BADGES ' )
2022-05-04 23:09:46 +00:00
def badge_grant_post ( v ) :
badges = g . db . query ( BadgeDef ) . order_by ( BadgeDef . id ) . all ( )
user = get_user ( request . values . get ( " username " ) . strip ( ) , graceful = True )
if not user :
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = True , error = " User not found. " )
2022-05-04 23:09:46 +00:00
try : badge_id = int ( request . values . get ( " badge_id " ) )
except : abort ( 400 )
2022-10-17 19:13:33 +00:00
if SITE == ' watchpeopledie.tv ' and badge_id not in { 99 , 101 } :
2022-10-14 17:11:39 +00:00
abort ( 403 )
2022-10-14 16:59:49 +00:00
if badge_id in { 16 , 17 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 94 , 95 , 96 , 97 , 98 , 109 , 137 , 67 , 68 , 83 , 84 , 87 , 90 , 140 } and v . id != AEVANN_ID and SITE != ' pcmemes.net ' :
2022-05-04 23:09:46 +00:00
abort ( 403 )
if user . has_badge ( badge_id ) :
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = True , error = " User already has that badge. " )
2022-05-04 23:09:46 +00:00
new_badge = Badge ( badge_id = badge_id , user_id = user . id )
desc = request . values . get ( " description " )
if desc : new_badge . description = desc
url = request . values . get ( " url " )
2022-05-25 18:29:22 +00:00
if ' \\ ' in url : abort ( 400 )
2022-05-04 23:09:46 +00:00
if url : new_badge . url = url
g . db . add ( new_badge )
2022-08-17 17:57:32 +00:00
g . db . flush ( )
2022-05-04 23:09:46 +00:00
if v . id != user . id :
2022-10-10 06:11:17 +00:00
text = f " @ { v . username } (Admin) has given you the following profile badge: \n \n ![]( { new_badge . path } ) \n \n ** { new_badge . name } ** \n \n { new_badge . badge . description } "
2022-09-13 10:27:09 +00:00
send_repeatable_notification ( user . id , text )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " badge_grant " ,
user_id = v . id ,
target_user_id = user . id ,
_note = new_badge . name
)
g . db . add ( ma )
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = True , msg = f " { new_badge . name } Badge granted to @ { user . username } successfully! " )
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/badge_remove " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:50:02 +00:00
@admin_level_required ( PERMS [ ' USER_BADGES ' ] )
2022-10-11 07:29:24 +00:00
@feature_required ( ' BADGES ' )
2022-05-04 23:09:46 +00:00
def badge_remove_post ( v ) :
badges = g . db . query ( BadgeDef ) . order_by ( BadgeDef . id ) . all ( )
user = get_user ( request . values . get ( " username " ) . strip ( ) , graceful = True )
if not user :
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = False , error = " User not found. " )
2022-05-04 23:09:46 +00:00
try : badge_id = int ( request . values . get ( " badge_id " ) )
except : abort ( 400 )
2022-10-14 17:11:39 +00:00
if badge_id in { 67 , 68 , 83 , 84 , 87 , 90 , 140 } and v . id != AEVANN_ID and SITE != ' pcmemes.net ' :
abort ( 403 )
2022-05-04 23:09:46 +00:00
badge = user . has_badge ( badge_id )
if not badge :
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = False , error = " User doesn ' t have that badge. " )
2022-05-04 23:09:46 +00:00
2022-09-13 10:27:09 +00:00
if v . id != user . id :
2022-10-10 06:11:17 +00:00
text = f " @ { v . username } (Admin) has removed the following profile badge from you: \n \n ![]( { badge . path } ) \n \n ** { badge . name } ** \n \n { badge . badge . description } "
2022-09-13 10:27:09 +00:00
send_repeatable_notification ( user . id , text )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " badge_remove " ,
user_id = v . id ,
target_user_id = user . id ,
_note = badge . name
)
g . db . add ( ma )
g . db . delete ( badge )
2022-10-29 06:08:29 +00:00
return render_template ( " admin/badge_admin.html " , v = v , badge_types = badges , grant = False , msg = f " { badge . name } Badge removed from @ { user . username } successfully! " )
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/users " )
2022-10-06 05:37:50 +00:00
@admin_level_required ( PERMS [ ' VIEW_ALL_USERS ' ] )
2022-05-04 23:09:46 +00:00
def users_list ( v ) :
try : page = int ( request . values . get ( " page " , 1 ) )
except : page = 1
2022-10-29 03:20:48 +00:00
users = g . db . query ( User ) . order_by ( User . id . desc ( ) ) . offset ( PAGE_SIZE * ( page - 1 ) ) . limit ( PAGE_SIZE + 1 ) . all ( )
2022-05-04 23:09:46 +00:00
2022-10-29 03:20:48 +00:00
next_exists = ( len ( users ) > PAGE_SIZE )
users = users [ : PAGE_SIZE ]
2022-05-04 23:09:46 +00:00
2022-09-05 20:23:35 +00:00
return render_template ( " user_cards.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
users = users ,
next_exists = next_exists ,
page = page ,
)
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/alt_votes " )
2022-10-06 05:37:50 +00:00
@admin_level_required ( PERMS [ ' VIEW_ALT_VOTES ' ] )
2022-05-04 23:09:46 +00:00
def alt_votes_get ( v ) :
u1 = request . values . get ( " u1 " )
u2 = request . values . get ( " u2 " )
if not u1 or not u2 :
return render_template ( " admin/alt_votes.html " , v = v )
u1 = get_user ( u1 )
u2 = get_user ( u2 )
u1_post_ups = g . db . query (
Vote . submission_id ) . filter_by (
user_id = u1 . id ,
vote_type = 1 ) . all ( )
u1_post_downs = g . db . query (
Vote . submission_id ) . filter_by (
user_id = u1 . id ,
vote_type = - 1 ) . all ( )
u1_comment_ups = g . db . query (
CommentVote . comment_id ) . filter_by (
user_id = u1 . id ,
vote_type = 1 ) . all ( )
u1_comment_downs = g . db . query (
CommentVote . comment_id ) . filter_by (
user_id = u1 . id ,
vote_type = - 1 ) . all ( )
u2_post_ups = g . db . query (
Vote . submission_id ) . filter_by (
user_id = u2 . id ,
vote_type = 1 ) . all ( )
u2_post_downs = g . db . query (
Vote . submission_id ) . filter_by (
user_id = u2 . id ,
vote_type = - 1 ) . all ( )
u2_comment_ups = g . db . query (
CommentVote . comment_id ) . filter_by (
user_id = u2 . id ,
vote_type = 1 ) . all ( )
u2_comment_downs = g . db . query (
CommentVote . comment_id ) . filter_by (
user_id = u2 . id ,
vote_type = - 1 ) . all ( )
data = { }
data [ ' u1_only_post_ups ' ] = len (
[ x for x in u1_post_ups if x not in u2_post_ups ] )
data [ ' u2_only_post_ups ' ] = len (
[ x for x in u2_post_ups if x not in u1_post_ups ] )
data [ ' both_post_ups ' ] = len ( list ( set ( u1_post_ups ) & set ( u2_post_ups ) ) )
data [ ' u1_only_post_downs ' ] = len (
[ x for x in u1_post_downs if x not in u2_post_downs ] )
data [ ' u2_only_post_downs ' ] = len (
[ x for x in u2_post_downs if x not in u1_post_downs ] )
data [ ' both_post_downs ' ] = len (
list ( set ( u1_post_downs ) & set ( u2_post_downs ) ) )
data [ ' u1_only_comment_ups ' ] = len (
[ x for x in u1_comment_ups if x not in u2_comment_ups ] )
data [ ' u2_only_comment_ups ' ] = len (
[ x for x in u2_comment_ups if x not in u1_comment_ups ] )
data [ ' both_comment_ups ' ] = len (
list ( set ( u1_comment_ups ) & set ( u2_comment_ups ) ) )
data [ ' u1_only_comment_downs ' ] = len (
[ x for x in u1_comment_downs if x not in u2_comment_downs ] )
data [ ' u2_only_comment_downs ' ] = len (
[ x for x in u2_comment_downs if x not in u1_comment_downs ] )
data [ ' both_comment_downs ' ] = len (
list ( set ( u1_comment_downs ) & set ( u2_comment_downs ) ) )
data [ ' u1_post_ups_unique ' ] = 100 * \
data [ ' u1_only_post_ups ' ] / / len ( u1_post_ups ) if u1_post_ups else 0
data [ ' u2_post_ups_unique ' ] = 100 * \
data [ ' u2_only_post_ups ' ] / / len ( u2_post_ups ) if u2_post_ups else 0
data [ ' u1_post_downs_unique ' ] = 100 * \
data [ ' u1_only_post_downs ' ] / / len (
u1_post_downs ) if u1_post_downs else 0
data [ ' u2_post_downs_unique ' ] = 100 * \
data [ ' u2_only_post_downs ' ] / / len (
u2_post_downs ) if u2_post_downs else 0
data [ ' u1_comment_ups_unique ' ] = 100 * \
data [ ' u1_only_comment_ups ' ] / / len (
u1_comment_ups ) if u1_comment_ups else 0
data [ ' u2_comment_ups_unique ' ] = 100 * \
data [ ' u2_only_comment_ups ' ] / / len (
u2_comment_ups ) if u2_comment_ups else 0
data [ ' u1_comment_downs_unique ' ] = 100 * \
data [ ' u1_only_comment_downs ' ] / / len (
u1_comment_downs ) if u1_comment_downs else 0
data [ ' u2_comment_downs_unique ' ] = 100 * \
data [ ' u2_only_comment_downs ' ] / / len (
u2_comment_downs ) if u2_comment_downs else 0
return render_template ( " admin/alt_votes.html " ,
2022-09-04 23:15:37 +00:00
u1 = u1 ,
u2 = u2 ,
v = v ,
data = data
)
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/link_accounts " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 05:37:50 +00:00
@admin_level_required ( PERMS [ ' USER_LINK ' ] )
2022-05-04 23:09:46 +00:00
def admin_link_accounts ( v ) :
2022-10-16 09:51:42 +00:00
u1 = get_account ( request . values . get ( " u1 " ) ) . id
u2 = get_account ( request . values . get ( " u2 " ) ) . id
2022-05-04 23:09:46 +00:00
new_alt = Alt (
user1 = u1 ,
user2 = u2 ,
is_manual = True
)
g . db . add ( new_alt )
2022-09-23 12:33:58 +00:00
g . db . flush ( )
check_for_alts ( g . db . get ( User , u1 ) )
check_for_alts ( g . db . get ( User , u2 ) )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " link_accounts " ,
user_id = v . id ,
target_user_id = u1 ,
_note = f ' with { u2 } '
)
g . db . add ( ma )
2022-06-24 13:19:53 +00:00
return redirect ( f " /admin/alt_votes?u1= { get_account ( u1 ) . username } &u2= { get_account ( u2 ) . username } " )
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/removed/posts " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def admin_removed ( v ) :
try : page = int ( request . values . get ( " page " , 1 ) )
except : page = 1
if page < 1 : abort ( 400 )
2022-10-29 03:20:48 +00:00
ids = g . db . query ( Submission . id ) . join ( Submission . author ) . filter ( or_ ( Submission . is_banned == True , User . shadowbanned != None ) ) . order_by ( Submission . id . desc ( ) ) . offset ( PAGE_SIZE * ( page - 1 ) ) . limit ( PAGE_SIZE + 1 ) . all ( )
2022-05-04 23:09:46 +00:00
ids = [ x [ 0 ] for x in ids ]
2022-10-29 03:20:48 +00:00
next_exists = len ( ids ) > PAGE_SIZE
ids = ids [ : PAGE_SIZE ]
2022-05-04 23:09:46 +00:00
posts = get_posts ( ids , v = v )
return render_template ( " admin/removed_posts.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
listing = posts ,
page = page ,
next_exists = next_exists
)
2022-05-04 23:09:46 +00:00
@app.get ( " /admin/removed/comments " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def admin_removed_comments ( v ) :
try : page = int ( request . values . get ( " page " , 1 ) )
except : page = 1
2022-10-29 03:20:48 +00:00
ids = g . db . query ( Comment . id ) . join ( Comment . author ) . filter ( or_ ( Comment . is_banned == True , User . shadowbanned != None ) ) . order_by ( Comment . id . desc ( ) ) . offset ( PAGE_SIZE * ( page - 1 ) ) . limit ( PAGE_SIZE ) . all ( )
2022-05-04 23:09:46 +00:00
ids = [ x [ 0 ] for x in ids ]
2022-10-29 03:20:48 +00:00
next_exists = len ( ids ) > PAGE_SIZE
ids = ids [ : PAGE_SIZE ]
2022-05-04 23:09:46 +00:00
comments = get_comments ( ids , v = v )
return render_template ( " admin/removed_comments.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
listing = comments ,
page = page ,
next_exists = next_exists
)
2022-05-04 23:09:46 +00:00
@app.post ( " /agendaposter/<user_id> " )
2022-10-06 03:04:38 +00:00
@admin_level_required ( PERMS [ ' USER_AGENDAPOSTER ' ] )
2022-05-04 23:09:46 +00:00
def agendaposter ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
2022-05-30 03:30:32 +00:00
days = request . values . get ( " days " )
2022-05-26 20:04:39 +00:00
2022-08-17 19:17:34 +00:00
if days :
2022-09-10 03:13:12 +00:00
expiry = int ( time . time ( ) + float ( days ) * 60 * 60 * 24 )
2022-08-17 19:17:34 +00:00
else : expiry = 1
2022-05-04 23:09:46 +00:00
user . agendaposter = expiry
g . db . add ( user )
2022-10-10 04:16:43 +00:00
if days :
2022-10-22 16:40:52 +00:00
days_txt = str ( days ) . split ( ' .0 ' ) [ 0 ]
2022-10-10 04:16:43 +00:00
note = f " for { days_txt } days "
2022-09-21 23:52:39 +00:00
else : note = " permanently "
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " agendaposter " ,
user_id = v . id ,
target_user_id = user . id ,
2022-09-29 10:18:27 +00:00
_note = note
2022-05-04 23:09:46 +00:00
)
g . db . add ( ma )
2022-06-15 19:33:21 +00:00
badge_grant ( user = user , badge_id = 28 )
2022-05-04 23:09:46 +00:00
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( user . id , f " @ { v . username } (Admin) has marked you as a chud ( { note } ). " )
2022-05-04 23:09:46 +00:00
return redirect ( user . url )
@app.post ( " /unagendaposter/<user_id> " )
2022-10-06 03:04:38 +00:00
@admin_level_required ( PERMS [ ' USER_AGENDAPOSTER ' ] )
2022-05-04 23:09:46 +00:00
def unagendaposter ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
user . agendaposter = 0
g . db . add ( user )
for alt in user . alts :
alt . agendaposter = 0
g . db . add ( alt )
ma = ModAction (
kind = " unagendaposter " ,
user_id = v . id ,
target_user_id = user . id
)
g . db . add ( ma )
badge = user . has_badge ( 28 )
if badge : g . db . delete ( badge )
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( user . id , f " @ { v . username } (Admin) has unmarked you as a chud. " )
2022-05-04 23:09:46 +00:00
2022-09-11 14:32:00 +00:00
return { " message " : f " @ { user . username } ' s chud theme has been disabled! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /shadowban/<user_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:04:38 +00:00
@admin_level_required ( PERMS [ ' USER_SHADOWBAN ' ] )
2022-05-04 23:09:46 +00:00
def shadowban ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
if user . admin_level != 0 : abort ( 403 )
user . shadowbanned = v . username
2022-09-29 06:36:59 +00:00
reason = request . values . get ( " reason " ) . strip ( ) [ : 256 ]
user . ban_reason = reason
2022-05-04 23:09:46 +00:00
g . db . add ( user )
2022-09-29 06:36:59 +00:00
if request . values . get ( " alts " ) :
for alt in user . alts :
if alt . admin_level : continue
alt . shadowbanned = v . username
alt . ban_reason = reason
g . db . add ( alt )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " shadowban " ,
user_id = v . id ,
target_user_id = user . id ,
2022-09-29 06:36:59 +00:00
_note = f ' reason: " { reason } " '
2022-05-04 23:09:46 +00:00
)
g . db . add ( ma )
cache . delete_memoized ( frontlist )
2022-09-29 06:36:59 +00:00
return redirect ( user . url )
2022-05-04 23:09:46 +00:00
@app.post ( " /unshadowban/<user_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 03:04:38 +00:00
@admin_level_required ( PERMS [ ' USER_SHADOWBAN ' ] )
2022-05-04 23:09:46 +00:00
def unshadowban ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
user . shadowbanned = None
2022-09-29 06:36:59 +00:00
if not user . is_banned : user . ban_reason = None
2022-05-04 23:09:46 +00:00
g . db . add ( user )
for alt in user . alts :
alt . shadowbanned = None
2022-09-29 06:36:59 +00:00
if not alt . is_banned : alt . ban_reason = None
2022-05-04 23:09:46 +00:00
g . db . add ( alt )
ma = ModAction (
kind = " unshadowban " ,
user_id = v . id ,
target_user_id = user . id ,
)
g . db . add ( ma )
cache . delete_memoized ( frontlist )
2022-09-29 06:36:59 +00:00
return redirect ( user . url )
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/title_change/<user_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 05:45:44 +00:00
@admin_level_required ( PERMS [ ' USER_TITLE_CHANGE ' ] )
2022-05-04 23:09:46 +00:00
def admin_title_change ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
new_name = request . values . get ( " title " ) . strip ( ) [ : 256 ]
user . customtitleplain = new_name
new_name = filter_emojis_only ( new_name )
2022-06-24 13:19:53 +00:00
user = get_account ( user . id )
2022-05-04 23:09:46 +00:00
user . customtitle = new_name
if request . values . get ( " locked " ) : user . flairchanged = int ( time . time ( ) ) + 2629746
else :
user . flairchanged = None
badge = user . has_badge ( 96 )
if badge : g . db . delete ( badge )
g . db . add ( user )
if user . flairchanged : kind = " set_flair_locked "
else : kind = " set_flair_notlocked "
ma = ModAction (
kind = kind ,
user_id = v . id ,
target_user_id = user . id ,
_note = f ' " { user . customtitleplain } " '
)
g . db . add ( ma )
return redirect ( user . url )
@app.post ( " /ban_user/<user_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 01:58:43 +00:00
@admin_level_required ( PERMS [ ' USER_BAN ' ] )
2022-05-04 23:09:46 +00:00
def ban_user ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-05-04 23:09:46 +00:00
2022-10-11 13:48:49 +00:00
if user . admin_level > v . admin_level :
abort ( 403 )
2022-05-04 23:09:46 +00:00
2022-10-15 11:02:02 +00:00
days = 0.0
try :
days = float ( request . values . get ( " days " ) )
except :
pass
2022-05-04 23:09:46 +00:00
2022-09-29 06:36:59 +00:00
reason = request . values . get ( " reason " ) . strip ( ) [ : 256 ]
2022-05-30 02:55:51 +00:00
reason = filter_emojis_only ( reason )
2022-05-04 23:09:46 +00:00
2022-05-30 02:55:51 +00:00
if reason . startswith ( " / " ) and ' \\ ' not in reason :
reason = f ' <a href= " { reason . split ( ) [ 0 ] } " > { reason } </a> '
2022-05-04 23:09:46 +00:00
2022-05-30 02:55:51 +00:00
user . ban ( admin = v , reason = reason , days = days )
2022-05-04 23:09:46 +00:00
if request . values . get ( " alts " ) :
for x in user . alts :
2022-10-11 13:48:49 +00:00
if x . admin_level > v . admin_level :
continue
2022-05-30 02:55:51 +00:00
x . ban ( admin = v , reason = reason , days = days )
2022-05-04 23:09:46 +00:00
2022-10-15 11:02:02 +00:00
duration = " permanently "
2022-05-04 23:09:46 +00:00
if days :
2022-10-22 16:40:52 +00:00
days_txt = str ( days ) . split ( ' .0 ' ) [ 0 ]
2022-10-15 11:02:02 +00:00
duration = f " for { days_txt } day "
if days != 1 : duration + = " s "
2022-10-10 06:11:17 +00:00
if reason : text = f " @ { v . username } (Admin) has banned you for ** { days_txt } ** days for the following reason: \n \n > { reason } "
else : text = f " @ { v . username } (Admin) has banned you for ** { days_txt } ** days. "
2022-05-04 23:09:46 +00:00
else :
2022-10-10 06:11:17 +00:00
if reason : text = f " @ { v . username } (Admin) has banned you permanently for the following reason: \n \n > { reason } "
else : text = f " @ { v . username } (Admin) has banned you permanently. "
2022-05-04 23:09:46 +00:00
send_repeatable_notification ( user . id , text )
note = f ' reason: " { reason } " , duration: { duration } '
ma = ModAction (
kind = " ban_user " ,
user_id = v . id ,
target_user_id = user . id ,
_note = note
)
g . db . add ( ma )
if ' reason ' in request . values :
2022-06-02 23:18:10 +00:00
if request . values [ " reason " ] . startswith ( " /post/ " ) :
2022-07-28 14:23:38 +00:00
try : post = int ( request . values [ " reason " ] . split ( " /post/ " ) [ 1 ] . split ( None , 1 ) [ 0 ] )
except : abort ( 400 )
2022-06-25 00:11:00 +00:00
post = get_post ( post )
2022-06-28 00:59:08 +00:00
post . bannedfor = f ' { duration } by @ { v . username } '
2022-06-25 00:11:00 +00:00
g . db . add ( post )
2022-06-02 23:18:10 +00:00
elif request . values [ " reason " ] . startswith ( " /comment/ " ) :
2022-07-28 14:23:38 +00:00
try : comment = int ( request . values [ " reason " ] . split ( " /comment/ " ) [ 1 ] . split ( None , 1 ) [ 0 ] )
except : abort ( 400 )
2022-06-25 00:11:00 +00:00
comment = get_comment ( comment )
2022-06-28 00:59:08 +00:00
comment . bannedfor = f ' { duration } by @ { v . username } '
2022-06-25 00:11:00 +00:00
g . db . add ( comment )
2022-05-04 23:09:46 +00:00
if ' redir ' in request . values : return redirect ( user . url )
2022-09-11 14:32:00 +00:00
else : return { " message " : f " @ { user . username } has been banned! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /unban_user/<user_id> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 01:58:43 +00:00
@admin_level_required ( PERMS [ ' USER_BAN ' ] )
2022-05-04 23:09:46 +00:00
def unban_user ( user_id , v ) :
2022-06-24 13:19:53 +00:00
user = get_account ( user_id )
2022-09-01 21:44:40 +00:00
if not user . is_banned :
abort ( 400 )
2022-05-04 23:09:46 +00:00
user . is_banned = 0
user . unban_utc = 0
user . ban_reason = None
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( user . id , f " @ { v . username } (Admin) has unbanned you! " )
2022-05-04 23:09:46 +00:00
g . db . add ( user )
for x in user . alts :
2022-10-10 06:11:17 +00:00
if x . is_banned : send_repeatable_notification ( x . id , f " @ { v . username } (Admin) has unbanned you! " )
2022-05-04 23:09:46 +00:00
x . is_banned = 0
x . unban_utc = 0
x . ban_reason = None
g . db . add ( x )
ma = ModAction (
kind = " unban_user " ,
user_id = v . id ,
target_user_id = user . id ,
)
g . db . add ( ma )
if " @ " in request . referrer : return redirect ( user . url )
2022-09-11 14:32:00 +00:00
else : return { " message " : f " @ { user . username } has been unbanned! " }
2022-05-04 23:09:46 +00:00
2022-09-01 21:29:27 +00:00
@app.post ( " /mute_user/<int:user_id>/<int:mute_status> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 01:58:43 +00:00
@admin_level_required ( PERMS [ ' USER_BAN ' ] )
2022-09-01 21:29:27 +00:00
def mute_user ( v , user_id , mute_status ) :
user = get_account ( user_id )
if mute_status != 0 and not user . is_muted :
user . is_muted = True
log_action = ' mod_mute_user '
2022-09-11 14:32:00 +00:00
success_msg = f " @ { user . username } has been muted! "
2022-09-01 21:29:27 +00:00
elif mute_status == 0 and user . is_muted :
user . is_muted = False
log_action = ' mod_unmute_user '
2022-09-11 14:32:00 +00:00
success_msg = f " @ { user . username } has been un-muted! "
2022-09-01 21:29:27 +00:00
else :
abort ( 400 )
ma = ModAction (
kind = log_action ,
user_id = v . id ,
target_user_id = user . id ,
)
g . db . add ( user )
g . db . add ( ma )
if ' redir ' in request . values :
return redirect ( user . url )
else :
return { " message " : success_msg }
2022-05-04 23:09:46 +00:00
2022-08-11 04:04:41 +00:00
@app.post ( " /remove_post/<post_id> " )
2022-05-04 23:09:46 +00:00
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-08-11 04:04:41 +00:00
def remove_post ( post_id , v ) :
2022-06-24 13:19:53 +00:00
post = get_post ( post_id )
2022-05-04 23:09:46 +00:00
post . is_banned = True
post . is_approved = None
2022-10-25 22:48:10 +00:00
if post . stickied and not post . stickied . endswith ( PIN_AWARD_TEXT ) :
2022-10-25 15:32:32 +00:00
post . stickied = None
2022-10-24 17:43:14 +00:00
post . is_pinned = False
2022-05-04 23:09:46 +00:00
post . ban_reason = v . username
g . db . add ( post )
ma = ModAction (
kind = " ban_post " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
cache . delete_memoized ( frontlist )
v . coins + = 1
g . db . add ( v )
2022-10-13 07:27:56 +00:00
purge_files_in_cache ( f " https:// { SITE } /logged_out " )
2022-05-04 23:09:46 +00:00
return { " message " : " Post removed! " }
2022-08-11 04:04:41 +00:00
@app.post ( " /approve_post/<post_id> " )
2022-05-04 23:09:46 +00:00
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-08-11 04:04:41 +00:00
def approve_post ( post_id , v ) :
2022-05-04 23:09:46 +00:00
2022-06-24 13:19:53 +00:00
post = get_post ( post_id )
2022-05-04 23:09:46 +00:00
2022-08-19 22:20:25 +00:00
if post . author . id == v . id and post . author . agendaposter and AGENDAPOSTER_PHRASE not in post . body . lower ( ) and post . sub != ' chudrama ' :
2022-10-11 13:01:39 +00:00
abort ( 400 , " You can ' t bypass the chud award! " )
2022-05-09 14:07:29 +00:00
2022-05-04 23:09:46 +00:00
if post . is_banned :
ma = ModAction (
kind = " unban_post " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
post . is_banned = False
post . ban_reason = None
post . is_approved = v . id
g . db . add ( post )
cache . delete_memoized ( frontlist )
2022-10-12 16:33:00 +00:00
v . charge_account ( ' coins ' , 1 )
2022-05-04 23:09:46 +00:00
g . db . add ( v )
return { " message " : " Post approved! " }
@app.post ( " /distinguish/<post_id> " )
2022-10-06 04:19:11 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_DISTINGUISH ' ] )
2022-08-11 04:05:23 +00:00
def distinguish_post ( post_id , v ) :
2022-06-24 13:19:53 +00:00
post = get_post ( post_id )
2022-05-04 23:09:46 +00:00
if post . distinguish_level :
post . distinguish_level = 0
kind = ' undistinguish_post '
else :
post . distinguish_level = v . admin_level
kind = ' distinguish_post '
g . db . add ( post )
ma = ModAction (
kind = kind ,
user_id = v . id ,
target_submission_id = post . id
)
g . db . add ( ma )
if post . distinguish_level : return { " message " : " Post distinguished! " }
else : return { " message " : " Post undistinguished! " }
@app.post ( " /sticky/<post_id> " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-10-11 07:29:24 +00:00
@feature_required ( ' PINS ' )
2022-05-04 23:09:46 +00:00
def sticky_post ( post_id , v ) :
2022-10-23 21:36:38 +00:00
post = get_post ( post_id )
2022-10-23 22:17:49 +00:00
if post . is_banned : abort ( 403 , " Can ' t sticky removed posts! " )
2022-10-25 22:48:10 +00:00
if post . stickied and post . stickied . endswith ( PIN_AWARD_TEXT ) :
2022-10-23 21:36:38 +00:00
abort ( 403 , " Can ' t pin award pins! " )
2022-10-14 18:28:20 +00:00
pins = g . db . query ( Submission ) . filter ( Submission . stickied != None , Submission . is_banned == False ) . count ( )
2022-10-26 23:32:30 +00:00
extra_pin_slots = 1 if post . stickied else 0
2022-10-14 18:28:20 +00:00
2022-10-26 23:32:30 +00:00
if pins > = PIN_LIMIT + extra_pin_slots and v . admin_level < PERMS [ ' BYPASS_PIN_LIMIT ' ] :
2022-10-14 18:28:20 +00:00
abort ( 403 , f " Can ' t exceed { PIN_LIMIT } pinned posts limit! " )
2022-10-10 09:51:29 +00:00
2022-10-14 18:28:20 +00:00
if not post . stickied_utc :
post . stickied_utc = int ( time . time ( ) ) + 3600
pin_time = ' for 1 hour '
2022-10-14 18:35:13 +00:00
if v . id != post . author_id :
send_repeatable_notification ( post . author_id , f " @ { v . username } (Admin) has pinned [ { post . title } ](/post/ { post_id } )! " )
2022-10-14 18:28:20 +00:00
else :
post . stickied_utc = None
2022-10-14 18:28:46 +00:00
pin_time = ' permanently '
2022-10-14 18:28:20 +00:00
post . stickied = v . username
g . db . add ( post )
2022-05-04 23:09:46 +00:00
2022-10-14 18:28:20 +00:00
ma = ModAction (
kind = " pin_post " ,
user_id = v . id ,
target_submission_id = post . id ,
_note = pin_time
)
g . db . add ( ma )
cache . delete_memoized ( frontlist )
2022-05-04 23:09:46 +00:00
2022-10-25 22:48:10 +00:00
return { " message " : f " Post pinned { pin_time } ! " , " length " : pin_time } , 201
2022-05-04 23:09:46 +00:00
@app.post ( " /unsticky/<post_id> " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def unsticky_post ( post_id , v ) :
2022-06-24 13:19:53 +00:00
post = get_post ( post_id )
2022-09-30 22:40:02 +00:00
if post . stickied :
2022-10-25 22:48:10 +00:00
if post . stickied . endswith ( PIN_AWARD_TEXT ) : abort ( 403 , " Can ' t unpin award pins! " )
2022-10-23 18:28:51 +00:00
if post . author_id == LAWLZ_ID and post . stickied_utc and SITE_NAME == ' rDrama ' : abort ( 403 , " Can ' t unpin lawlzposts! " )
2022-10-23 17:01:00 +00:00
2022-05-04 23:09:46 +00:00
post . stickied = None
post . stickied_utc = None
g . db . add ( post )
ma = ModAction (
kind = " unpin_post " ,
user_id = v . id ,
target_submission_id = post . id
)
g . db . add ( ma )
if v . id != post . author_id :
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( post . author_id , f " @ { v . username } (Admin) has unpinned [ { post . title } ](/post/ { post_id } )! " )
2022-05-04 23:09:46 +00:00
cache . delete_memoized ( frontlist )
return { " message " : " Post unpinned! " }
@app.post ( " /sticky_comment/<cid> " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def sticky_comment ( cid , v ) :
comment = get_comment ( cid , v = v )
2022-05-26 23:08:23 +00:00
if not comment . stickied :
comment . stickied = v . username
2022-05-04 23:09:46 +00:00
g . db . add ( comment )
ma = ModAction (
kind = " pin_comment " ,
user_id = v . id ,
target_comment_id = comment . id
)
g . db . add ( ma )
if v . id != comment . author_id :
2022-10-10 06:11:17 +00:00
message = f " @ { v . username } (Admin) has pinned your [comment]( { comment . shortlink } )! "
2022-05-04 23:09:46 +00:00
send_repeatable_notification ( comment . author_id , message )
return { " message " : " Comment pinned! " }
@app.post ( " /unsticky_comment/<cid> " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def unsticky_comment ( cid , v ) :
comment = get_comment ( cid , v = v )
2022-05-26 23:08:23 +00:00
if comment . stickied :
2022-10-25 22:48:10 +00:00
if comment . stickied . endswith ( PIN_AWARD_TEXT ) : abort ( 403 , " Can ' t unpin award pins! " )
2022-05-04 23:09:46 +00:00
2022-05-26 23:08:23 +00:00
comment . stickied = None
2022-05-04 23:09:46 +00:00
g . db . add ( comment )
ma = ModAction (
kind = " unpin_comment " ,
user_id = v . id ,
target_comment_id = comment . id
)
g . db . add ( ma )
if v . id != comment . author_id :
2022-10-10 06:11:17 +00:00
message = f " @ { v . username } (Admin) has unpinned your [comment]( { comment . shortlink } )! "
2022-05-04 23:09:46 +00:00
send_repeatable_notification ( comment . author_id , message )
return { " message " : " Comment unpinned! " }
2022-08-11 04:04:41 +00:00
@app.post ( " /remove_comment/<c_id> " )
2022-05-04 23:09:46 +00:00
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-08-11 04:05:23 +00:00
def remove_comment ( c_id , v ) :
2022-06-24 13:19:53 +00:00
comment = get_comment ( c_id )
2022-05-04 23:09:46 +00:00
comment . is_banned = True
comment . is_approved = None
comment . ban_reason = v . username
g . db . add ( comment )
ma = ModAction (
kind = " ban_comment " ,
user_id = v . id ,
target_comment_id = comment . id ,
)
g . db . add ( ma )
2022-05-30 03:01:03 +00:00
2022-05-04 23:09:46 +00:00
return { " message " : " Comment removed! " }
2022-08-11 04:04:41 +00:00
@app.post ( " /approve_comment/<c_id> " )
2022-05-04 23:09:46 +00:00
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-08-11 04:05:23 +00:00
def approve_comment ( c_id , v ) :
2022-05-04 23:09:46 +00:00
2022-06-24 13:19:53 +00:00
comment = get_comment ( c_id )
2022-05-04 23:09:46 +00:00
if not comment : abort ( 404 )
2022-08-19 22:20:25 +00:00
if comment . author . id == v . id and comment . author . agendaposter and AGENDAPOSTER_PHRASE not in comment . body . lower ( ) and comment . post . sub != ' chudrama ' :
2022-10-11 13:01:39 +00:00
abort ( 400 , " You can ' t bypass the chud award! " )
2022-05-04 23:09:46 +00:00
if comment . is_banned :
ma = ModAction (
kind = " unban_comment " ,
user_id = v . id ,
target_comment_id = comment . id ,
)
g . db . add ( ma )
comment . is_banned = False
comment . ban_reason = None
comment . is_approved = v . id
g . db . add ( comment )
return { " message " : " Comment approved! " }
@app.post ( " /distinguish_comment/<c_id> " )
2022-10-06 04:19:11 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_DISTINGUISH ' ] )
2022-05-04 23:09:46 +00:00
def admin_distinguish_comment ( c_id , v ) :
comment = get_comment ( c_id , v = v )
if comment . distinguish_level :
comment . distinguish_level = 0
kind = ' undistinguish_comment '
else :
comment . distinguish_level = v . admin_level
kind = ' distinguish_comment '
g . db . add ( comment )
ma = ModAction (
kind = kind ,
user_id = v . id ,
target_comment_id = comment . id
)
g . db . add ( ma )
if comment . distinguish_level : return { " message " : " Comment distinguished! " }
else : return { " message " : " Comment undistinguished! " }
@app.get ( " /admin/dump_cache " )
2022-10-06 06:21:04 +00:00
@admin_level_required ( PERMS [ ' SITE_CACHE_DUMP_INTERNAL ' ] )
2022-05-04 23:09:46 +00:00
def admin_dump_cache ( v ) :
2022-09-01 23:59:56 +00:00
online = cache . get ( ONLINE_STR )
2022-05-04 23:09:46 +00:00
cache . clear ( )
2022-09-01 23:59:56 +00:00
cache . set ( ONLINE_STR , online )
2022-05-04 23:09:46 +00:00
ma = ModAction (
kind = " dump_cache " ,
user_id = v . id
)
g . db . add ( ma )
return { " message " : " Internal cache cleared. " }
@app.get ( " /admin/banned_domains/ " )
2022-10-06 04:26:15 +00:00
@admin_level_required ( PERMS [ ' DOMAINS_BAN ' ] )
2022-05-04 23:09:46 +00:00
def admin_banned_domains ( v ) :
banned_domains = g . db . query ( BannedDomain ) . all ( )
return render_template ( " admin/banned_domains.html " , v = v , banned_domains = banned_domains )
2022-10-20 22:14:25 +00:00
@app.post ( " /admin/ban_domain " )
2022-05-04 23:09:46 +00:00
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 04:26:15 +00:00
@admin_level_required ( PERMS [ ' DOMAINS_BAN ' ] )
2022-10-20 22:14:25 +00:00
def ban_domain ( v ) :
2022-05-04 23:09:46 +00:00
2022-10-31 14:33:11 +00:00
domain = request . values . get ( " domain " , " " ) . strip ( ) . lower ( )
2022-05-04 23:09:46 +00:00
if not domain : abort ( 400 )
2022-09-29 06:36:59 +00:00
reason = request . values . get ( " reason " ) . strip ( )
2022-10-20 22:14:25 +00:00
if not reason : abort ( 400 , ' Reason is required! ' )
2022-05-04 23:09:46 +00:00
2022-10-20 22:14:25 +00:00
existing = g . db . get ( BannedDomain , domain )
if not existing :
2022-05-04 23:09:46 +00:00
d = BannedDomain ( domain = domain , reason = reason )
g . db . add ( d )
ma = ModAction (
kind = " ban_domain " ,
user_id = v . id ,
_note = f ' { domain } , reason: { reason } '
)
g . db . add ( ma )
return redirect ( " /admin/banned_domains/ " )
2022-10-20 22:14:25 +00:00
@app.post ( " /admin/unban_domain/<domain> " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
@admin_level_required ( PERMS [ ' DOMAINS_BAN ' ] )
def unban_domain ( v , domain ) :
existing = g . db . get ( BannedDomain , domain )
if not existing : abort ( 400 , ' Domain is not banned! ' )
g . db . delete ( existing )
ma = ModAction (
kind = " unban_domain " ,
user_id = v . id ,
_note = domain
)
g . db . add ( ma )
return { " message " : f " { domain } has been unbanned! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /admin/nuke_user " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 01:58:43 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def admin_nuke_user ( v ) :
user = get_user ( request . values . get ( " user " ) )
for post in g . db . query ( Submission ) . filter_by ( author_id = user . id ) . all ( ) :
if post . is_banned :
continue
post . is_banned = True
post . ban_reason = v . username
g . db . add ( post )
for comment in g . db . query ( Comment ) . filter_by ( author_id = user . id ) . all ( ) :
if comment . is_banned :
continue
comment . is_banned = True
comment . ban_reason = v . username
g . db . add ( comment )
ma = ModAction (
kind = " nuke_user " ,
user_id = v . id ,
target_user_id = user . id ,
)
g . db . add ( ma )
return redirect ( user . url )
@app.post ( " /admin/unnuke_user " )
@limiter.limit ( " 1/second;30/minute;200/hour;1000/day " )
2022-10-06 01:58:43 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2022-05-04 23:09:46 +00:00
def admin_nunuke_user ( v ) :
user = get_user ( request . values . get ( " user " ) )
for post in g . db . query ( Submission ) . filter_by ( author_id = user . id ) . all ( ) :
if not post . is_banned :
continue
post . is_banned = False
post . ban_reason = None
2022-07-12 20:00:19 +00:00
post . is_approved = v . id
2022-05-04 23:09:46 +00:00
g . db . add ( post )
for comment in g . db . query ( Comment ) . filter_by ( author_id = user . id ) . all ( ) :
if not comment . is_banned :
continue
comment . is_banned = False
comment . ban_reason = None
2022-07-12 20:00:19 +00:00
comment . is_approved = v . id
2022-05-04 23:09:46 +00:00
g . db . add ( comment )
ma = ModAction (
kind = " unnuke_user " ,
user_id = v . id ,
target_user_id = user . id ,
)
g . db . add ( ma )
2022-06-13 14:40:16 +00:00
return redirect ( user . url )