2022-05-04 23:09:46 +00:00
from files . mail import *
from files . __main__ import app , limiter , mail
from files . helpers . alerts import *
from files . helpers . const import *
2022-09-09 09:13:50 +00:00
from files . helpers . actions import *
2022-05-04 23:09:46 +00:00
from files . classes . award import AWARDS
from sqlalchemy import func
2022-05-18 19:29:07 +00:00
import os
2022-05-04 23:09:46 +00:00
from files . classes . mod_logs import ACTIONTYPES , ACTIONTYPES2
from files . classes . badges import BadgeDef
2022-06-07 12:31:24 +00:00
import files . helpers . stats as statshelper
2022-09-10 01:09:33 +00:00
from shutil import move , copyfile
2022-05-04 23:09:46 +00:00
@app.get ( " /r/drama/comments/<id>/<title> " )
@app.get ( " /r/Drama/comments/<id>/<title> " )
def rdrama ( id , title ) :
id = ' ' . join ( f ' { x } / ' for x in id )
return redirect ( f ' /archives/drama/comments/ { id } { title } .html ' )
@app.get ( " /marseys " )
@auth_required
def marseys ( v ) :
2022-09-02 23:58:55 +00:00
if SITE == ' rdrama.net ' :
2022-09-09 09:13:50 +00:00
marseys = g . db . query ( Marsey , User ) . join ( User , Marsey . author_id == User . id ) . filter ( Marsey . submitter_id == None )
2022-05-04 23:09:46 +00:00
sort = request . values . get ( " sort " , " usage " )
if sort == " usage " : marseys = marseys . order_by ( Marsey . count . desc ( ) , User . username )
else : marseys = marseys . order_by ( User . username , Marsey . count . desc ( ) )
else :
2022-09-09 09:13:50 +00:00
marseys = g . db . query ( Marsey ) . filter ( Marsey . submitter_id == None ) . order_by ( Marsey . count . desc ( ) )
2022-05-04 23:09:46 +00:00
return render_template ( " marseys.html " , v = v , marseys = marseys )
2022-05-05 21:12:07 +00:00
@app.get ( " /marsey_list.json " )
@cache.memoize ( timeout = 600 )
2022-05-04 23:09:46 +00:00
def marsey_list ( ) :
2022-07-09 04:32:48 +00:00
emojis = [ ]
2022-05-05 21:12:07 +00:00
2022-07-09 04:32:48 +00:00
# From database
if EMOJI_MARSEYS :
emojis = [ {
" name " : emoji . name ,
2022-09-02 23:58:55 +00:00
" author " : author if SITE == ' rdrama.net ' or author == " anton-d " else None ,
2022-07-09 04:32:48 +00:00
# yikes, I don't really like this DB schema. Next time be better
" tags " : emoji . tags . split ( " " ) + [ emoji . name [ len ( " marsey " ) : ] \
if emoji . name . startswith ( " marsey " ) else emoji . name ] ,
" count " : emoji . count ,
" class " : " Marsey "
2022-09-09 09:13:50 +00:00
} for emoji , author in g . db . query ( Marsey , User . username ) . join ( User , Marsey . author_id == User . id ) . filter ( Marsey . submitter_id == None ) \
2022-07-09 04:32:48 +00:00
. order_by ( Marsey . count . desc ( ) ) ]
2022-05-05 21:12:07 +00:00
2022-07-09 04:32:48 +00:00
# Static shit
for src in EMOJI_SRCS :
with open ( src , " r " , encoding = " utf-8 " ) as f :
emojis = emojis + json . load ( f )
2022-05-04 23:09:46 +00:00
2022-05-05 21:12:07 +00:00
return jsonify ( emojis )
2022-05-04 23:09:46 +00:00
@app.get ( ' /rules ' )
@app.get ( ' /sidebar ' )
2022-05-06 04:55:15 +00:00
@app.get ( ' /logged_out/rules ' )
@app.get ( ' /logged_out/sidebar ' )
2022-05-04 23:09:46 +00:00
@auth_desired
def sidebar ( v ) :
2022-08-01 18:03:29 +00:00
if not v and not request . path . startswith ( ' /logged_out ' ) : return redirect ( f " /logged_out { request . full_path } " )
if v and request . path . startswith ( ' /logged_out ' ) : return redirect ( request . full_path . replace ( ' /logged_out ' , ' ' ) )
2022-05-06 04:55:15 +00:00
2022-05-04 23:09:46 +00:00
return render_template ( ' sidebar.html ' , v = v )
@app.get ( " /stats " )
@auth_required
def participation_stats ( v ) :
2022-09-05 20:23:35 +00:00
return render_template ( " stats.html " , v = v , title = " Content Statistics " , data = stats_cached ( ) )
2022-05-04 23:09:46 +00:00
@cache.memoize ( timeout = 86400 )
2022-06-10 09:47:41 +00:00
def stats_cached ( ) :
return statshelper . stats ( SITE_NAME )
2022-05-04 23:09:46 +00:00
@app.get ( " /chart " )
def chart ( ) :
return redirect ( ' /weekly_chart ' )
@app.get ( " /weekly_chart " )
@auth_required
def weekly_chart ( v ) :
2022-06-07 12:31:24 +00:00
return send_file ( statshelper . chart_path ( kind = " weekly " , site = SITE ) )
2022-05-04 23:09:46 +00:00
@app.get ( " /daily_chart " )
@auth_required
def daily_chart ( v ) :
2022-06-07 12:31:24 +00:00
return send_file ( statshelper . chart_path ( kind = " daily " , site = SITE ) )
2022-05-04 23:09:46 +00:00
@app.get ( " /patrons " )
@app.get ( " /paypigs " )
@admin_level_required ( 3 )
def patrons ( v ) :
users = g . db . query ( User ) . filter ( User . patron > 0 ) . order_by ( User . patron . desc ( ) , User . id ) . all ( )
return render_template ( " patrons.html " , v = v , users = users )
@app.get ( " /admins " )
@app.get ( " /badmins " )
@auth_required
def admins ( v ) :
if v and v . admin_level > 2 :
admins = g . db . query ( User ) . filter ( User . admin_level > 1 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
admins + = g . db . query ( User ) . filter ( User . admin_level == 1 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
else : admins = g . db . query ( User ) . filter ( User . admin_level > 0 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
return render_template ( " admins.html " , v = v , admins = admins )
@app.get ( " /log " )
@app.get ( " /modlog " )
@auth_required
def log ( v ) :
try : page = max ( int ( request . values . get ( " page " , 1 ) ) , 1 )
except : page = 1
admin = request . values . get ( " admin " )
if admin : admin_id = get_id ( admin )
else : admin_id = 0
kind = request . values . get ( " kind " )
if v and v . admin_level > 1 : types = ACTIONTYPES
else : types = ACTIONTYPES2
2022-06-13 16:41:46 +00:00
if kind and kind not in types :
kind = None
actions = [ ]
else :
actions = g . db . query ( ModAction )
if not ( v and v . admin_level > = 2 ) :
actions = actions . filter ( ModAction . kind . notin_ ( [ " shadowban " , " unshadowban " ] ) )
if admin_id :
actions = actions . filter_by ( user_id = admin_id )
kinds = set ( [ x . kind for x in actions ] )
types2 = { }
for k , val in types . items ( ) :
if k in kinds : types2 [ k ] = val
types = types2
if kind : actions = actions . filter_by ( kind = kind )
actions = actions . order_by ( ModAction . id . desc ( ) ) . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
2022-05-04 23:09:46 +00:00
next_exists = len ( actions ) > 25
actions = actions [ : 25 ]
2022-06-07 02:18:56 +00:00
admins = [ x [ 0 ] for x in g . db . query ( User . username ) . filter ( User . admin_level > = 2 ) . order_by ( User . username ) . all ( ) ]
2022-05-04 23:09:46 +00:00
return render_template ( " log.html " , v = v , admins = admins , types = types , admin = admin , type = kind , actions = actions , next_exists = next_exists , page = page )
@app.get ( " /log/<id> " )
@auth_required
def log_item ( id , v ) :
try : id = int ( id )
except : abort ( 404 )
2022-06-18 00:57:23 +00:00
action = g . db . get ( ModAction , id )
2022-05-04 23:09:46 +00:00
if not action : abort ( 404 )
admins = [ x [ 0 ] for x in g . db . query ( User . username ) . filter ( User . admin_level > 1 ) . all ( ) ]
if v and v . admin_level > 1 : types = ACTIONTYPES
else : types = ACTIONTYPES2
return render_template ( " log.html " , v = v , actions = [ action ] , next_exists = False , page = 1 , action = action , admins = admins , types = types )
2022-06-15 07:27:04 +00:00
@app.get ( " /directory " )
2022-06-17 20:38:30 +00:00
@auth_desired
2022-06-15 07:27:04 +00:00
def static_megathread_index ( v ) :
return render_template ( " megathread_index.html " , v = v )
2022-05-04 23:09:46 +00:00
@app.get ( " /api " )
2022-05-22 16:14:32 +00:00
@auth_required
def api ( v ) :
return render_template ( " api.html " , v = v )
2022-05-04 23:09:46 +00:00
@app.get ( " /contact " )
2022-09-09 09:13:50 +00:00
@app.get ( " /contactus " )
@app.get ( " /contact_us " )
2022-05-04 23:09:46 +00:00
@app.get ( " /press " )
@app.get ( " /media " )
@auth_required
def contact ( v ) :
return render_template ( " contact.html " , v = v )
@app.post ( " /send_admin " )
@limiter.limit ( " 1/second;2/minute;6/hour;10/day " )
2022-07-13 18:14:37 +00:00
@limiter.limit ( " 1/second;2/minute;6/hour;10/day " , key_func = lambda : f ' { SITE } - { session . get ( " lo_user " ) } ' )
2022-05-04 23:09:46 +00:00
@auth_required
def submit_contact ( v ) :
body = request . values . get ( " message " )
if not body : abort ( 400 )
2022-09-01 21:29:27 +00:00
if v . is_muted :
abort ( 403 )
2022-05-04 23:09:46 +00:00
body = f ' This message has been sent automatically to all admins via [/contact](/contact) \n \n Message: \n \n ' + body
2022-06-18 17:41:24 +00:00
body + = process_files ( )
2022-05-04 23:09:46 +00:00
2022-06-18 17:41:24 +00:00
body = body . strip ( )
body_html = sanitize ( body )
2022-05-04 23:09:46 +00:00
new_comment = Comment ( author_id = v . id ,
2022-09-04 23:15:37 +00:00
parent_submission = None ,
level = 1 ,
body_html = body_html ,
sentto = 2
)
2022-05-04 23:09:46 +00:00
g . db . add ( new_comment )
g . db . flush ( )
new_comment . top_comment_id = new_comment . id
2022-08-13 03:11:44 +00:00
for admin in g . db . query ( User ) . filter ( User . admin_level > 2 , User . id != AEVANN_ID ) . all ( ) :
2022-05-04 23:09:46 +00:00
notif = Notification ( comment_id = new_comment . id , user_id = admin . id )
g . db . add ( notif )
return render_template ( " contact.html " , v = v , msg = " Your message has been sent. " )
@app.get ( ' /archives ' )
def archivesindex ( ) :
return redirect ( " /archives/index.html " )
@app.get ( ' /archives/<path:path> ' )
def archives ( path ) :
resp = make_response ( send_from_directory ( ' /archives ' , path ) )
if request . path . endswith ( ' .css ' ) : resp . headers . add ( " Content-Type " , " text/css " )
return resp
@app.get ( ' /e/<emoji> ' )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-04 23:09:46 +00:00
def emoji ( emoji ) :
if not emoji . endswith ( ' .webp ' ) : abort ( 404 )
resp = make_response ( send_from_directory ( ' assets/images/emojis ' , emoji ) )
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
2022-06-22 15:54:25 +00:00
@app.get ( ' /i/<path:path> ' )
2022-06-22 15:51:19 +00:00
@limiter.exempt
2022-06-22 15:54:25 +00:00
def image ( path ) :
resp = make_response ( send_from_directory ( ' assets/images ' , path ) )
2022-06-22 15:51:19 +00:00
if request . path . endswith ( ' .webp ' ) or request . path . endswith ( ' .gif ' ) or request . path . endswith ( ' .ttf ' ) or request . path . endswith ( ' .woff2 ' ) :
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
if request . path . endswith ( ' .webp ' ) :
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
2022-05-04 23:09:46 +00:00
@app.get ( ' /assets/<path:path> ' )
@app.get ( ' /static/assets/<path:path> ' )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-04 23:09:46 +00:00
def static_service ( path ) :
resp = make_response ( send_from_directory ( ' assets ' , path ) )
if request . path . endswith ( ' .webp ' ) or request . path . endswith ( ' .gif ' ) or request . path . endswith ( ' .ttf ' ) or request . path . endswith ( ' .woff2 ' ) :
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
if request . path . endswith ( ' .webp ' ) :
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
@app.get ( ' /images/<path> ' )
@app.get ( ' /hostedimages/<path> ' )
@app.get ( " /static/images/<path> " )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-04 23:09:46 +00:00
def images ( path ) :
2022-05-24 15:32:49 +00:00
resp = make_response ( send_from_directory ( ' /images ' , path ) )
2022-05-04 23:09:46 +00:00
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
2022-05-22 16:13:19 +00:00
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
@app.get ( ' /videos/<path> ' )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-22 16:13:19 +00:00
def videos ( path ) :
2022-05-24 15:32:49 +00:00
resp = make_response ( send_from_directory ( ' /videos ' , path ) )
2022-05-22 16:13:19 +00:00
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
2022-05-04 23:09:46 +00:00
return resp
2022-05-22 22:15:29 +00:00
@app.get ( ' /audio/<path> ' )
@limiter.exempt
def audio ( path ) :
2022-05-24 15:32:49 +00:00
resp = make_response ( send_from_directory ( ' /audio ' , path ) )
2022-05-22 22:15:29 +00:00
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
return resp
2022-05-04 23:09:46 +00:00
@app.get ( " /robots.txt " )
def robots_txt ( ) :
2022-09-11 01:56:47 +00:00
return send_file ( " assets/robots.txt " )
2022-05-04 23:09:46 +00:00
no = ( 21 , 22 , 23 , 24 , 25 , 26 , 27 )
@cache.memoize ( timeout = 3600 )
def badge_list ( site ) :
badges = g . db . query ( BadgeDef ) . filter ( BadgeDef . id . notin_ ( no ) ) . order_by ( BadgeDef . id ) . all ( )
counts_raw = g . db . query ( Badge . badge_id , func . count ( ) ) . group_by ( Badge . badge_id ) . all ( )
2022-05-09 11:21:49 +00:00
users = g . db . query ( User ) . count ( )
2022-05-04 23:09:46 +00:00
counts = { }
for c in counts_raw :
counts [ c [ 0 ] ] = ( c [ 1 ] , float ( c [ 1 ] ) * 100 / max ( users , 1 ) )
return badges , counts
@app.get ( " /badges " )
@auth_required
def badges ( v ) :
2022-07-19 23:59:39 +00:00
if not FEATURES [ ' BADGES ' ] :
abort ( 404 )
2022-05-04 23:09:46 +00:00
badges , counts = badge_list ( SITE )
return render_template ( " badges.html " , v = v , badges = badges , counts = counts )
@app.get ( " /blocks " )
2022-07-18 07:17:45 +00:00
@admin_level_required ( PERMS [ ' USER_BLOCKS_VISIBLE ' ] )
2022-05-04 23:09:46 +00:00
def blocks ( v ) :
blocks = g . db . query ( UserBlock ) . all ( )
users = [ ]
targets = [ ]
for x in blocks :
2022-06-13 03:03:36 +00:00
acc_user = get_account ( x . user_id )
2022-09-04 23:15:37 +00:00
acc_tgt = get_account ( x . target_id )
2022-06-13 03:03:36 +00:00
if acc_user . shadowbanned or acc_tgt . shadowbanned : continue
users . append ( acc_user )
targets . append ( acc_tgt )
2022-05-04 23:09:46 +00:00
return render_template ( " blocks.html " , v = v , users = users , targets = targets )
@app.get ( " /banned " )
@auth_required
def banned ( v ) :
2022-05-25 20:16:26 +00:00
users = g . db . query ( User ) . filter ( User . is_banned > 0 , User . unban_utc == 0 ) . all ( )
2022-05-04 23:09:46 +00:00
return render_template ( " banned.html " , v = v , users = users )
@app.get ( " /formatting " )
@auth_required
def formatting ( v ) :
return render_template ( " formatting.html " , v = v )
2022-06-10 22:14:03 +00:00
@app.get ( " /service-worker.js " )
2022-05-04 23:09:46 +00:00
def serviceworker ( ) :
2022-06-10 22:14:03 +00:00
with open ( " files/assets/js/service-worker.js " , " r " , encoding = " utf-8 " ) as f :
return Response ( f . read ( ) , mimetype = ' application/javascript ' )
2022-05-27 19:23:01 +00:00
2022-05-04 23:09:46 +00:00
@app.get ( " /settings/security " )
@auth_required
def settings_security ( v ) :
return render_template ( " settings_security.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
mfa_secret = pyotp . random_base32 ( ) if not v . mfa_secret else None
)
2022-05-04 23:09:46 +00:00
@app.post ( " /dismiss_mobile_tip " )
def dismiss_mobile_tip ( ) :
session [ " tooltip_last_dismissed " ] = int ( time . time ( ) )
return " " , 204
2022-07-13 15:20:10 +00:00
@app.get ( " /transfers/<id> " )
@auth_required
def transfers_id ( id , v ) :
try : id = int ( id )
except : abort ( 404 )
transfer = g . db . get ( Comment , id )
if not transfer : abort ( 404 )
return render_template ( " transfers.html " , v = v , page = 1 , comments = [ transfer ] , standalone = True , next_exists = False )
@app.get ( " /transfers " )
@auth_required
def transfers ( v ) :
comments = g . db . query ( Comment ) . filter ( Comment . author_id == AUTOJANNY_ID , Comment . parent_submission == None , Comment . body_html . like ( " % </a> has transferred % " ) ) . order_by ( Comment . id . desc ( ) )
try : page = max ( int ( request . values . get ( " page " , 1 ) ) , 1 )
except : page = 1
comments = comments . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
next_exists = len ( comments ) > 25
comments = comments [ : 25 ]
2022-09-10 00:30:25 +00:00
if request . headers . get ( " Authorization " ) :
return { " data " : [ x . json for x in comments ] }
else :
return render_template ( " transfers.html " , v = v , page = page , comments = comments , standalone = True , next_exists = next_exists )
2022-07-18 08:39:21 +00:00
@app.get ( " /kb/<page> " )
@auth_desired
def knowledgebase ( v , page ) :
if not knowledgebase_page_regex . fullmatch ( page ) :
abort ( 404 )
template_path = f ' kb/ { SITE_NAME } / { page } .html '
if not os . path . exists ( ' files/templates/ ' + template_path ) :
abort ( 404 )
return render_template ( template_path , v = v )
2022-08-07 06:56:13 +00:00
@app.get ( " /categories.json " )
def categories_json ( ) :
categories = g . db . query ( Category ) . all ( )
data = { }
for c in categories :
sub = c . sub if c . sub else ' '
sub_cats = ( data [ sub ] if sub in data else [ ] ) + [ c . as_json ( ) ]
data . update ( { sub : sub_cats } )
return jsonify ( data )