2022-11-15 09:19:08 +00:00
import os
2022-05-04 23:09:46 +00:00
import time
2022-11-26 06:11:00 +00:00
import html
2022-11-15 09:19:08 +00:00
from io import BytesIO
from os import path
from shutil import copyfile
from sys import stdout
from urllib . parse import ParseResult , quote , unquote , urlparse , urlunparse
2022-05-04 23:09:46 +00:00
import gevent
import requests
2022-11-15 09:19:08 +00:00
from PIL import Image
from files . __main__ import app , cache , limiter
from files . classes import *
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-11-15 09:19:08 +00:00
from files . helpers . get import *
2022-06-24 14:30:59 +00:00
from files . helpers . regex import *
2022-11-15 09:19:08 +00:00
from files . helpers . sanitize import *
from files . helpers . settings import get_setting
2022-05-04 23:09:46 +00:00
from files . helpers . slots import *
2022-07-09 10:32:49 +00:00
from files . helpers . sorting_and_time import *
2022-11-15 09:19:08 +00:00
from files . routes . routehelpers import execute_shadowban_viewers_and_voters
from files . routes . wrappers import *
2022-07-13 21:03:11 +00:00
from . front import frontlist
2022-11-15 09:19:08 +00:00
from . users import userpagelisting
2022-05-04 23:09:46 +00:00
2022-11-15 09:19:08 +00:00
from files . __main__ import app , limiter
2022-05-04 23:09:46 +00:00
titleheaders = { " User-Agent " : " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 " }
2022-12-29 10:39:10 +00:00
@app.post ( " /publish/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def publish ( pid , v ) :
post = get_post ( pid )
if not post . private : return { " message " : " Post published! " }
if post . author_id != v . id : abort ( 403 )
post . private = False
post . created_utc = int ( time . time ( ) )
g . db . add ( post )
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
if not post . ghost :
notify_users = NOTIFY_USERS ( f ' { post . title } { post . body } ' , v )
if notify_users :
2023-02-24 02:28:10 +00:00
cid , text = notif_comment2 ( post )
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
cache . delete_memoized ( frontlist )
2022-11-15 09:19:08 +00:00
cache . delete_memoized ( userpagelisting )
2022-05-04 23:09:46 +00:00
2022-07-08 11:44:17 +00:00
execute_snappy ( post , v )
2022-07-10 14:21:16 +00:00
if is_site_url ( request . referrer ) :
2022-06-27 02:34:15 +00:00
return redirect ( request . referrer )
2022-05-04 23:09:46 +00:00
return redirect ( post . permalink )
@app.get ( " /submit " )
@app.get ( " /h/<sub>/submit " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
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 submit_get ( v : User , sub = None ) :
2022-10-05 10:30:44 +00:00
sub = get_sub_by_name ( sub , graceful = True )
2022-05-04 23:09:46 +00:00
if request . path . startswith ( ' /h/ ' ) and not sub : abort ( 404 )
SUBS = [ x [ 0 ] for x in g . db . query ( Sub . name ) . order_by ( Sub . name ) . all ( ) ]
return render_template ( " submit.html " , SUBS = SUBS , v = v , sub = sub )
2022-12-29 10:39:10 +00:00
@app.get ( " /post/<int:pid> " )
@app.get ( " /post/<int:pid>/<anything> " )
@app.get ( " /h/<sub>/post/<int:pid> " )
@app.get ( " /h/<sub>/post/<int:pid>/<anything> " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2022-08-05 20:40:48 +00:00
@auth_desired_with_logingate
2022-05-04 23:09:46 +00:00
def post_id ( pid , anything = None , v = None , sub = None ) :
2022-07-08 18:28:56 +00:00
post = get_post ( pid , v = v )
2022-11-17 21:02:08 +00:00
if not User . can_see ( v , post ) : abort ( 403 )
2022-08-19 22:36:28 +00:00
2022-05-04 23:09:46 +00:00
if post . over_18 and not ( v and v . over_18 ) and session . get ( ' over_18 ' , 0 ) < int ( time . time ( ) ) :
2022-12-26 02:37:30 +00:00
if g . is_api_or_xhr : abort ( 451 , " Must be 18+ to view " )
2022-05-04 23:09:46 +00:00
return render_template ( " errors/nsfw.html " , v = v )
2022-12-01 22:04:10 +00:00
if post . new : defaultsortingcomments = ' new '
2022-05-04 23:09:46 +00:00
elif v : defaultsortingcomments = v . defaultsortingcomments
2022-10-11 16:41:09 +00:00
else : defaultsortingcomments = " hot "
2022-05-04 23:09:46 +00:00
sort = request . values . get ( " sort " , defaultsortingcomments )
if v :
2022-11-15 09:19:08 +00:00
execute_shadowban_viewers_and_voters ( v , post )
2023-02-10 13:48:31 +00:00
# shadowban check is done in sort_objects
# output is needed: see comments.py
comments , output = get_comments_v_properties ( v , True , None , Comment . parent_submission == post . id , Comment . level < 10 )
pinned = [ c [ 0 ] for c in comments . filter ( Comment . stickied != None ) . order_by ( Comment . created_utc . desc ( ) ) . all ( ) ]
2022-06-26 01:12:31 +00:00
comments = comments . filter ( Comment . level == 1 , Comment . stickied == None )
2023-02-10 13:48:31 +00:00
comments = sort_objects ( sort , comments , Comment ,
include_shadowbanned = ( v and v . can_see_shadowbanned ) )
comments = [ c [ 0 ] for c in comments . all ( ) ]
2022-05-04 23:09:46 +00:00
else :
2022-11-29 00:04:06 +00:00
pinned = g . db . query ( Comment ) . filter ( Comment . parent_submission == post . id , Comment . stickied != None ) . order_by ( Comment . created_utc . desc ( ) ) . all ( )
2022-05-04 23:09:46 +00:00
2022-10-13 10:47:55 +00:00
comments = g . db . query ( Comment ) . filter (
Comment . parent_submission == post . id ,
Comment . level == 1 ,
Comment . stickied == None
)
2022-05-04 23:09:46 +00:00
2022-10-12 08:05:26 +00:00
comments = sort_objects ( sort , comments , Comment ,
2022-10-13 04:10:34 +00:00
include_shadowbanned = False )
2022-05-04 23:09:46 +00:00
2022-09-04 23:21:48 +00:00
comments = comments . all ( )
2022-05-04 23:09:46 +00:00
2022-07-04 03:37:48 +00:00
offset = 0
ids = set ( )
2022-10-25 18:20:43 +00:00
threshold = 100
2022-06-27 19:08:05 +00:00
2023-02-08 02:32:14 +00:00
if post . comment_count > threshold + 25 and not ( v and v . client ) :
2022-05-04 23:09:46 +00:00
comments2 = [ ]
count = 0
2022-11-15 10:43:19 +00:00
if post . created_utc > 1638672040 : # TODO: migrate old comments to use top_comment_id
2022-07-04 03:37:48 +00:00
for comment in comments :
comments2 . append ( comment )
ids . add ( comment . id )
count + = g . db . query ( Comment ) . filter_by ( parent_submission = post . id , top_comment_id = comment . id ) . count ( ) + 1
if count > threshold : break
else :
for comment in comments :
comments2 . append ( comment )
ids . add ( comment . id )
count + = g . db . query ( Comment ) . filter_by ( parent_submission = post . id , parent_comment_id = comment . id ) . count ( ) + 1
if count > 20 : break
if len ( comments ) == len ( comments2 ) : offset = 0
else : offset = 1
2022-05-04 23:09:46 +00:00
comments = comments2
2022-11-29 00:04:06 +00:00
pinned2 = { }
2022-05-04 23:09:46 +00:00
for pin in pinned :
2022-05-26 23:08:23 +00:00
if pin . stickied_utc and int ( time . time ( ) ) > pin . stickied_utc :
pin . stickied = None
pin . stickied_utc = None
2022-05-04 23:09:46 +00:00
g . db . add ( pin )
2022-09-02 00:46:17 +00:00
elif pin . level > 1 :
2022-11-29 00:04:06 +00:00
pinned2 [ pin . top_comment ( g . db ) ] = ' '
2022-11-18 21:11:03 +00:00
if pin . top_comment ( g . db ) in comments :
2022-11-15 10:29:47 +00:00
comments . remove ( pin . top_comment ( g . db ) )
2022-11-12 11:33:18 +00:00
else :
2022-11-29 00:04:06 +00:00
pinned2 [ pin ] = ' '
2022-05-04 23:09:46 +00:00
2022-11-29 00:04:06 +00:00
post . replies = list ( pinned2 . keys ( ) ) + comments
2022-05-04 23:09:46 +00:00
post . views + = 1
g . db . add ( post )
2022-08-08 22:21:59 +00:00
2022-10-15 09:11:36 +00:00
if v and v . client :
2022-11-15 09:19:08 +00:00
return post . json ( g . db )
2022-08-08 22:21:59 +00:00
template = " submission.html "
if ( post . is_banned or post . author . shadowbanned ) \
2022-10-06 00:57:08 +00:00
and not ( v and ( v . admin_level > = PERMS [ ' POST_COMMENT_MODERATION ' ] or post . author_id == v . id ) ) :
2022-08-08 22:21:59 +00:00
template = " submission_banned.html "
2023-02-10 13:51:42 +00:00
return render_template ( template , v = v , p = post , ids = list ( ids ) ,
2022-08-08 22:21:59 +00:00
sort = sort , render_replies = True , offset = offset , sub = post . subr ,
2022-11-30 18:26:07 +00:00
fart = get_setting ( ' fart_mode ' ) )
2022-07-04 03:37:48 +00:00
2022-12-29 14:20:27 +00:00
@app.get ( " /view_more/<int:pid>/<sort>/<offset> " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2022-08-05 20:40:48 +00:00
@auth_desired_with_logingate
2022-12-29 14:20:27 +00:00
def view_more ( v , pid , sort , offset ) :
2022-07-04 03:37:48 +00:00
post = get_post ( pid , v = v )
2022-10-16 09:51:42 +00:00
try :
offset = int ( offset )
except : abort ( 400 )
2022-07-04 03:37:48 +00:00
try : ids = set ( int ( x ) for x in request . values . get ( " ids " ) . split ( ' , ' ) )
except : abort ( 400 )
2023-01-01 11:36:20 +00:00
2022-07-04 03:37:48 +00:00
if v :
2022-10-29 00:13:37 +00:00
# shadowban check is done in sort_objects
# output is needed: see comments.py
comments , output = get_comments_v_properties ( v , True , None , Comment . parent_submission == pid , Comment . stickied == None , Comment . id . notin_ ( ids ) , Comment . level < 10 )
2022-07-04 03:37:48 +00:00
comments = comments . filter ( Comment . level == 1 )
2022-10-12 08:05:26 +00:00
comments = sort_objects ( sort , comments , Comment ,
2022-10-13 04:10:34 +00:00
include_shadowbanned = ( v and v . can_see_shadowbanned ) )
2022-07-04 03:37:48 +00:00
2022-09-04 23:21:48 +00:00
comments = [ c [ 0 ] for c in comments . all ( ) ]
2022-07-04 03:37:48 +00:00
else :
2022-10-13 10:47:55 +00:00
comments = g . db . query ( Comment ) . filter (
Comment . parent_submission == pid ,
Comment . level == 1 ,
Comment . stickied == None ,
Comment . id . notin_ ( ids )
)
2022-07-04 03:37:48 +00:00
2022-10-12 08:05:26 +00:00
comments = sort_objects ( sort , comments , Comment ,
2022-10-13 04:10:34 +00:00
include_shadowbanned = False )
2023-01-01 11:36:20 +00:00
2022-09-04 23:21:48 +00:00
comments = comments . offset ( offset ) . all ( )
2022-07-04 03:37:48 +00:00
comments2 = [ ]
count = 0
2022-11-15 10:43:19 +00:00
if post . created_utc > 1638672040 : # TODO: migrate old comments to use top_comment_id
2022-07-04 03:37:48 +00:00
for comment in comments :
comments2 . append ( comment )
ids . add ( comment . id )
count + = g . db . query ( Comment ) . filter_by ( parent_submission = post . id , top_comment_id = comment . id ) . count ( ) + 1
if count > 100 : break
else :
for comment in comments :
comments2 . append ( comment )
ids . add ( comment . id )
count + = g . db . query ( Comment ) . filter_by ( parent_submission = post . id , parent_comment_id = comment . id ) . count ( ) + 1
if count > 20 : break
2023-01-01 11:36:20 +00:00
2022-07-04 03:37:48 +00:00
if len ( comments ) == len ( comments2 ) : offset = 0
else : offset + = 1
comments = comments2
2022-08-30 05:26:13 +00:00
return render_template ( " comments.html " , v = v , comments = comments , p = post , ids = list ( ids ) , render_replies = True , pid = pid , sort = sort , offset = offset )
2022-05-04 23:09:46 +00:00
2022-12-29 14:20:27 +00:00
@app.get ( " /more_comments/<int:cid> " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2022-08-05 20:40:48 +00:00
@auth_desired_with_logingate
2022-12-29 14:20:27 +00:00
def more_comments ( v , cid ) :
2022-05-04 23:09:46 +00:00
try : cid = int ( cid )
2022-10-09 08:25:21 +00:00
except : abort ( 404 )
2022-05-04 23:09:46 +00:00
tcid = g . db . query ( Comment . top_comment_id ) . filter_by ( id = cid ) . one_or_none ( ) [ 0 ]
if v :
2022-10-29 00:13:37 +00:00
# shadowban check is done in sort_objects i think
# output is needed: see comments.py
2022-12-14 18:24:14 +00:00
comments , output = get_comments_v_properties ( v , True , None , Comment . top_comment_id == tcid , Comment . level > 9 )
comments = comments . filter ( Comment . parent_comment_id == cid )
comments = [ c [ 0 ] for c in comments . all ( ) ]
2022-05-04 23:09:46 +00:00
else :
2022-06-24 13:19:53 +00:00
c = get_comment ( cid )
2022-11-15 09:19:08 +00:00
comments = c . replies ( sort = request . values . get ( ' sort ' ) , v = v , db = g . db )
2022-05-04 23:09:46 +00:00
if comments : p = comments [ 0 ] . post
else : p = None
2023-01-01 11:36:20 +00:00
2022-08-30 05:26:13 +00:00
return render_template ( " comments.html " , v = v , comments = comments , p = p , render_replies = True )
2022-05-04 23:09:46 +00:00
2022-12-29 10:39:10 +00:00
@app.post ( " /edit_post/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( " 10/minute;100/hour;200/day " )
@limiter.limit ( " 10/minute;100/hour;200/day " , key_func = get_ID )
2022-11-14 00:00:55 +00:00
@is_not_permabanned
2022-05-04 23:09:46 +00:00
def edit_post ( pid , v ) :
2022-07-08 18:28:56 +00:00
p = get_post ( pid )
2022-12-01 22:04:10 +00:00
if not v . can_edit ( p ) : abort ( 403 )
2022-05-04 23:09:46 +00:00
2022-10-11 03:40:08 +00:00
# Disable edits on things older than 1wk unless it's a draft or editor is a jannie
if ( time . time ( ) - p . created_utc > 7 * 24 * 60 * 60 and not p . private
and not v . admin_level > = PERMS [ ' POST_EDITING ' ] ) :
2022-10-11 13:01:39 +00:00
abort ( 403 , " You can ' t edit posts older than 1 week! " )
2022-10-11 03:40:08 +00:00
2022-10-05 08:04:32 +00:00
title = sanitize_raw_title ( request . values . get ( " title " , " " ) )
2022-10-09 12:54:46 +00:00
body = sanitize_raw_body ( request . values . get ( " body " , " " ) , True )
2022-05-04 23:09:46 +00:00
if v . id == p . author_id :
if v . longpost and ( len ( body ) < 280 or ' []( ' in body or body . startswith ( ' []( ' ) ) :
2022-10-11 13:01:39 +00:00
abort ( 403 , " You have to type more than 280 characters! " )
2022-05-04 23:09:46 +00:00
elif v . bird and len ( body ) > 140 :
2022-10-11 13:01:39 +00:00
abort ( 403 , " You have to type less than 140 characters! " )
2022-05-04 23:09:46 +00:00
2022-10-05 08:04:32 +00:00
if not title :
2023-01-27 11:57:29 +00:00
abort ( 400 , " Please enter a better title! " )
2022-05-04 23:09:46 +00:00
if title != p . title :
2022-09-05 20:05:04 +00:00
torture = ( v . agendaposter and not v . marseyawarded and p . sub != ' chudrama ' and v . id == p . author_id )
2022-05-04 23:09:46 +00:00
2022-09-16 16:30:34 +00:00
title_html = filter_emojis_only ( title , golden = False , torture = torture )
2022-05-04 23:09:46 +00:00
if v . id == p . author_id and v . marseyawarded and not marseyaward_title_regex . fullmatch ( title_html ) :
2022-10-11 13:01:39 +00:00
abort ( 403 , " You can only type marseys! " )
2022-05-04 23:09:46 +00:00
2022-12-03 22:01:08 +00:00
if ' megathread ' in title . lower ( ) and ' megathread ' not in p . title . lower ( ) :
p . new = True
2022-10-05 08:04:32 +00:00
p . title = title
2022-05-04 23:09:46 +00:00
p . title_html = title_html
2022-11-15 09:19:08 +00:00
body + = process_files ( request . files , v )
2022-10-05 09:54:44 +00:00
body = body . strip ( ) [ : POST_BODY_LENGTH_LIMIT ] # process_files() may be adding stuff to the body
2022-05-22 10:26:59 +00:00
2022-05-04 23:09:46 +00:00
if body != p . body :
2022-12-04 21:46:27 +00:00
body , bets , options , choices = sanitize_poll_options ( v , body , False )
process_poll_options ( p , SubmissionOption , bets , 2 , " Bet " , g . db )
process_poll_options ( p , SubmissionOption , options , 0 , " Poll " , g . db )
process_poll_options ( p , SubmissionOption , choices , 1 , " Poll " , g . db )
2022-05-04 23:09:46 +00:00
2022-09-05 20:05:04 +00:00
torture = ( v . agendaposter and not v . marseyawarded and p . sub != ' chudrama ' and v . id == p . author_id )
2022-09-16 16:30:34 +00:00
body_html = sanitize ( body , golden = False , limit_pings = 100 , showmore = False , torture = torture )
2022-05-04 23:09:46 +00:00
if v . id == p . author_id and v . marseyawarded and marseyaward_body_regex . search ( body_html ) :
2022-10-11 13:01:39 +00:00
abort ( 403 , " You can only type marseys! " )
2022-05-04 23:09:46 +00:00
p . body = body
2022-11-30 17:37:35 +00:00
execute_under_siege ( v , p , p . body , ' submission ' )
for text in [ p . body , p . title , p . url ] :
2023-02-07 03:31:49 +00:00
if execute_blackjack ( v , p , text , ' submission ' ) : break
2022-05-04 23:09:46 +00:00
2023-01-01 11:36:20 +00:00
if len ( body_html ) > POST_BODY_HTML_LENGTH_LIMIT :
2023-02-24 02:54:31 +00:00
abort ( 400 , " Submission body_html too long! " )
2022-05-04 23:09:46 +00:00
p . body_html = body_html
2022-08-19 22:20:25 +00:00
if v . id == p . author_id and v . agendaposter and not v . marseyawarded and AGENDAPOSTER_PHRASE not in f ' { p . body } { p . title } ' . lower ( ) and p . sub != ' chudrama ' :
2022-10-11 13:01:39 +00:00
abort ( 403 , f ' You have to include " { AGENDAPOSTER_PHRASE } " in your post! ' )
2022-05-04 23:09:46 +00:00
if not p . private and not p . ghost :
notify_users = NOTIFY_USERS ( f ' { p . title } { p . body } ' , v )
if notify_users :
2023-02-24 02:28:10 +00:00
cid , text = notif_comment2 ( p )
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
if v . id == p . author_id :
if int ( time . time ( ) ) - p . created_utc > 60 * 3 : p . edited_utc = int ( time . time ( ) )
g . db . add ( p )
else :
ma = ModAction (
kind = " edit_post " ,
user_id = v . id ,
target_submission_id = p . id
)
g . db . add ( ma )
return redirect ( p . permalink )
2022-11-15 10:57:49 +00:00
def thumbnail_thread ( pid : int , vid : int ) :
db = db_session ( )
2022-05-04 23:09:46 +00:00
def expand_url ( post_url , fragment_url ) :
if fragment_url . startswith ( " https:// " ) :
return fragment_url
elif fragment_url . startswith ( " https:// " ) :
return f " https:// { fragment_url . split ( ' https:// ' ) [ 1 ] } "
elif fragment_url . startswith ( ' // ' ) :
return f " https: { fragment_url } "
2022-05-25 19:45:34 +00:00
elif fragment_url . startswith ( ' / ' ) and ' \\ ' not in fragment_url :
2022-05-04 23:09:46 +00:00
parsed_url = urlparse ( post_url )
return f " https:// { parsed_url . netloc } { fragment_url } "
else :
2022-07-08 18:06:54 +00:00
return f " { post_url } / { fragment_url } "
2022-05-04 23:09:46 +00:00
2022-08-25 15:26:27 +00:00
post = db . get ( Submission , pid )
2023-01-01 11:36:20 +00:00
2022-08-25 15:26:27 +00:00
if not post or not post . url :
time . sleep ( 5 )
post = db . get ( Submission , pid )
if not post or not post . url : return
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
fetch_url = post . url
2022-05-25 19:45:34 +00:00
if fetch_url . startswith ( ' / ' ) and ' \\ ' not in fetch_url :
2022-10-27 17:53:08 +00:00
fetch_url = f " { SITE_FULL } { fetch_url } "
2022-05-04 23:09:46 +00:00
headers = { " User-Agent " : " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 " }
2022-08-25 15:26:27 +00:00
try :
x = requests . get ( fetch_url , headers = headers , timeout = 5 , proxies = proxies )
except :
db . close ( )
return
if x . status_code != 200 :
db . close ( )
return
2022-05-04 23:09:46 +00:00
if x . headers . get ( " Content-Type " , " " ) . startswith ( " text/html " ) :
soup = BeautifulSoup ( x . content , ' lxml ' )
thumb_candidate_urls = [ ]
meta_tags = [
" drama:thumbnail " ,
" twitter:image " ,
" og:image " ,
" thumbnail "
]
for tag_name in meta_tags :
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
tag = soup . find (
2023-01-01 11:36:20 +00:00
' meta ' ,
2022-05-04 23:09:46 +00:00
attrs = {
2023-01-01 11:36:20 +00:00
" name " : tag_name ,
2022-05-04 23:09:46 +00:00
" content " : True
}
)
if not tag :
tag = soup . find (
' meta ' ,
attrs = {
' property ' : tag_name ,
' content ' : True
}
)
if tag :
thumb_candidate_urls . append ( expand_url ( post . url , tag [ ' content ' ] ) )
for tag in soup . find_all ( " img " , attrs = { ' src ' : True } ) :
thumb_candidate_urls . append ( expand_url ( post . url , tag [ ' src ' ] ) )
for url in thumb_candidate_urls :
try :
image_req = requests . get ( url , headers = headers , timeout = 5 , proxies = proxies )
except :
continue
if image_req . status_code > = 400 :
continue
if not image_req . headers . get ( " Content-Type " , " " ) . startswith ( " image/ " ) :
continue
if image_req . headers . get ( " Content-Type " , " " ) . startswith ( " image/svg " ) :
continue
2022-10-25 15:39:57 +00:00
with Image . open ( BytesIO ( image_req . content ) ) as i :
if i . width < 30 or i . height < 30 :
continue
2022-05-04 23:09:46 +00:00
break
2022-08-25 15:26:27 +00:00
else :
db . close ( )
return
2022-05-04 23:09:46 +00:00
elif x . headers . get ( " Content-Type " , " " ) . startswith ( " image/ " ) :
image_req = x
2022-10-25 15:39:57 +00:00
with Image . open ( BytesIO ( x . content ) ) as i :
size = len ( i . fp . read ( ) )
2022-10-28 17:13:21 +00:00
if size > 8 * 1024 * 1024 :
db . close ( )
return
2022-05-04 23:09:46 +00:00
2022-08-25 15:26:27 +00:00
else :
db . close ( )
return
2022-05-04 23:09:46 +00:00
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
with open ( name , " wb " ) as file :
for chunk in image_req . iter_content ( 1024 ) :
file . write ( chunk )
2022-11-15 09:19:08 +00:00
v = db . get ( User , vid )
2022-12-26 02:16:46 +00:00
url = process_image ( name , v , resize = 99 , uploader_id = post . author_id , db = db )
2022-11-15 09:19:08 +00:00
if url :
post . thumburl = url
db . add ( post )
db . commit ( )
2022-08-25 15:26:27 +00:00
db . close ( )
2022-05-04 23:09:46 +00:00
stdout . flush ( )
@app.post ( " /is_repost " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2022-08-11 04:05:23 +00:00
def is_repost ( ) :
2022-10-14 10:26:48 +00:00
not_a_repost = { ' permalink ' : ' ' }
2022-08-04 04:07:17 +00:00
if not FEATURES [ ' REPOST_DETECTION ' ] :
2022-10-14 10:26:48 +00:00
return not_a_repost
2022-05-04 23:09:46 +00:00
url = request . values . get ( ' url ' )
2022-10-14 10:26:48 +00:00
if not url or len ( url ) < MIN_REPOST_CHECK_URL_LENGTH : abort ( 400 )
2022-05-04 23:09:46 +00:00
2022-05-25 08:43:16 +00:00
url = normalize_url ( url )
2022-05-04 23:09:46 +00:00
parsed_url = urlparse ( url )
domain = parsed_url . netloc
2022-12-23 22:22:41 +00:00
if domain in { ' old.reddit.com ' , ' twitter.com ' , ' instagram.com ' , ' tiktok.com ' } and ' /search ' not in url :
2022-05-04 23:09:46 +00:00
new_url = ParseResult ( scheme = " https " ,
netloc = parsed_url . netloc ,
path = parsed_url . path ,
params = parsed_url . params ,
query = None ,
fragment = parsed_url . fragment )
else :
2022-09-01 20:46:57 +00:00
qd = parse_qs ( parsed_url . query , keep_blank_values = True )
2022-05-04 23:09:46 +00:00
filtered = { k : val for k , val in qd . items ( ) if not k . startswith ( ' utm_ ' ) and not k . startswith ( ' ref_ ' ) }
new_url = ParseResult ( scheme = " https " ,
netloc = parsed_url . netloc ,
path = parsed_url . path ,
params = parsed_url . params ,
query = urlencode ( filtered , doseq = True ) ,
fragment = parsed_url . fragment )
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
url = urlunparse ( new_url )
2022-09-05 01:44:38 +00:00
url = url . rstrip ( ' / ' )
2022-05-04 23:09:46 +00:00
search_url = url . replace ( ' % ' , ' ' ) . replace ( ' \\ ' , ' ' ) . replace ( ' _ ' , ' \ _ ' ) . strip ( )
repost = g . db . query ( Submission ) . filter (
Submission . url . ilike ( search_url ) ,
Submission . deleted_utc == 0 ,
Submission . is_banned == False
) . first ( )
if repost : return { ' permalink ' : repost . permalink }
2022-10-14 10:26:48 +00:00
else : return not_a_repost
2022-05-04 23:09:46 +00:00
@app.post ( " /submit " )
@app.post ( " /h/<sub>/submit " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-18 18:22:48 +00:00
@limiter.limit ( POST_RATELIMIT )
@limiter.limit ( POST_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
2022-11-26 21:00:03 +00:00
def submit_post ( v : User , sub = None ) :
2022-05-04 23:09:46 +00:00
url = request . values . get ( " url " , " " ) . strip ( )
2022-05-25 18:29:22 +00:00
if ' \\ ' in url : abort ( 400 )
2022-10-05 08:04:32 +00:00
title = sanitize_raw_title ( request . values . get ( " title " , " " ) )
2022-10-09 12:54:46 +00:00
body = sanitize_raw_body ( request . values . get ( " body " , " " ) , True )
2022-06-29 01:13:11 +00:00
2022-10-05 08:04:32 +00:00
if not title :
2023-02-17 23:25:01 +00:00
abort ( 400 , " Please enter a better title! " )
2022-12-05 01:21:47 +00:00
sub = request . values . get ( " sub " , " " ) . lower ( ) . replace ( ' /h/ ' , ' ' ) . strip ( )
2022-12-24 19:11:32 +00:00
if SITE == ' rdrama.net ' and v . id == 2167 :
sub = " chudrama "
2022-10-05 08:04:32 +00:00
torture = ( v . agendaposter and not v . marseyawarded and sub != ' chudrama ' )
title_html = filter_emojis_only ( title , graceful = True , count_marseys = True , torture = torture )
if v . marseyawarded and not marseyaward_title_regex . fullmatch ( title_html ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , " You can only type marseys! " )
2023-01-01 11:36:20 +00:00
if len ( title_html ) > POST_TITLE_HTML_LENGTH_LIMIT :
2023-02-17 23:25:01 +00:00
abort ( 400 , " Rendered title is too big! " )
2022-05-04 23:09:46 +00:00
2022-10-06 09:09:46 +00:00
if sub == ' changelog ' and not v . admin_level > = PERMS [ ' POST_TO_CHANGELOG ' ] :
2023-02-17 23:25:01 +00:00
abort ( 400 , " You don ' t have sufficient permissions to post in /h/changelog " )
2022-05-04 23:09:46 +00:00
2022-12-23 22:22:41 +00:00
if sub in { ' furry ' , ' vampire ' , ' racist ' , ' femboy ' } and not v . client and not v . house . lower ( ) . startswith ( sub ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , f " You need to be a member of House { sub . capitalize ( ) } to post in /h/ { sub } " )
2022-08-21 15:24:16 +00:00
2022-05-04 23:09:46 +00:00
if sub and sub != ' none ' :
sname = sub . strip ( ) . lower ( )
sub = g . db . query ( Sub . name ) . filter_by ( name = sname ) . one_or_none ( )
2023-02-17 23:25:01 +00:00
if not sub : abort ( 400 , f " /h/ { sname } not found! " )
2022-05-04 23:09:46 +00:00
sub = sub [ 0 ]
2023-02-17 23:25:01 +00:00
if v . exiled_from ( sub ) : abort ( 400 , f " You ' re exiled from /h/ { sub } " )
2022-05-04 23:09:46 +00:00
else : sub = None
2022-06-26 01:01:21 +00:00
if not sub and HOLE_REQUIRED :
2023-02-17 23:25:01 +00:00
abort ( 400 , f " You must choose a { HOLE_NAME } for your post! " )
2022-06-26 01:01:21 +00:00
2023-02-17 23:25:01 +00:00
if v . is_suspended : abort ( 400 , " You can ' t perform this action while banned! " )
2022-05-04 23:09:46 +00:00
if v . longpost and ( len ( body ) < 280 or ' []( ' in body or body . startswith ( ' []( ' ) ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , " You have to type more than 280 characters! " )
2022-05-04 23:09:46 +00:00
elif v . bird and len ( body ) > 140 :
2023-02-17 23:25:01 +00:00
abort ( 400 , " You have to type less than 140 characters! " )
2022-05-04 23:09:46 +00:00
embed = None
if url :
2022-05-25 08:43:16 +00:00
url = normalize_url ( url )
2022-05-04 23:09:46 +00:00
parsed_url = urlparse ( url )
domain = parsed_url . netloc
2022-12-23 22:22:41 +00:00
if domain in { ' old.reddit.com ' , ' twitter.com ' , ' instagram.com ' , ' tiktok.com ' } and ' /search ' not in url :
2022-07-01 18:29:12 +00:00
new_url = ParseResult ( scheme = " https " ,
netloc = parsed_url . netloc ,
path = parsed_url . path ,
params = parsed_url . params ,
query = None ,
fragment = parsed_url . fragment )
else :
2022-09-01 20:46:57 +00:00
qd = parse_qs ( parsed_url . query , keep_blank_values = True )
2022-07-01 18:29:12 +00:00
filtered = { k : val for k , val in qd . items ( ) if not k . startswith ( ' utm_ ' ) and not k . startswith ( ' ref_ ' ) }
new_url = ParseResult ( scheme = " https " ,
netloc = parsed_url . netloc ,
path = parsed_url . path ,
params = parsed_url . params ,
query = urlencode ( filtered , doseq = True ) ,
fragment = parsed_url . fragment )
2023-01-01 11:36:20 +00:00
2022-07-01 18:29:12 +00:00
url = urlunparse ( new_url )
2022-09-05 01:44:38 +00:00
url = url . rstrip ( ' / ' )
2022-07-01 18:29:12 +00:00
search_url = url . replace ( ' % ' , ' ' ) . replace ( ' \\ ' , ' ' ) . replace ( ' _ ' , ' \ _ ' ) . strip ( )
repost = g . db . query ( Submission ) . filter (
Submission . url . ilike ( search_url ) ,
Submission . deleted_utc == 0 ,
Submission . is_banned == False
) . first ( )
2022-10-20 22:51:29 +00:00
if repost and FEATURES [ ' REPOST_DETECTION ' ] and not v . admin_level > = PERMS [ ' POST_BYPASS_REPOST_CHECKING ' ] :
2022-08-04 04:07:17 +00:00
return redirect ( repost . permalink )
2022-05-04 23:09:46 +00:00
2022-10-28 18:12:37 +00:00
y = tldextract . extract ( url ) . registered_domain + parsed_url . path
2022-10-31 14:33:11 +00:00
y = y . lower ( )
2022-10-27 22:37:24 +00:00
banned_domains = g . db . query ( BannedDomain ) . all ( )
for x in banned_domains :
if y . startswith ( x . domain ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , f ' Remove the banned link " { x . domain } " and try again!<br>Reason for link ban: " { x . reason } " ' )
2022-10-27 22:37:24 +00:00
2022-10-27 22:42:32 +00:00
if " twitter.com " == domain :
2022-06-11 09:53:53 +00:00
try :
2022-09-26 04:01:25 +00:00
embed = requests . get ( " https://publish.twitter.com/oembed " , params = { " url " : url , " omit_script " : " t " } , timeout = 5 ) . json ( ) [ " html " ]
2022-10-29 21:46:30 +00:00
embed = embed . replace ( ' <a href ' , ' <a rel= " nofollow noopener " href ' )
2022-05-04 23:09:46 +00:00
except : pass
2023-01-23 02:06:56 +00:00
elif url . startswith ( ' https://youtube.com/watch? ' ) :
2023-01-25 11:17:12 +00:00
embed = handle_youtube_links ( url )
2022-07-11 11:55:15 +00:00
elif SITE in domain and " /post/ " in url and " context " not in url and url . count ( ' / ' ) < 6 :
2022-05-04 23:09:46 +00:00
id = url . split ( " /post/ " ) [ 1 ]
if " / " in id : id = id . split ( " / " ) [ 0 ]
embed = str ( int ( id ) )
2022-10-05 08:16:56 +00:00
if not url and not body and not request . files . get ( " file " ) and not request . files . get ( " file-url " ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , " Please enter a url or some text! " )
2022-05-04 23:09:46 +00:00
2023-01-01 11:36:20 +00:00
if not IS_LOCALHOST :
2022-11-06 23:48:37 +00:00
dup = g . db . query ( Submission ) . filter (
Submission . author_id == v . id ,
Submission . deleted_utc == 0 ,
Submission . title == title ,
Submission . url == url ,
Submission . body == body
) . one_or_none ( )
if dup : return redirect ( dup . permalink )
2022-05-04 23:09:46 +00:00
2022-10-12 05:11:20 +00:00
if not execute_antispam_submission_check ( title , v , url ) :
2022-05-04 23:09:46 +00:00
return redirect ( " /notifications " )
if len ( url ) > 2048 :
2023-02-17 23:25:01 +00:00
abort ( 400 , " There ' s a 2048 character limit for URLs! " )
2022-05-04 23:09:46 +00:00
2022-12-04 21:46:27 +00:00
body , bets , options , choices = sanitize_poll_options ( v , body , True )
2022-05-04 23:09:46 +00:00
2022-11-15 09:19:08 +00:00
body + = process_files ( request . files , v )
2022-10-05 09:14:12 +00:00
body = body . strip ( ) [ : POST_BODY_LENGTH_LIMIT ] # process_files() adds content to the body, so we need to re-strip
2022-05-22 10:26:59 +00:00
2022-09-05 20:05:04 +00:00
torture = ( v . agendaposter and not v . marseyawarded and sub != ' chudrama ' )
2022-09-16 16:30:34 +00:00
body_html = sanitize ( body , count_marseys = True , limit_pings = 100 , showmore = False , torture = torture )
2022-05-04 23:09:46 +00:00
if v . marseyawarded and marseyaward_body_regex . search ( body_html ) :
2023-02-17 23:25:01 +00:00
abort ( 400 , " You can only type marseys! " )
2022-05-04 23:09:46 +00:00
2022-12-28 09:50:48 +00:00
if len ( body_html ) > POST_BODY_HTML_LENGTH_LIMIT :
2023-02-24 02:54:31 +00:00
abort ( 400 , " Submission body_html too long! " )
2022-05-04 23:09:46 +00:00
2022-11-11 05:21:18 +00:00
flag_notify = ( request . values . get ( " notify " , " on " ) == " on " )
2022-12-03 22:01:08 +00:00
flag_new = request . values . get ( " new " , False , bool ) or ' megathread ' in title . lower ( )
2023-02-08 06:22:11 +00:00
flag_over_18 = FEATURES [ ' NSFW_MARKING ' ] and request . values . get ( " over_18 " , False , bool )
2022-11-11 05:21:18 +00:00
flag_private = request . values . get ( " private " , False , bool )
2022-11-14 17:17:29 +00:00
flag_ghost = request . values . get ( " ghost " , False , bool ) and v . can_post_in_ghost_threads
2022-05-04 23:09:46 +00:00
2022-11-11 05:21:18 +00:00
if embed and len ( embed ) > 1500 : embed = None
2022-07-09 07:41:05 +00:00
if embed : embed = embed . strip ( )
2022-10-18 11:09:53 +00:00
if url and url . startswith ( SITE_FULL ) :
url = url . split ( SITE_FULL ) [ 1 ]
2022-12-11 18:56:04 +00:00
elif url . startswith ( BAN_EVASION_FULL ) :
url = url . split ( BAN_EVASION_FULL , 1 ) [ 1 ]
2022-10-18 11:09:53 +00:00
2022-05-04 23:09:46 +00:00
post = Submission (
2022-11-11 05:21:18 +00:00
private = flag_private ,
notify = flag_notify ,
2022-05-04 23:09:46 +00:00
author_id = v . id ,
2022-11-11 05:21:18 +00:00
over_18 = flag_over_18 ,
new = flag_new ,
2022-05-04 23:09:46 +00:00
app_id = v . client . application . id if v . client else None ,
2022-10-15 14:11:14 +00:00
is_bot = ( v . client is not None ) ,
2022-05-04 23:09:46 +00:00
url = url ,
2022-10-05 08:16:56 +00:00
body = body ,
2022-05-04 23:09:46 +00:00
body_html = body_html ,
2023-02-18 16:33:19 +00:00
embed = embed ,
2022-10-05 08:04:32 +00:00
title = title ,
2022-05-04 23:09:46 +00:00
title_html = title_html ,
sub = sub ,
2022-11-11 05:21:18 +00:00
ghost = flag_ghost
2022-05-04 23:09:46 +00:00
)
g . db . add ( post )
g . db . flush ( )
2022-11-26 04:52:47 +00:00
for text in { post . body , post . title , post . url } :
2023-02-07 03:31:49 +00:00
if execute_blackjack ( v , post , text , ' submission ' ) : break
2022-05-04 23:09:46 +00:00
2022-12-04 21:46:27 +00:00
process_poll_options ( post , SubmissionOption , bets , 2 , " Bet " , g . db )
process_poll_options ( post , SubmissionOption , options , 0 , " Poll " , g . db )
process_poll_options ( post , SubmissionOption , choices , 1 , " Poll " , g . db )
2022-05-04 23:09:46 +00:00
vote = Vote ( user_id = v . id ,
vote_type = 1 ,
submission_id = post . id
)
g . db . add ( vote )
2023-01-01 11:36:20 +00:00
2022-11-15 09:19:08 +00:00
if request . files . get ( ' file-url ' ) and not g . is_tor :
2022-06-18 15:53:34 +00:00
file = request . files [ ' file-url ' ]
2022-05-04 23:09:46 +00:00
if file . content_type . startswith ( ' image/ ' ) :
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
2022-11-15 09:19:08 +00:00
post . url = process_image ( name , v )
2022-05-04 23:09:46 +00:00
name2 = name . replace ( ' .webp ' , ' r.webp ' )
copyfile ( name , name2 )
2022-12-26 02:16:46 +00:00
post . thumburl = process_image ( name2 , v , resize = 99 )
2022-05-04 23:09:46 +00:00
elif file . content_type . startswith ( ' video/ ' ) :
2022-11-15 09:19:08 +00:00
post . url = process_video ( file , v )
2022-12-09 06:35:56 +00:00
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
2022-12-10 04:02:35 +00:00
subprocess . run ( [ ' ffmpeg ' , ' -y ' , ' -loglevel ' , ' warning ' ,
2022-12-26 02:14:09 +00:00
' -i ' , post . url , ' -vf ' , " scale= ' min(500,iw) ' :-2 " ,
2022-12-10 19:39:30 +00:00
' -q:v ' , ' 3 ' , ' -frames:v ' , ' 1 ' , name ] , check = True )
2022-12-09 06:35:56 +00:00
post . thumburl = name
2022-05-22 22:15:29 +00:00
elif file . content_type . startswith ( ' audio/ ' ) :
2022-11-15 09:19:08 +00:00
post . url = process_audio ( file , v )
2022-05-04 23:09:46 +00:00
else :
2022-06-19 16:56:45 +00:00
abort ( 415 )
2023-01-01 11:36:20 +00:00
2022-05-04 23:09:46 +00:00
if not post . thumburl and post . url :
2022-11-15 10:57:49 +00:00
gevent . spawn ( thumbnail_thread , post . id , v . id )
2022-05-04 23:09:46 +00:00
if not post . private and not post . ghost :
notify_users = NOTIFY_USERS ( f ' { title } { body } ' , v )
if notify_users :
2023-02-24 02:28:10 +00:00
cid , text = notif_comment2 ( post )
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-08-19 22:20:25 +00:00
if v . agendaposter and not v . marseyawarded and AGENDAPOSTER_PHRASE not in f ' { post . body } { post . title } ' . lower ( ) and sub != ' chudrama ' :
2022-05-04 23:09:46 +00:00
post . is_banned = True
post . ban_reason = " AutoJanny "
body = AGENDAPOSTER_MSG . format ( username = v . username , type = ' post ' , AGENDAPOSTER_PHRASE = AGENDAPOSTER_PHRASE )
2022-06-18 15:37:01 +00:00
body_jannied_html = AGENDAPOSTER_MSG_HTML . format ( id = v . id , username = v . username , type = ' post ' , AGENDAPOSTER_PHRASE = AGENDAPOSTER_PHRASE )
2022-05-04 23:09:46 +00:00
2022-07-08 19:03:04 +00:00
c_jannied = Comment ( author_id = AUTOJANNY_ID ,
2022-05-04 23:09:46 +00:00
parent_submission = post . id ,
level = 1 ,
over_18 = False ,
is_bot = True ,
app_id = None ,
distinguish_level = 6 ,
2022-06-18 15:37:01 +00:00
body = body ,
2022-05-04 23:09:46 +00:00
body_html = body_jannied_html ,
2022-05-30 03:26:52 +00:00
ghost = post . ghost
2022-05-04 23:09:46 +00:00
)
g . db . add ( c_jannied )
g . db . flush ( )
2023-02-18 21:59:17 +00:00
post . comment_count + = 1
g . db . add ( post )
2022-05-04 23:09:46 +00:00
c_jannied . top_comment_id = c_jannied . id
n = Notification ( comment_id = c_jannied . id , user_id = v . id )
g . db . add ( n )
2022-07-08 11:44:17 +00:00
if not post . private and not ( post . sub and g . db . query ( Exile . user_id ) . filter_by ( user_id = SNAPPY_ID , sub = post . sub ) . one_or_none ( ) ) :
execute_snappy ( post , v )
2022-05-10 07:20:49 +00:00
2022-07-08 11:07:27 +00:00
v . post_count = g . db . query ( Submission ) . filter_by ( author_id = v . id , deleted_utc = 0 ) . count ( )
2022-05-04 23:09:46 +00:00
g . db . add ( v )
2022-10-23 17:01:00 +00:00
execute_lawlz_actions ( v , post )
2022-08-21 17:14:03 +00:00
2022-12-01 22:12:57 +00:00
if ( SITE == ' rdrama.net '
2023-02-10 11:53:42 +00:00
and v . id in ( IMPASSIONATA_ID , TGTW_ID , SNALLY_ID )
2022-12-10 08:24:29 +00:00
and not ( post . sub and post . subr . stealth ) ) :
2023-02-10 11:53:42 +00:00
post . stickied_utc = int ( time . time ( ) ) + 3600
2022-12-01 12:27:12 +00:00
post . stickied = " AutoJanny "
2022-11-28 02:07:06 +00:00
2022-05-04 23:09:46 +00:00
cache . delete_memoized ( frontlist )
2022-11-15 09:19:08 +00:00
cache . delete_memoized ( userpagelisting )
2022-05-04 23:09:46 +00:00
2022-09-22 20:20:29 +00:00
g . db . commit ( )
2022-11-15 09:19:08 +00:00
if v . client : return post . json ( g . db )
2022-05-04 23:09:46 +00:00
else :
post . voted = 1
2023-02-08 07:15:37 +00:00
return { " post_id " : post . id }
2022-05-04 23:09:46 +00:00
2022-12-29 10:39:10 +00:00
@app.post ( " /delete_post/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def delete_post_pid ( pid , v ) :
post = get_post ( pid )
2022-07-08 11:07:27 +00:00
if post . author_id != v . id : abort ( 403 )
2022-05-04 23:09:46 +00:00
2022-10-11 03:47:39 +00:00
# Temporary special logic by Carp request for events of 2022-10-10
if SITE_NAME == ' rDrama ' and post . author_id == 3161 : abort ( 403 )
2022-07-08 11:07:27 +00:00
if not post . deleted_utc :
post . deleted_utc = int ( time . time ( ) )
post . is_pinned = False
post . stickied = None
2022-05-04 23:09:46 +00:00
2022-07-08 11:07:27 +00:00
g . db . add ( post )
2022-05-04 23:09:46 +00:00
2022-07-08 11:07:27 +00:00
cache . delete_memoized ( frontlist )
2022-11-15 09:19:08 +00:00
cache . delete_memoized ( userpagelisting )
2022-05-04 23:09:46 +00:00
2022-07-08 11:07:27 +00:00
g . db . flush ( )
v . post_count = g . db . query ( Submission ) . filter_by ( author_id = v . id , deleted_utc = 0 ) . count ( )
g . db . add ( v )
2022-05-04 23:09:46 +00:00
return { " message " : " Post deleted! " }
2022-12-29 10:39:10 +00:00
@app.post ( " /undelete_post/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def undelete_post_pid ( pid , v ) :
post = get_post ( pid )
if post . author_id != v . id : abort ( 403 )
2022-07-08 11:07:27 +00:00
if post . deleted_utc :
post . deleted_utc = 0
g . db . add ( post )
cache . delete_memoized ( frontlist )
2022-11-15 09:19:08 +00:00
cache . delete_memoized ( userpagelisting )
2022-05-04 23:09:46 +00:00
2022-07-08 11:07:27 +00:00
g . db . flush ( )
v . post_count = g . db . query ( Submission ) . filter_by ( author_id = v . id , deleted_utc = 0 ) . count ( )
g . db . add ( v )
2022-05-04 23:09:46 +00:00
return { " message " : " Post undeleted! " }
2022-12-29 10:39:10 +00:00
@app.post ( " /mark_post_nsfw/<int:pid> " )
2023-02-01 18:52:34 +00:00
@feature_required ( ' NSFW_MARKING ' )
2023-02-26 08:41:04 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
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-12-10 14:47:20 +00:00
def mark_post_nsfw ( pid , v ) :
2022-05-04 23:09:46 +00:00
post = get_post ( pid )
2022-10-06 00:57:08 +00:00
if post . author_id != v . id and not v . admin_level > = PERMS [ ' POST_COMMENT_MODERATION ' ] and not ( post . sub and v . mods ( post . sub ) ) :
2022-05-04 23:09:46 +00:00
abort ( 403 )
2023-01-01 11:36:20 +00:00
2022-10-04 21:09:25 +00:00
if post . over_18 and v . is_suspended_permanently :
abort ( 403 )
2022-05-04 23:09:46 +00:00
2022-12-10 14:47:20 +00:00
post . over_18 = True
2022-05-04 23:09:46 +00:00
g . db . add ( post )
2022-05-30 12:00:16 +00:00
if post . author_id != v . id :
2022-10-06 00:57:08 +00:00
if v . admin_level > = PERMS [ ' POST_COMMENT_MODERATION ' ] :
2022-10-05 23:24:54 +00:00
ma = ModAction (
2022-12-10 14:47:20 +00:00
kind = " set_nsfw " ,
2022-10-05 23:24:54 +00:00
user_id = v . id ,
target_submission_id = post . id ,
2022-05-30 12:00:16 +00:00
)
2022-10-05 23:24:54 +00:00
g . db . add ( ma )
else :
ma = SubAction (
sub = post . sub ,
2022-12-10 14:47:20 +00:00
kind = " set_nsfw " ,
2022-10-05 23:24:54 +00:00
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
2022-12-13 17:11:26 +00:00
send_repeatable_notification ( post . author_id , f " @ { v . username } (a site admin) has marked [ { post . title } ](/post/ { post . id } ) as +18 " )
2022-05-04 23:09:46 +00:00
2022-12-10 14:47:20 +00:00
return { " message " : " Post has been marked as +18! " }
2022-12-29 10:39:10 +00:00
@app.post ( " /unmark_post_nsfw/<int:pid> " )
2023-02-01 18:52:34 +00:00
@feature_required ( ' NSFW_MARKING ' )
2023-02-26 08:41:04 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
@limiter.limit ( DEFAULT_RATELIMIT )
2023-01-21 04:39:46 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-10 14:47:20 +00:00
@auth_required
def unmark_post_nsfw ( pid , v ) :
post = get_post ( pid )
if post . author_id != v . id and not v . admin_level > = PERMS [ ' POST_COMMENT_MODERATION ' ] and not ( post . sub and v . mods ( post . sub ) ) :
abort ( 403 )
2023-01-01 11:36:20 +00:00
2022-12-10 14:47:20 +00:00
if post . over_18 and v . is_suspended_permanently :
abort ( 403 )
post . over_18 = False
g . db . add ( post )
if post . author_id != v . id :
if v . admin_level > = PERMS [ ' POST_COMMENT_MODERATION ' ] :
ma = ModAction (
kind = " unset_nsfw " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
else :
ma = SubAction (
sub = post . sub ,
kind = " unset_nsfw " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
2022-12-13 17:11:26 +00:00
send_repeatable_notification ( post . author_id , f " @ { v . username } (a site admin) has unmarked [ { post . title } ](/post/ { post . id } ) as +18 " )
2022-12-10 14:47:20 +00:00
return { " message " : " Post has been unmarked as +18! " }
2022-05-04 23:09:46 +00:00
2022-12-29 10:39:10 +00:00
@app.post ( " /save_post/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def save_post ( pid , v ) :
post = get_post ( pid )
save = g . db . query ( SaveRelationship ) . filter_by ( user_id = v . id , submission_id = post . id ) . one_or_none ( )
if not save :
new_save = SaveRelationship ( user_id = v . id , submission_id = post . id )
g . db . add ( new_save )
return { " message " : " Post saved! " }
2022-12-29 10:39:10 +00:00
@app.post ( " /unsave_post/<int:pid> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def unsave_post ( pid , v ) :
post = get_post ( pid )
save = g . db . query ( SaveRelationship ) . filter_by ( user_id = v . id , submission_id = post . id ) . one_or_none ( )
if save :
g . db . delete ( save )
return { " message " : " Post unsaved! " }
2022-12-29 10:39:10 +00:00
@app.post ( " /pin/<int:post_id> " )
2023-02-26 01:42:39 +00:00
@limiter.limit ( ' 1/second ' , scope = path )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
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-08-11 04:05:23 +00:00
def pin_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 :
2022-11-03 07:15:49 +00:00
if v . id != post . author_id : abort ( 403 , " Only the post author can do that! " )
2022-05-04 23:09:46 +00:00
post . is_pinned = not post . is_pinned
g . db . add ( post )
2022-11-15 09:19:08 +00:00
cache . delete_memoized ( userpagelisting )
2022-05-04 23:09:46 +00:00
if post . is_pinned : return { " message " : " Post pinned! " }
else : return { " message " : " Post unpinned! " }
2022-10-11 13:01:39 +00:00
return abort ( 404 , " Post not found! " )
2022-05-04 23:09:46 +00:00
2022-12-29 10:39:10 +00:00
@app.put ( " /post/<int:post_id>/new " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-01 22:04:10 +00:00
@auth_required
2022-12-10 14:57:19 +00:00
def set_new_sort ( post_id : int , v : User ) :
2022-12-01 22:04:10 +00:00
post = get_post ( post_id )
if not v . can_edit ( post ) : abort ( 403 , " Only the post author can do that! " )
2022-12-10 14:57:19 +00:00
post . new = True
2022-12-01 22:04:10 +00:00
g . db . add ( post )
2022-12-10 14:57:19 +00:00
if v . id != post . author_id :
ma = ModAction (
kind = " set_new " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
2022-12-13 17:11:26 +00:00
send_repeatable_notification ( post . author_id , f " @ { v . username } (a site admin) has changed the the default sorting of comments on [ { post . title } ](/post/ { post . id } ) to `new` " )
2022-12-10 14:57:19 +00:00
2023-02-24 02:54:31 +00:00
return { " message " : " Changed the the default sorting of comments on this post to ' new ' " }
2022-12-10 14:57:19 +00:00
2022-12-29 10:39:10 +00:00
@app.delete ( " /post/<int:post_id>/new " )
2023-02-26 08:41:04 +00:00
@limiter.limit ( DEFAULT_RATELIMIT )
2023-02-26 01:42:39 +00:00
@limiter.limit ( DEFAULT_RATELIMIT , key_func = get_ID )
2022-12-10 14:57:19 +00:00
@auth_required
def unset_new_sort ( post_id : int , v : User ) :
post = get_post ( post_id )
if not v . can_edit ( post ) : abort ( 403 , " Only the post author can do that! " )
post . new = None
g . db . add ( post )
if v . id != post . author_id :
ma = ModAction (
kind = " set_hot " ,
user_id = v . id ,
target_submission_id = post . id ,
)
g . db . add ( ma )
2022-12-13 17:11:26 +00:00
send_repeatable_notification ( post . author_id , f " @ { v . username } (a site admin) has changed the the default sorting of comments on [ { post . title } ](/post/ { post . id } ) to `hot` " )
2022-12-10 14:57:19 +00:00
2023-02-24 02:54:31 +00:00
return { " message " : " Changed the the default sorting of comments on this post to ' hot ' " }
2022-12-01 22:04:10 +00:00
2022-05-04 23:09:46 +00:00
2022-10-30 14:55:43 +00:00
extensions = IMAGE_FORMATS + VIDEO_FORMATS + AUDIO_FORMATS
2022-08-30 01:51:09 +00:00
2022-05-04 23:09:46 +00:00
@app.get ( " /submit/title " )
2022-09-30 12:13:06 +00:00
@limiter.limit ( " 3/minute " )
2023-01-21 04:39:46 +00:00
@limiter.limit ( " 3/minute " , key_func = get_ID )
2022-05-04 23:09:46 +00:00
@auth_required
def get_post_title ( v ) :
2022-11-25 13:10:05 +00:00
POST_TITLE_TIMEOUT = 5
2022-05-04 23:09:46 +00:00
url = request . values . get ( " url " )
2022-08-30 01:51:09 +00:00
if not url or ' \\ ' in url : abort ( 400 )
2022-11-12 05:40:17 +00:00
url = url . strip ( )
if not url . startswith ( ' http ' ) : abort ( 400 )
2022-08-30 01:51:09 +00:00
2022-10-30 14:55:43 +00:00
checking_url = url . lower ( ) . split ( ' ? ' ) [ 0 ] . split ( ' %3F ' ) [ 0 ]
if any ( ( checking_url . endswith ( f ' . { x } ' ) for x in extensions ) ) :
2022-08-26 22:03:15 +00:00
abort ( 400 )
2022-05-04 23:09:46 +00:00
2022-11-25 13:10:05 +00:00
try :
x = gevent . with_timeout ( POST_TITLE_TIMEOUT , requests . get , url , headers = titleheaders , timeout = POST_TITLE_TIMEOUT , proxies = proxies )
2022-05-04 23:09:46 +00:00
except : abort ( 400 )
2023-01-01 11:36:20 +00:00
2022-09-30 12:13:06 +00:00
content_type = x . headers . get ( " Content-Type " )
if not content_type or " text/html " not in content_type : abort ( 400 )
2022-05-04 23:09:46 +00:00
2022-11-11 09:24:54 +00:00
# no you can't just parse html with reeeeeeeegex
2022-11-11 10:17:59 +00:00
match = html_title_regex . search ( x . text )
2022-11-11 09:24:54 +00:00
if match and match . lastindex > = 1 :
title = match . group ( 1 )
2022-11-11 09:49:43 +00:00
else : abort ( 400 )
2022-05-04 23:09:46 +00:00
2022-11-26 06:11:00 +00:00
title = html . unescape ( title )
2022-11-11 10:17:59 +00:00
return { " url " : url , " title " : title }