2022-05-04 23:09:46 +00:00
from __future__ import unicode_literals
2022-11-15 09:19:08 +00:00
import os
from shutil import copyfile
import pyotp
import requests
import youtube_dl
from files . helpers . actions import *
2022-05-04 23:09:46 +00:00
from files . helpers . alerts import *
2022-12-11 23:44:34 +00:00
from files . helpers . config . const import *
2022-06-24 13:19:53 +00:00
from files . helpers . get import *
2022-11-15 09:19:08 +00:00
from files . helpers . mail import *
from files . helpers . media import process_files , process_image
from files . helpers . regex import *
from files . helpers . sanitize import *
from files . helpers . sanitize import filter_emojis_only
2022-10-06 22:59:50 +00:00
from files . helpers . security import *
2022-11-15 09:19:08 +00:00
from files . helpers . useractions import *
from files . routes . wrappers import *
2022-05-04 23:09:46 +00:00
from . front import frontlist
2022-11-15 09:19:08 +00:00
from files . __main__ import app , cache , limiter
2022-11-06 07:02:15 +00:00
@app.get ( " /settings " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-11-06 07:02:15 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings ( v : User ) :
2022-11-06 07:02:15 +00:00
return redirect ( " /settings/personal " )
@app.get ( " /settings/personal " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-11-06 07:02:15 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings_personal ( v : User ) :
2023-01-27 11:48:48 +00:00
return render_template ( " settings/personal.html " , v = v , error = get_error ( ) , msg = get_msg ( ) )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
@app.delete ( ' /settings/background ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-11-06 12:08:39 +00:00
@auth_required
2022-11-06 12:04:00 +00:00
def remove_background ( v ) :
2022-12-03 22:17:24 +00:00
if v . background :
2022-12-05 00:02:29 +00:00
if v . background . startswith ( ' /images/ ' ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . background )
2022-11-06 12:04:00 +00:00
v . background = None
g . db . add ( v )
2022-05-04 23:09:46 +00:00
return { " message " : " Background removed! " }
2022-12-05 00:02:29 +00:00
@app.post ( ' /settings/custom_background ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-05 00:02:29 +00:00
@auth_required
def upload_custom_background ( v ) :
2023-01-27 11:57:29 +00:00
if g . is_tor : abort ( 403 , " Image uploads are not allowed through TOR! " )
2022-12-05 00:02:29 +00:00
if not v . patron :
abort ( 403 , f " This feature is only available to { patron } s! " )
file = request . files [ " file " ]
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
background = process_image ( name , v )
if background :
if v . background and v . background . startswith ( ' /images/ ' ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . background )
2022-12-05 00:02:29 +00:00
v . background = background
g . db . add ( v )
return redirect ( ' /settings/personal ' )
2022-12-05 04:16:45 +00:00
@app.post ( ' /settings/profile_background ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-05 04:16:45 +00:00
@auth_required
def upload_profile_background ( v ) :
2023-01-27 11:57:29 +00:00
if g . is_tor : abort ( 403 , " Image uploads are not allowed through TOR! " )
2022-12-05 04:16:45 +00:00
file = request . files [ " file " ]
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
background = process_image ( name , v )
if background :
2022-12-09 05:54:53 +00:00
if v . profile_background and path . isfile ( v . profile_background ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . profile_background )
2022-12-05 04:16:45 +00:00
v . profile_background = background
g . db . add ( v )
2022-12-07 07:53:46 +00:00
badge_grant ( badge_id = 193 , user = v )
2022-12-05 04:16:45 +00:00
return redirect ( f ' /@ { v . username } ' )
2022-12-05 15:14:53 +00:00
@app.delete ( ' /settings/profile_background ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-05 15:14:53 +00:00
@auth_required
def delete_profile_background ( v ) :
if v . profile_background :
os . remove ( v . profile_background )
v . profile_background = None
return { " message " : " Profile background removed! " }
2022-11-06 12:30:07 +00:00
@app.post ( " /settings/personal " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-06 22:55:31 +00:00
def settings_personal_post ( v ) :
2022-12-27 05:31:28 +00:00
if v . id == 253 and request . values . get ( " private " ) :
abort ( 403 )
2022-05-04 23:09:46 +00:00
updated = False
2022-11-07 03:28:27 +00:00
# begin common selectors #
2023-01-01 11:36:20 +00:00
2022-11-06 12:04:00 +00:00
def update_flag ( column_name : str , request_name : str ) :
2022-11-07 02:03:09 +00:00
if not request . values . get ( request_name , ' ' ) : return False
2022-11-06 12:04:00 +00:00
request_flag = request . values . get ( request_name , ' ' ) == ' true '
2022-11-07 00:40:51 +00:00
if request_flag != getattr ( v , column_name ) :
2022-11-06 12:04:00 +00:00
setattr ( v , column_name , request_flag )
return True
return False
2023-01-01 11:36:20 +00:00
2022-11-07 02:11:59 +00:00
def update_potentially_permanent_flag ( column_name : str , request_name : str , friendly_name : str , badge_id : Optional [ int ] ) :
2022-11-06 12:33:25 +00:00
if not request . values . get ( request_name ) : return False
2022-11-06 12:04:00 +00:00
current_value = getattr ( v , column_name )
if FEATURES [ ' USERS_PERMANENT_WORD_FILTERS ' ] and current_value > 1 :
2022-11-07 00:03:43 +00:00
abort ( 403 , f " Cannot change the { friendly_name } setting after you ' ve already set it permanently! " )
2022-11-06 22:55:31 +00:00
request_flag = int ( request . values . get ( request_name , ' ' ) == ' true ' )
2022-11-06 12:04:00 +00:00
if current_value and request_flag and request . values . get ( " permanent " , ' ' ) == ' true ' and request . values . get ( " username " ) == v . username :
2022-11-07 02:11:59 +00:00
if v . client : abort ( 403 , f " Cannot set { friendly_name } permanently from the API " )
2022-11-06 12:04:00 +00:00
request_flag = int ( time . time ( ) )
2022-11-06 22:55:31 +00:00
setattr ( v , column_name , request_flag )
2022-11-06 12:04:00 +00:00
if badge_id : badge_grant ( v , badge_id )
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " , v = v , msg = f " You have set the { friendly_name } permanently! Enjoy your new badge! " )
2022-11-06 12:04:00 +00:00
elif current_value != request_flag :
setattr ( v , column_name , request_flag )
return True
return False
2022-11-07 03:28:27 +00:00
def set_selector_option ( column_name : str , api_name : str , valid_values : Iterable [ str ] , error_msg : str = " value " ) :
opt = request . values . get ( api_name )
if opt : opt = opt . strip ( )
if not opt : return False
if opt in valid_values :
setattr ( v , column_name , opt )
return True
abort ( 400 , f " ' { opt } ' is not a valid { error_msg } " )
# end common selectors #
background = request . values . get ( " background " , v . background )
2022-11-07 03:50:38 +00:00
if background != v . background and background . endswith ( " .webp " ) and len ( background ) < = 20 :
2022-12-05 00:02:29 +00:00
v . background = ' /i/backgrounds/ ' + request . values . get ( " background " )
2022-11-07 03:28:27 +00:00
updated = True
2022-05-04 23:09:46 +00:00
elif request . values . get ( " reddit " , v . reddit ) != v . reddit :
reddit = request . values . get ( " reddit " )
if reddit in { ' old.reddit.com ' , ' reddit.com ' , ' i.reddit.com ' , ' teddit.net ' , ' libredd.it ' , ' unddit.com ' } :
updated = True
v . reddit = reddit
2022-10-25 16:07:40 +00:00
elif request . values . get ( " poor " , v . poor ) != v . poor :
2022-05-29 07:08:28 +00:00
updated = True
2022-11-07 03:28:27 +00:00
v . poor = request . values . get ( " poor " , v . poor ) == ' true '
2022-10-25 16:07:40 +00:00
session [ ' poor ' ] = v . poor
2023-01-01 11:36:20 +00:00
2022-11-07 02:11:59 +00:00
slur_filter_updated = updated or update_potentially_permanent_flag ( " slurreplacer " , " slurreplacer " , " slur replacer " , 192 )
2022-11-06 12:04:00 +00:00
if isinstance ( slur_filter_updated , bool ) :
updated = slur_filter_updated
else :
g . db . add ( v )
return slur_filter_updated
2023-01-01 11:36:20 +00:00
2022-11-07 02:11:59 +00:00
profanity_filter_updated = updated or update_potentially_permanent_flag ( " profanityreplacer " , " profanityreplacer " , " profanity replacer " , 190 )
2022-11-06 12:04:00 +00:00
if isinstance ( profanity_filter_updated , bool ) :
updated = profanity_filter_updated
else :
g . db . add ( v )
return profanity_filter_updated
updated = updated or update_flag ( " hidevotedon " , " hidevotedon " )
2022-11-21 16:55:09 +00:00
updated = updated or update_flag ( " newtab " , " newtab " )
2022-11-06 12:04:00 +00:00
updated = updated or update_flag ( " newtabexternal " , " newtabexternal " )
updated = updated or update_flag ( " nitter " , " nitter " )
updated = updated or update_flag ( " imginn " , " imginn " )
updated = updated or update_flag ( " controversial " , " controversial " )
updated = updated or update_flag ( " sigs_disabled " , " sigs_disabled " )
2022-11-07 05:45:09 +00:00
updated = updated or update_flag ( " over_18 " , " over_18 " )
2022-11-06 12:04:00 +00:00
updated = updated or update_flag ( " is_private " , " private " )
2022-11-06 22:55:31 +00:00
2022-11-06 12:04:00 +00:00
if not updated and request . values . get ( " spider " , v . spider ) != v . spider and v . spider < = 1 :
2022-09-25 02:44:19 +00:00
updated = True
v . spider = int ( request . values . get ( " spider " ) == ' true ' )
if v . spider : badge_grant ( user = v , badge_id = 179 )
2023-01-01 11:36:20 +00:00
else :
2022-09-25 20:17:11 +00:00
badge = v . has_badge ( 179 )
2022-09-25 02:44:19 +00:00
if badge : g . db . delete ( badge )
2023-01-01 05:33:09 +00:00
2023-01-01 05:48:15 +00:00
elif IS_FISTMAS ( ) and not updated and request . values . get ( " event_music " , v . event_music ) != v . event_music and v . can_toggle_event_music :
2022-12-20 01:38:19 +00:00
updated = True
v . event_music = not v . event_music
2023-01-01 10:41:27 +00:00
2022-12-03 02:14:01 +00:00
elif not updated and request . values . get ( " bio " ) == " " and not request . files . get ( ' file ' ) :
2022-05-04 23:09:46 +00:00
v . bio = None
v . bio_html = None
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/personal.html " , v = v , msg = " Your bio has been updated! " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and request . values . get ( " sig " ) == " " :
2022-05-04 23:09:46 +00:00
v . sig = None
v . sig_html = None
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/personal.html " , v = v , msg = " Your sig has been updated! " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and request . values . get ( " friends " ) == " " :
2022-05-04 23:09:46 +00:00
v . friends = None
v . friends_html = None
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/personal.html " , v = v , msg = " Your friends list has been updated! " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and request . values . get ( " enemies " ) == " " :
2022-05-04 23:09:46 +00:00
v . enemies = None
v . enemies_html = None
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/personal.html " , v = v , msg = " Your enemies list has been updated! " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and v . patron and request . values . get ( " sig " ) :
2022-07-17 17:24:58 +00:00
sig = request . values . get ( " sig " ) [ : 200 ] . replace ( ' \n ' , ' ' ) . replace ( ' \r ' , ' ' )
2023-02-07 03:31:49 +00:00
sig_html = sanitize ( sig , blackjack = " signature " )
2022-05-04 23:09:46 +00:00
if len ( sig_html ) > 1000 :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " Your sig is too long " )
2022-05-04 23:09:46 +00:00
v . sig = sig [ : 200 ]
v . sig_html = sig_html
g . db . add ( v )
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
msg = " Your sig has been updated. " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and FEATURES [ ' USERS_PROFILE_BODYTEXT ' ] and request . values . get ( " friends " ) :
2022-12-13 17:15:07 +00:00
friends = request . values . get ( " friends " ) [ : 1000 ]
2022-05-04 23:09:46 +00:00
2023-02-07 03:31:49 +00:00
friends_html = sanitize ( friends , blackjack = " friends " )
2022-05-04 23:09:46 +00:00
2022-12-13 17:15:07 +00:00
if len ( friends_html ) > 5000 :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " Your friends list is too long " )
2022-05-04 23:09:46 +00:00
notify_users = NOTIFY_USERS ( friends , v )
if notify_users :
2023-02-24 02:28:10 +00:00
text = f " @ { v . username } has added you to their friends list! "
cid = notif_comment ( text )
2022-05-04 23:09:46 +00:00
for x in notify_users :
2023-02-24 02:28:10 +00:00
add_notif ( cid , x , text )
2022-05-04 23:09:46 +00:00
2022-12-13 17:15:07 +00:00
v . friends = friends [ : 1000 ]
2022-05-04 23:09:46 +00:00
v . friends_html = friends_html
g . db . add ( v )
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
msg = " Your friends list has been updated. " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and FEATURES [ ' USERS_PROFILE_BODYTEXT ' ] and request . values . get ( " enemies " ) :
2022-12-13 17:15:07 +00:00
enemies = request . values . get ( " enemies " ) [ : 1000 ]
2022-05-04 23:09:46 +00:00
2023-02-07 03:31:49 +00:00
enemies_html = sanitize ( enemies , blackjack = " enemies " )
2022-05-04 23:09:46 +00:00
2022-12-13 17:15:07 +00:00
if len ( enemies_html ) > 5000 :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " Your enemies list is too long " )
2022-05-04 23:09:46 +00:00
notify_users = NOTIFY_USERS ( enemies , v )
if notify_users :
2023-02-24 02:28:10 +00:00
text = f " @ { v . username } has added you to their enemies list! "
cid = notif_comment ( text )
2022-05-04 23:09:46 +00:00
for x in notify_users :
2023-02-24 02:28:10 +00:00
add_notif ( cid , x , text )
2022-05-04 23:09:46 +00:00
2022-12-13 17:15:07 +00:00
v . enemies = enemies [ : 1000 ]
2022-05-04 23:09:46 +00:00
v . enemies_html = enemies_html
g . db . add ( v )
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
msg = " Your enemies list has been updated. " )
2022-05-04 23:09:46 +00:00
2022-11-06 12:04:00 +00:00
elif not updated and FEATURES [ ' USERS_PROFILE_BODYTEXT ' ] and \
2022-07-20 01:16:59 +00:00
( request . values . get ( " bio " ) or request . files . get ( ' file ' ) ) :
2022-05-04 23:09:46 +00:00
bio = request . values . get ( " bio " ) [ : 1500 ]
2022-11-15 09:19:08 +00:00
bio + = process_files ( request . files , v )
2022-05-22 10:26:59 +00:00
bio = bio . strip ( )
2023-02-07 03:31:49 +00:00
bio_html = sanitize ( bio , blackjack = " bio " )
2022-05-04 23:09:46 +00:00
if len ( bio_html ) > 10000 :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " Your bio is too long " )
2022-05-04 23:09:46 +00:00
if len ( bio_html ) > 10000 : abort ( 400 )
v . bio = bio [ : 1500 ]
v . bio_html = bio_html
g . db . add ( v )
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
msg = " Your bio has been updated. " )
2022-05-04 23:09:46 +00:00
frontsize = request . values . get ( " frontsize " )
if frontsize :
2022-11-06 22:55:31 +00:00
frontsize = int ( frontsize )
2022-11-06 12:04:00 +00:00
if frontsize in PAGE_SIZES :
2022-11-06 22:55:31 +00:00
v . frontsize = frontsize
2022-05-04 23:09:46 +00:00
updated = True
cache . delete_memoized ( frontlist )
else : abort ( 400 )
2023-01-01 11:36:20 +00:00
2022-11-07 03:28:27 +00:00
updated = updated or set_selector_option ( " defaultsortingcomments " , " defaultsortingcomments " , COMMENT_SORTS , " comment sort " )
updated = updated or set_selector_option ( " defaultsorting " , " defaultsorting " , SORTS , " post sort " )
updated = updated or set_selector_option ( " defaulttime " , " defaulttime " , TIME_FILTERS , " time filter " )
2022-05-04 23:09:46 +00:00
theme = request . values . get ( " theme " )
2022-11-07 03:28:27 +00:00
if not updated and theme :
2022-11-06 12:04:00 +00:00
if theme in THEMES :
2022-12-14 16:59:00 +00:00
if v . theme == " win98 " : v . themecolor = DEFAULT_COLOR
2022-05-04 23:09:46 +00:00
v . theme = theme
if theme == " win98 " : v . themecolor = " 30409f "
updated = True
2022-11-06 12:04:00 +00:00
else : abort ( 400 , f " { theme } is not a valid theme " )
2022-05-04 23:09:46 +00:00
house = request . values . get ( " house " )
2022-11-07 03:28:27 +00:00
if not updated and house and house in HOUSES and FEATURES [ ' HOUSES ' ] :
2022-08-27 02:57:19 +00:00
if v . bite : abort ( 403 )
2022-11-05 05:49:12 +00:00
if v . house :
if v . house . replace ( ' Founder ' , ' ' ) == house : abort ( 409 , f " You ' re already in House { house } " )
2022-11-05 06:00:02 +00:00
cost = HOUSE_SWITCH_COST
2023-01-01 11:36:20 +00:00
else :
2022-11-05 06:00:02 +00:00
cost = HOUSE_JOIN_COST
2022-05-04 23:09:46 +00:00
2022-11-01 05:25:19 +00:00
success = v . charge_account ( ' coins ' , cost )
if not success :
2022-11-21 23:08:29 +00:00
success = v . charge_account ( ' marseybux ' , cost )
2022-11-01 05:25:19 +00:00
if not success : abort ( 403 )
2022-05-04 23:09:46 +00:00
2023-01-01 11:36:20 +00:00
if house == " None " : house = ' '
2022-05-04 23:09:46 +00:00
v . house = house
updated = True
if updated :
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return { " message " : " Your settings have been updated! " }
2022-05-04 23:09:46 +00:00
else :
2023-01-27 11:57:29 +00:00
abort ( 400 , " You didn ' t change anything! " )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/filters " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def filters ( v : User ) :
2022-05-04 23:09:46 +00:00
filters = request . values . get ( " filters " ) [ : 1000 ] . strip ( )
if filters == v . custom_filter_list :
2023-01-27 11:28:50 +00:00
return redirect ( " /settings/advanced?error=You didn ' t change anything! " )
2022-05-04 23:09:46 +00:00
v . custom_filter_list = filters
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return redirect ( " /settings/advanced?msg=Your custom filters have been updated! " )
2022-05-04 23:09:46 +00:00
2022-11-07 00:03:43 +00:00
def set_color ( v : User , attr : str , color : Optional [ str ] ) :
current = getattr ( v , attr )
color = color . strip ( ) . lower ( ) if color else None
if color :
if color . startswith ( ' # ' ) : color = color [ 1 : ]
if not color_regex . fullmatch ( color ) :
2022-11-15 14:33:18 +00:00
return render_template ( " settings/personal.html " , v = v , error = " Invalid color hex code! " )
2022-11-07 00:03:43 +00:00
if color and current != color :
setattr ( v , attr , color )
g . db . add ( v )
2022-11-15 14:33:18 +00:00
return render_template ( " settings/personal.html " , v = v , msg = " Color successfully updated! " )
2022-11-07 00:03:43 +00:00
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/namecolor " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def namecolor ( v ) :
2022-11-07 21:15:25 +00:00
return set_color ( v , " namecolor " , request . values . get ( " namecolor " ) )
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/themecolor " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def themecolor ( v ) :
2022-11-07 00:03:43 +00:00
return set_color ( v , " themecolor " , request . values . get ( " themecolor " ) )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/titlecolor " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def titlecolor ( v ) :
2022-11-07 04:31:38 +00:00
return set_color ( v , " titlecolor " , request . values . get ( " titlecolor " ) )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/verifiedcolor " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def verifiedcolor ( v ) :
2022-11-07 00:03:43 +00:00
if not v . verified : abort ( 403 , " You don ' t have a checkmark " )
2022-11-15 09:19:08 +00:00
return set_color ( v , " verifiedcolor " , request . values . get ( " verifiedcolor " ) )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/security " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_security_post ( v ) :
if request . values . get ( " new_password " ) :
if request . values . get ( " new_password " ) != request . values . get ( " cnf_password " ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Passwords do not match! " )
2022-05-04 23:09:46 +00:00
if not valid_password_regex . fullmatch ( request . values . get ( " new_password " ) ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Password must be between 8 and 100 characters! " )
2022-05-04 23:09:46 +00:00
if not v . verifyPass ( request . values . get ( " old_password " ) ) :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/security.html " , v = v , error = " Incorrect password " )
2022-05-04 23:09:46 +00:00
2022-10-06 22:59:50 +00:00
v . passhash = hash_password ( request . values . get ( " new_password " ) )
2022-05-04 23:09:46 +00:00
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , msg = " Your password has been changed! " )
2022-05-04 23:09:46 +00:00
if request . values . get ( " new_email " ) :
if not v . verifyPass ( request . values . get ( ' password ' ) ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Invalid password! " )
2022-05-04 23:09:46 +00:00
new_email = request . values . get ( " new_email " , " " ) . strip ( ) . lower ( )
if new_email == v . email :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/security.html " , v = v , error = " This email is already yours! " )
2022-05-04 23:09:46 +00:00
url = f " { SITE_FULL } /activate "
now = int ( time . time ( ) )
token = generate_hash ( f " { new_email } + { v . id } + { now } " )
params = f " ?email= { quote ( new_email ) } &id= { v . id } &time= { now } &token= { token } "
link = url + params
send_mail ( to_address = new_email ,
2022-09-04 23:15:37 +00:00
subject = " Verify your email address. " ,
html = render_template ( " email/email_change.html " ,
action_url = link ,
v = v )
)
2022-05-04 23:09:46 +00:00
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , msg = " We have sent you an email, click the verification link inside it to complete the email change. Check your spam folder if you can ' t find it! " )
2022-05-04 23:09:46 +00:00
if request . values . get ( " 2fa_token " ) :
if not v . verifyPass ( request . values . get ( ' password ' ) ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Invalid password! " )
2022-05-04 23:09:46 +00:00
secret = request . values . get ( " 2fa_secret " )
x = pyotp . TOTP ( secret )
if not x . verify ( request . values . get ( " 2fa_token " ) , valid_window = 1 ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Invalid token! " )
2022-05-04 23:09:46 +00:00
v . mfa_secret = secret
g . db . add ( v )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , msg = " Two-factor authentication enabled! " )
2022-05-04 23:09:46 +00:00
if request . values . get ( " 2fa_remove " ) :
if not v . verifyPass ( request . values . get ( ' password ' ) ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Invalid password! " )
2022-05-04 23:09:46 +00:00
token = request . values . get ( " 2fa_remove " )
2022-12-20 21:15:24 +00:00
if not token or not v . validate_2fa ( token ) :
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , error = " Invalid token! " )
2022-05-04 23:09:46 +00:00
v . mfa_secret = None
g . db . add ( v )
2022-11-18 20:07:07 +00:00
g . db . commit ( )
2023-01-27 11:57:29 +00:00
return render_template ( " settings/security.html " , v = v , msg = " Two-factor authentication disabled! " )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/log_out_all_others " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_log_out_others ( v ) :
submitted_password = request . values . get ( " password " , " " ) . strip ( )
if not v . verifyPass ( submitted_password ) :
2023-01-27 11:33:03 +00:00
return redirect ( " /settings/security?error=Incorrect password! " )
2022-05-04 23:09:46 +00:00
v . login_nonce + = 1
session [ " login_nonce " ] = v . login_nonce
g . db . add ( v )
2023-01-27 11:33:03 +00:00
return redirect ( " /settings/security?msg=All other devices have been logged out! " )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/images/profile " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_images_profile ( v ) :
2023-01-27 11:57:29 +00:00
if g . is_tor : abort ( 403 , " Image uploads are not allowed through TOR! " )
2022-05-04 23:09:46 +00:00
file = request . files [ " profile " ]
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
2022-11-15 09:19:08 +00:00
highres = process_image ( name , v )
2022-05-04 23:09:46 +00:00
if not highres : abort ( 400 )
name2 = name . replace ( ' .webp ' , ' r.webp ' )
copyfile ( name , name2 )
2022-11-15 09:19:08 +00:00
imageurl = process_image ( name2 , v , resize = 100 )
2022-05-04 23:09:46 +00:00
if not imageurl : abort ( 400 )
2023-02-17 14:21:12 +00:00
if v . highres and ' /images/ ' in v . highres and path . isfile ( v . highres ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . highres )
2023-02-17 14:21:12 +00:00
if v . profileurl and ' /images/ ' in v . profileurl and path . isfile ( v . profileurl ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . profileurl )
2023-02-17 14:21:12 +00:00
2022-05-04 23:09:46 +00:00
v . highres = highres
v . profileurl = imageurl
g . db . add ( v )
2023-01-25 03:18:17 +00:00
cache . delete_memoized ( get_profile_picture , v . id )
cache . delete_memoized ( get_profile_picture , v . username )
cache . delete_memoized ( get_profile_picture , v . original_username )
2022-05-04 23:09:46 +00:00
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Profile picture successfully updated! " )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/images/banner " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2022-11-14 15:11:05 +00:00
@feature_required ( ' USERS_PROFILE_BANNER ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_images_banner ( v ) :
2023-01-27 11:57:29 +00:00
if g . is_tor : abort ( 403 , " Image uploads are not allowed through TOR! " )
2022-05-04 23:09:46 +00:00
file = request . files [ " banner " ]
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
2022-11-15 09:19:08 +00:00
bannerurl = process_image ( name , v )
2022-05-04 23:09:46 +00:00
if bannerurl :
2022-12-23 21:44:47 +00:00
if v . bannerurl and ' /images/ ' in v . bannerurl and path . isfile ( v . bannerurl ) :
2022-12-05 15:10:15 +00:00
os . remove ( v . bannerurl )
2022-05-04 23:09:46 +00:00
v . bannerurl = bannerurl
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Banner successfully updated! " )
2022-05-04 23:09:46 +00:00
@app.get ( " /settings/css " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings_css_get ( v : User ) :
2023-01-27 10:04:30 +00:00
return render_template ( " settings/css.html " , v = v , msg = get_msg ( ) , profilecss = v . profilecss )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/css " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_css ( v ) :
2022-10-11 13:01:39 +00:00
if v . agendaposter : abort ( 400 , " Agendapostered users can ' t edit CSS! " )
2022-12-10 18:30:15 +00:00
css = request . values . get ( " css " , v . css ) . strip ( ) . replace ( ' \\ ' , ' ' ) . strip ( ) [ : CSS_LENGTH_LIMIT ]
2022-05-04 23:09:46 +00:00
v . css = css
g . db . add ( v )
2023-01-27 10:02:11 +00:00
return render_template ( " settings/css.html " , v = v , msg = " Custom CSS successfully updated! " , profilecss = v . profilecss )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/profilecss " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_profilecss ( v ) :
2022-12-10 18:30:15 +00:00
profilecss = request . values . get ( " profilecss " , v . profilecss ) . strip ( ) . replace ( ' \\ ' , ' ' ) . strip ( ) [ : CSS_LENGTH_LIMIT ]
2022-08-05 17:09:41 +00:00
valid , error = validate_css ( profilecss )
if not valid :
2023-01-27 10:02:11 +00:00
return render_template ( " settings/css.html " , error = error , v = v , profilecss = profilecss )
2022-05-04 23:09:46 +00:00
v . profilecss = profilecss
g . db . add ( v )
2023-01-27 10:04:30 +00:00
return redirect ( " /settings/css?msg=Profile CSS successfully updated! " )
2022-11-06 07:02:15 +00:00
@app.get ( " /settings/security " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-11-06 07:02:15 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings_security ( v : User ) :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/security.html " ,
2022-11-06 07:02:15 +00:00
v = v ,
mfa_secret = pyotp . random_base32 ( ) if not v . mfa_secret else None ,
2023-01-27 11:33:03 +00:00
now = int ( time . time ( ) ) ,
error = get_error ( ) ,
msg = get_msg ( )
2022-11-06 07:02:15 +00:00
)
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/block " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( " 20/day " )
@limiter.limit ( " 20/day " , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_block_user ( v ) :
user = get_user ( request . values . get ( " username " ) , graceful = True )
2023-01-27 11:57:29 +00:00
if not user : abort ( 404 , " This user doesn ' t exist! " )
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
if user . unblockable :
2022-06-13 02:11:55 +00:00
if not v . shadowbanned :
send_notification ( user . id , f " @ { v . username } has tried to block you and failed because of your unblockable status! " )
2022-11-12 10:11:46 +00:00
abort ( 403 , f " @ { user . username } is unblockable! " )
2022-05-04 23:09:46 +00:00
2022-10-11 14:51:14 +00:00
if user . id == v . id : abort ( 400 , " You can ' t block yourself " )
2022-11-12 10:11:46 +00:00
if user . id == AUTOJANNY_ID : abort ( 403 , f " You can ' t block @ { user . username } " )
2022-10-11 14:51:14 +00:00
if v . has_blocked ( user ) : abort ( 409 , f " You have already blocked @ { user . username } " )
2022-05-04 23:09:46 +00:00
2022-11-06 07:02:15 +00:00
new_block = UserBlock ( user_id = v . id , target_id = user . id )
2022-05-04 23:09:46 +00:00
g . db . add ( new_block )
2022-07-20 02:29:45 +00:00
if user . admin_level > = PERMS [ ' USER_BLOCKS_VISIBLE ' ] :
send_notification ( user . id , f " @ { v . username } has blocked you! " )
2022-05-04 23:09:46 +00:00
cache . delete_memoized ( frontlist )
2023-01-27 11:57:29 +00:00
return { " message " : f " @ { user . username } blocked! " }
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/unblock " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_unblock_user ( v ) :
user = get_user ( request . values . get ( " username " ) )
2022-07-03 17:55:25 +00:00
x = v . has_blocked ( user )
2022-10-11 14:51:14 +00:00
if not x : abort ( 409 , " You can ' t unblock someone you haven ' t blocked " )
2022-05-04 23:09:46 +00:00
g . db . delete ( x )
2022-07-20 02:29:45 +00:00
if not v . shadowbanned and user . admin_level > = PERMS [ ' USER_BLOCKS_VISIBLE ' ] :
2022-06-13 02:11:55 +00:00
send_notification ( user . id , f " @ { v . username } has unblocked you! " )
2022-05-04 23:09:46 +00:00
cache . delete_memoized ( frontlist )
2022-12-04 15:40:32 +00:00
return { " message " : f " @ { user . username } unblocked successfully! " }
2022-05-04 23:09:46 +00:00
@app.get ( " /settings/apps " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings_apps ( v : User ) :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/apps.html " , v = v )
2022-05-04 23:09:46 +00:00
2022-11-06 07:02:15 +00:00
@app.get ( " /settings/advanced " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def settings_advanced_get ( v : User ) :
2023-01-27 11:20:15 +00:00
return render_template ( " settings/advanced.html " , v = v , msg = get_msg ( ) , error = get_error ( ) )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/name_change " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@is_not_permabanned
def settings_name_change ( v ) :
new_name = request . values . get ( " name " ) . strip ( )
if new_name == v . username :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " You didn ' t change anything " )
2022-05-04 23:09:46 +00:00
if not valid_username_regex . fullmatch ( new_name ) :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = " This isn ' t a valid username. " )
2022-05-04 23:09:46 +00:00
search_name = new_name . replace ( ' \\ ' , ' ' ) . replace ( ' _ ' , ' \ _ ' ) . replace ( ' % ' , ' ' )
2022-09-04 23:15:37 +00:00
x = g . db . query ( User ) . filter (
2022-05-04 23:09:46 +00:00
or_ (
User . username . ilike ( search_name ) ,
User . original_username . ilike ( search_name )
)
) . one_or_none ( )
if x and x . id != v . id :
2022-11-09 06:11:46 +00:00
return render_template ( " settings/personal.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
error = f " Username ` { new_name } ` is already in use. " )
2022-05-04 23:09:46 +00:00
2022-06-24 13:19:53 +00:00
v = get_account ( v . id )
2022-05-04 23:09:46 +00:00
v . username = new_name
v . name_changed_utc = int ( time . time ( ) )
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Name successfully changed! " )
2022-05-24 23:26:50 +00:00
@app.post ( " /settings/song_change_mp3 " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2022-11-14 15:11:05 +00:00
@feature_required ( ' USERS_PROFILE_SONG ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( " 10/day " )
@limiter.limit ( " 10/day " , key_func = get_ID )
2022-05-24 23:26:50 +00:00
@auth_required
def settings_song_change_mp3 ( v ) :
file = request . files [ ' file ' ]
if file . content_type != ' audio/mpeg ' :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Not a valid MP3 file! " )
2022-05-24 23:26:50 +00:00
2022-07-10 15:43:27 +00:00
song = str ( time . time ( ) ) . replace ( ' . ' , ' ' )
name = f ' /songs/ { song } .mp3 '
2022-05-24 23:26:50 +00:00
file . save ( name )
size = os . stat ( name ) . st_size
if size > 8 * 1024 * 1024 :
os . remove ( name )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=MP3 file must be smaller than 8MB " )
2022-05-24 23:26:50 +00:00
2022-07-10 15:43:27 +00:00
if path . isfile ( f " /songs/ { v . song } .mp3 " ) and g . db . query ( User ) . filter_by ( song = v . song ) . count ( ) == 1 :
os . remove ( f " /songs/ { v . song } .mp3 " )
v . song = song
2022-05-24 23:26:50 +00:00
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Profile Anthem successfully updated! " )
2022-05-24 23:26:50 +00:00
2023-02-10 14:29:09 +00:00
def _change_song_youtube ( vid , id ) :
db = db_session ( )
v = db . get ( User , vid )
if v . song and path . isfile ( f " /songs/ { v . song } .mp3 " ) and db . query ( User ) . filter_by ( song = v . song ) . count ( ) == 1 :
os . remove ( f " /songs/ { v . song } .mp3 " )
ydl_opts = {
2023-02-25 17:45:13 +00:00
' cookiefile ' : ' /cookies ' ,
2023-02-17 16:42:55 +00:00
' outtmpl ' : ' /temp_songs/ %(id)s . %(ext)s ' ,
2023-02-10 14:29:09 +00:00
' format ' : ' bestaudio/best ' ,
' postprocessors ' : [ {
' key ' : ' FFmpegExtractAudio ' ,
' preferredcodec ' : ' mp3 ' ,
' preferredquality ' : ' 192 ' ,
} ] ,
}
with youtube_dl . YoutubeDL ( ydl_opts ) as ydl :
try : ydl . download ( [ f " https://youtube.com/watch?v= { id } " ] )
except Exception as e :
print ( e , flush = True )
2023-02-17 14:49:23 +00:00
db . rollback ( )
2023-02-10 14:29:09 +00:00
db . close ( )
return
2023-02-17 16:42:55 +00:00
os . rename ( f " /temp_songs/ { id } .mp3 " , f " /songs/ { id } .mp3 " )
2023-02-10 14:29:09 +00:00
v . song = id
db . add ( v )
db . commit ( )
db . close ( )
stdout . flush ( )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/song_change " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2022-11-14 15:11:05 +00:00
@feature_required ( ' USERS_PROFILE_SONG ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( " 10/day " )
@limiter.limit ( " 10/day " , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_song_change ( v ) :
song = request . values . get ( " song " ) . strip ( )
if song == " " and v . song :
2022-05-09 11:21:49 +00:00
if path . isfile ( f " /songs/ { v . song } .mp3 " ) and g . db . query ( User ) . filter_by ( song = v . song ) . count ( ) == 1 :
2022-05-04 23:09:46 +00:00
os . remove ( f " /songs/ { v . song } .mp3 " )
v . song = None
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Profile Anthem successfully removed! " )
2022-05-04 23:09:46 +00:00
song = song . replace ( " https://music.youtube.com " , " https://youtube.com " )
if song . startswith ( ( " https://www.youtube.com/watch?v= " , " https://youtube.com/watch?v= " , " https://m.youtube.com/watch?v= " ) ) :
id = song . split ( " v= " ) [ 1 ]
elif song . startswith ( " https://youtu.be/ " ) :
id = song . split ( " https://youtu.be/ " ) [ 1 ]
else :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Not a YouTube link! " ) , 400
2022-05-04 23:09:46 +00:00
if " ? " in id : id = id . split ( " ? " ) [ 0 ]
if " & " in id : id = id . split ( " & " ) [ 0 ]
2022-11-22 12:13:44 +00:00
if not yt_id_regex . fullmatch ( id ) :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Not a YouTube link! " ) , 400
2023-01-01 11:36:20 +00:00
if path . isfile ( f ' /songs/ { id } .mp3 ' ) :
2022-05-04 23:09:46 +00:00
v . song = id
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Profile Anthem successfully updated! " )
2023-01-01 11:36:20 +00:00
2023-02-17 15:20:51 +00:00
if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE :
req = requests . get ( f " https://www.googleapis.com/youtube/v3/videos?id= { id } &key= { YOUTUBE_KEY } &part=contentDetails " , timeout = 5 ) . json ( )
duration = req [ ' items ' ] [ 0 ] [ ' contentDetails ' ] [ ' duration ' ]
if duration == ' P0D ' :
return redirect ( " /settings/personal?error=Can ' t use a live youtube video! " ) , 400
2022-05-04 23:09:46 +00:00
2023-02-17 15:20:51 +00:00
if " H " in duration :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Duration of the video must not exceed 15 minutes! " ) , 400
2022-05-04 23:09:46 +00:00
2023-02-17 15:20:51 +00:00
if " M " in duration :
duration = int ( duration . split ( " PT " ) [ 1 ] . split ( " M " ) [ 0 ] )
if duration > 15 :
return redirect ( " /settings/personal?error=Duration of the video must not exceed 15 minutes! " ) , 400
2023-02-10 14:29:09 +00:00
gevent . spawn ( _change_song_youtube , v . id , id )
2022-05-04 23:09:46 +00:00
2023-02-10 14:29:09 +00:00
return redirect ( " /settings/personal?msg=Profile Anthem successfully updated. Wait 5 minutes for the change to take effect. " )
2022-05-04 23:09:46 +00:00
@app.post ( " /settings/title_change " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def settings_title_change ( v ) :
if v . flairchanged : abort ( 403 )
2023-01-01 11:36:20 +00:00
2022-11-07 00:40:51 +00:00
customtitleplain = sanitize_settings_text ( request . values . get ( " title " ) , 100 )
2022-12-28 09:28:00 +00:00
if len ( customtitleplain ) > 100 :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Flair too long! " )
2022-12-28 09:28:00 +00:00
2022-08-26 22:01:36 +00:00
if customtitleplain == v . customtitleplain :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=You didn ' t change anything! " )
2022-05-04 23:09:46 +00:00
2022-11-02 03:22:16 +00:00
customtitle = filter_emojis_only ( customtitleplain )
customtitle = censor_slurs ( customtitle , None )
2022-05-04 23:09:46 +00:00
2022-08-26 22:01:36 +00:00
if len ( customtitle ) > 1000 :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Flair too long! " )
2022-07-12 20:09:59 +00:00
2022-08-26 22:01:36 +00:00
v . customtitleplain = customtitleplain
v . customtitle = customtitle
g . db . add ( v )
2022-05-04 23:09:46 +00:00
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Flair successfully updated! " )
2022-05-04 23:09:46 +00:00
2022-07-11 16:46:08 +00:00
@app.post ( " /settings/pronouns_change " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2022-11-13 12:24:58 +00:00
@feature_required ( ' PRONOUNS ' )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-07-11 16:46:08 +00:00
@auth_required
def settings_pronouns_change ( v ) :
2022-11-07 00:40:51 +00:00
pronouns = sanitize_settings_text ( request . values . get ( " pronouns " ) )
2022-07-11 16:46:08 +00:00
2022-07-23 06:48:32 +00:00
if len ( pronouns ) > 11 :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=Your pronouns exceed the character limit (11 characters) " )
2022-07-23 06:48:32 +00:00
2022-07-11 16:46:08 +00:00
if pronouns == v . pronouns :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=You didn ' t change anything! " )
2022-07-11 16:46:08 +00:00
if not pronouns_regex . fullmatch ( pronouns ) :
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?error=The pronouns you entered don ' t match the required format! " )
2022-07-11 16:46:08 +00:00
2022-08-16 16:16:04 +00:00
bare_pronouns = pronouns . lower ( ) . replace ( ' / ' , ' ' )
if ' nig ' in bare_pronouns : pronouns = ' BI/POC '
elif ' fag ' in bare_pronouns : pronouns = ' cute/twink '
2022-07-11 16:46:08 +00:00
v . pronouns = pronouns
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Pronouns successfully updated! " )
2022-07-11 16:46:08 +00:00
2022-05-10 07:20:49 +00:00
@app.post ( " /settings/checkmark_text " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-10 07:20:49 +00:00
@auth_required
def settings_checkmark_text ( v ) :
if not v . verified : abort ( 403 )
2022-11-08 08:03:57 +00:00
new_name = sanitize_settings_text ( request . values . get ( " checkmark-text " ) , 100 )
2022-05-10 07:20:49 +00:00
if not new_name : abort ( 400 )
2023-01-27 11:48:48 +00:00
if new_name == v . verified : return redirect ( " /settings/personal?error=You didn ' t change anything! " )
2022-05-10 07:20:49 +00:00
v . verified = new_name
g . db . add ( v )
2023-01-27 11:48:48 +00:00
return redirect ( " /settings/personal?msg=Checkmark Text successfully updated! " )
2023-01-01 05:33:09 +00:00
if IS_FISTMAS ( ) :
@app.post ( " /events/fistmas2022/darkmode " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2023-01-01 05:33:09 +00:00
@auth_required
def event_darkmode ( v ) :
v . event_darkmode = not v . event_darkmode
g . db . add ( v )
return { " message " : " Dark mode toggled successfully! " }