MarseyWorld/files/routes/posts.py

1542 lines
46 KiB
Python
Raw Normal View History

2022-05-04 23:09:46 +00:00
import time
import gevent
import requests
from files.helpers.wrappers import *
from files.helpers.sanitize import *
from files.helpers.alerts import *
2022-05-07 04:13:19 +00:00
from files.helpers.discord import send_discord_message
2022-05-04 23:09:46 +00:00
from files.helpers.const import *
from files.helpers.slots import *
from files.classes import *
from flask import *
from io import BytesIO
from files.__main__ import app, limiter, cache, db_session
from PIL import Image as PILimage
from .front import frontlist, changeloglist
from urllib.parse import ParseResult, urlunparse, urlparse, quote, unquote
from os import path
import requests
from shutil import copyfile
from sys import stdout
2022-05-18 19:29:07 +00:00
import os
2022-05-04 23:09:46 +00:00
if SITE_NAME == 'PCM': snappyquotes = []
else: snappyquotes = [f':#{x}:' for x in marseys_const2]
if path.exists(f'snappy_{SITE_NAME}.txt'):
with open(f'snappy_{SITE_NAME}.txt', "r", encoding="utf-8") as f:
snappyquotes += f.read().split("\n{[para]}\n")
discounts = {
69: 0.02,
70: 0.04,
71: 0.06,
72: 0.08,
73: 0.10,
}
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"}
@app.post("/toggle_club/<pid>")
@auth_required
def toggle_club(pid, v):
post = get_post(pid)
if post.author_id != v.id and v.admin_level < 2: abort(403)
post.club = not post.club
g.db.add(post)
g.db.commit()
if post.club: return {"message": "Post has been marked as club-only!"}
else: return {"message": "Post has been unmarked as club-only!"}
@app.post("/publish/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@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)
if not post.ghost:
notify_users = NOTIFY_USERS(f'{post.title} {post.body}', v)
if notify_users:
cid = notif_comment2(post)
for x in notify_users:
add_notif(cid, x)
if v.followers:
text = f"@{v.username} has made a new post: [{post.title}]({post.shortlink})"
if post.sub: text += f" in <a href='/h/{post.sub}'>/h/{post.sub}"
cid = notif_comment(text, autojanny=True)
for follow in v.followers:
user = get_account(follow.user_id)
if post.club and not user.paid_dues: continue
add_notif(cid, user.id)
g.db.commit()
cache.delete_memoized(frontlist)
cache.delete_memoized(User.userpagelisting)
2022-05-07 04:13:19 +00:00
if v.admin_level > 0 and ("[changelog]" in post.title.lower() or "(changelog)" in post.title.lower()):
2022-05-04 23:09:46 +00:00
send_discord_message(post.permalink)
cache.delete_memoized(changeloglist)
return redirect(post.permalink)
@app.get("/submit")
@app.get("/h/<sub>/submit")
@auth_required
def submit_get(v, sub=None):
if sub: sub = g.db.query(Sub.name).filter_by(name=sub.strip().lower()).one_or_none()
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)
@app.get("/post/<pid>")
@app.get("/post/<pid>/<anything>")
@app.get("/h/<sub>/post/<pid>")
@app.get("/h/<sub>/post/<pid>/<anything>")
2022-05-06 04:55:15 +00:00
@app.get("/logged_out/post/<pid>")
@app.get("/logged_out/post/<pid>/<anything>")
@app.get("/logged_out/h/<sub>/post/<pid>")
@app.get("/logged_out/h/<sub>/post/<pid>/<anything>")
2022-05-04 23:09:46 +00:00
@auth_desired
def post_id(pid, anything=None, v=None, sub=None):
2022-05-06 04:55:15 +00:00
if not v and not request.path.startswith('/logged_out'): return redirect(f"/logged_out{request.full_path}")
2022-05-09 08:47:35 +00:00
if v and request.path.startswith('/logged_out'): return redirect(request.full_path.replace('/logged_out',''))
2022-05-06 04:55:15 +00:00
2022-05-04 23:09:46 +00:00
try: pid = int(pid)
except Exception as e: pass
try: pid = int(pid)
except: abort(404)
post = get_post(pid, v=v)
if post.over_18 and not (v and v.over_18) and session.get('over_18', 0) < int(time.time()):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error":"Must be 18+ to view"}, 451
return render_template("errors/nsfw.html", v=v)
if post.new or 'megathread' in post.title.lower(): defaultsortingcomments = 'new'
elif v: defaultsortingcomments = v.defaultsortingcomments
else: defaultsortingcomments = "top"
sort = request.values.get("sort", defaultsortingcomments)
if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403)
if v:
votes = g.db.query(CommentVote).filter_by(user_id=v.id).subquery()
blocking = v.blocking.subquery()
blocked = v.blocked.subquery()
comments = g.db.query(
Comment,
votes.c.vote_type,
blocking.c.target_id,
blocked.c.target_id,
)
if not (v and v.shadowbanned) and not (v and v.admin_level > 2):
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
comments=comments.filter(Comment.parent_submission == post.id, Comment.author_id.notin_((AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID))).join(
votes,
votes.c.comment_id == Comment.id,
isouter=True
).join(
blocking,
blocking.c.target_id == Comment.author_id,
isouter=True
).join(
blocked,
blocked.c.user_id == Comment.author_id,
isouter=True
)
output = []
for c in comments.all():
comment = c[0]
comment.voted = c[1] or 0
comment.is_blocking = c[2] or 0
comment.is_blocked = c[3] or 0
output.append(comment)
2022-05-26 23:08:23 +00:00
pinned = [c[0] for c in comments.filter(Comment.stickied != None).all()]
2022-05-04 23:09:46 +00:00
2022-05-26 23:08:23 +00:00
comments = comments.filter(Comment.level == 1, Comment.stickied == None)
2022-05-04 23:09:46 +00:00
if sort == "new":
comments = comments.order_by(Comment.created_utc.desc())
elif sort == "old":
comments = comments.order_by(Comment.created_utc)
elif sort == "controversial":
comments = comments.order_by((Comment.upvotes+1)/(Comment.downvotes+1) + (Comment.downvotes+1)/(Comment.upvotes+1), Comment.downvotes.desc())
elif sort == "top":
comments = comments.order_by(Comment.realupvotes.desc())
elif sort == "bottom":
comments = comments.order_by(Comment.upvotes - Comment.downvotes)
first = [c[0] for c in comments.filter(or_(and_(Comment.slots_result == None, Comment.blackjack_result == None, Comment.wordle_result == None), func.length(Comment.body_html) > 100)).all()]
second = [c[0] for c in comments.filter(or_(Comment.slots_result != None, Comment.blackjack_result != None, Comment.wordle_result != None), func.length(Comment.body_html) <= 100).all()]
comments = first + second
else:
2022-05-26 23:08:23 +00:00
pinned = g.db.query(Comment).filter(Comment.parent_submission == post.id, Comment.stickied != None).all()
2022-05-04 23:09:46 +00:00
2022-05-26 23:08:23 +00:00
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == post.id, Comment.author_id.notin_((AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID)), Comment.level == 1, Comment.stickied == None)
2022-05-04 23:09:46 +00:00
if sort == "new":
comments = comments.order_by(Comment.created_utc.desc())
elif sort == "old":
comments = comments.order_by(Comment.created_utc)
elif sort == "controversial":
comments = comments.order_by((Comment.upvotes+1)/(Comment.downvotes+1) + (Comment.downvotes+1)/(Comment.upvotes+1), Comment.downvotes.desc())
elif sort == "top":
comments = comments.order_by(Comment.realupvotes.desc())
elif sort == "bottom":
comments = comments.order_by(Comment.upvotes - Comment.downvotes)
first = comments.filter(or_(and_(Comment.slots_result == None, Comment.blackjack_result == None, Comment.wordle_result == None), func.length(Comment.body_html) > 100)).all()
second = comments.filter(or_(Comment.slots_result != None, Comment.blackjack_result != None, Comment.wordle_result != None), func.length(Comment.body_html) <= 100).all()
comments = first + second
offset = 0
ids = set()
if post.comment_count > 60 and not request.headers.get("Authorization") and not request.values.get("all"):
comments2 = []
count = 0
if post.created_utc > 1638672040:
for comment in comments:
comments2.append(comment)
ids.add(comment.id)
2022-05-09 11:21:49 +00:00
count += g.db.query(Comment).filter_by(parent_submission=post.id, top_comment_id=comment.id).count() + 1
2022-05-04 23:09:46 +00:00
if count > 50: break
else:
for comment in comments:
comments2.append(comment)
ids.add(comment.id)
2022-05-09 11:21:49 +00:00
count += g.db.query(Comment).filter_by(parent_submission=post.id, parent_comment_id=comment.id).count() + 1
2022-05-04 23:09:46 +00:00
if count > 10: break
if len(comments) == len(comments2): offset = 0
else: offset = 1
comments = comments2
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)
pinned.remove(pin)
post.replies = pinned + comments
post.views += 1
g.db.add(post)
g.db.commit()
if request.headers.get("Authorization"): return post.json
else:
if post.is_banned and not (v and (v.admin_level > 1 or post.author_id == v.id)): template = "submission_banned.html"
else: template = "submission.html"
return render_template(template, v=v, p=post, ids=list(ids), sort=sort, render_replies=True, offset=offset, sub=post.subr, fart=app.config['SETTINGS']['Fart mode'])
@app.get("/viewmore/<pid>/<sort>/<offset>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@auth_desired
def viewmore(v, pid, sort, offset):
try: pid = int(pid)
except: abort(400)
post = get_post(pid, v=v)
if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403)
offset = int(offset)
try: ids = set(int(x) for x in request.values.get("ids").split(','))
except: abort(400)
if v:
votes = g.db.query(CommentVote).filter_by(user_id=v.id).subquery()
blocking = v.blocking.subquery()
blocked = v.blocked.subquery()
comments = g.db.query(
Comment,
votes.c.vote_type,
blocking.c.target_id,
blocked.c.target_id,
2022-05-26 23:08:23 +00:00
).filter(Comment.parent_submission == pid, Comment.author_id.notin_((AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID)), Comment.stickied == None, Comment.id.notin_(ids))
2022-05-04 23:09:46 +00:00
if not (v and v.shadowbanned) and not (v and v.admin_level > 2):
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
comments=comments.join(
votes,
votes.c.comment_id == Comment.id,
isouter=True
).join(
blocking,
blocking.c.target_id == Comment.author_id,
isouter=True
).join(
blocked,
blocked.c.user_id == Comment.author_id,
isouter=True
)
output = []
for c in comments.all():
comment = c[0]
comment.voted = c[1] or 0
comment.is_blocking = c[2] or 0
comment.is_blocked = c[3] or 0
output.append(comment)
comments = comments.filter(Comment.level == 1)
if sort == "new":
comments = comments.order_by(Comment.created_utc.desc())
elif sort == "old":
comments = comments.order_by(Comment.created_utc)
elif sort == "controversial":
comments = comments.order_by((Comment.upvotes+1)/(Comment.downvotes+1) + (Comment.downvotes+1)/(Comment.upvotes+1), Comment.downvotes.desc())
elif sort == "top":
comments = comments.order_by(Comment.realupvotes.desc())
elif sort == "bottom":
comments = comments.order_by(Comment.upvotes - Comment.downvotes)
first = [c[0] for c in comments.filter(or_(and_(Comment.slots_result == None, Comment.blackjack_result == None, Comment.wordle_result == None), func.length(Comment.body_html) > 100)).all()]
second = [c[0] for c in comments.filter(or_(Comment.slots_result != None, Comment.blackjack_result != None, Comment.wordle_result != None), func.length(Comment.body_html) <= 100).all()]
comments = first + second
else:
2022-05-26 23:08:23 +00:00
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == pid, Comment.author_id.notin_((AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID)), Comment.level == 1, Comment.stickied == None, Comment.id.notin_(ids))
2022-05-04 23:09:46 +00:00
if sort == "new":
comments = comments.order_by(Comment.created_utc.desc())
elif sort == "old":
comments = comments.order_by(Comment.created_utc)
elif sort == "controversial":
comments = comments.order_by((Comment.upvotes+1)/(Comment.downvotes+1) + (Comment.downvotes+1)/(Comment.upvotes+1), Comment.downvotes.desc())
elif sort == "top":
comments = comments.order_by(Comment.realupvotes.desc())
elif sort == "bottom":
comments = comments.order_by(Comment.upvotes - Comment.downvotes)
first = comments.filter(or_(and_(Comment.slots_result == None, Comment.blackjack_result == None, Comment.wordle_result == None), func.length(Comment.body_html) > 100)).all()
second = comments.filter(or_(Comment.slots_result != None, Comment.blackjack_result != None, Comment.wordle_result != None), func.length(Comment.body_html) <= 100).all()
comments = first + second
comments = comments[offset:]
comments2 = []
count = 0
if post.created_utc > 1638672040:
for comment in comments:
comments2.append(comment)
ids.add(comment.id)
2022-05-09 11:21:49 +00:00
count += g.db.query(Comment).filter_by(parent_submission=post.id, top_comment_id=comment.id).count() + 1
2022-05-04 23:09:46 +00:00
if count > 50: break
else:
for comment in comments:
comments2.append(comment)
ids.add(comment.id)
2022-05-09 11:21:49 +00:00
count += g.db.query(Comment).filter_by(parent_submission=post.id, parent_comment_id=comment.id).count() + 1
2022-05-04 23:09:46 +00:00
if count > 10: break
if len(comments) == len(comments2): offset = 0
else: offset += 1
comments = comments2
return render_template("comments.html", v=v, comments=comments, p=post, ids=list(ids), render_replies=True, pid=pid, sort=sort, offset=offset, ajax=True)
@app.get("/morecomments/<cid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@auth_desired
def morecomments(v, cid):
try: cid = int(cid)
except: abort(400)
tcid = g.db.query(Comment.top_comment_id).filter_by(id=cid).one_or_none()[0]
if v:
votes = g.db.query(CommentVote).filter_by(user_id=v.id).subquery()
blocking = v.blocking.subquery()
blocked = v.blocked.subquery()
comments = g.db.query(
Comment,
votes.c.vote_type,
blocking.c.target_id,
blocked.c.target_id,
).filter(Comment.top_comment_id == tcid, Comment.level > 9).join(
votes,
votes.c.comment_id == Comment.id,
isouter=True
).join(
blocking,
blocking.c.target_id == Comment.author_id,
isouter=True
).join(
blocked,
blocked.c.user_id == Comment.author_id,
isouter=True
)
output = []
dump = []
for c in comments.all():
comment = c[0]
comment.voted = c[1] or 0
comment.is_blocking = c[2] or 0
comment.is_blocked = c[3] or 0
if c[0].parent_comment_id == int(cid): output.append(comment)
else: dump.append(comment)
comments = output
else:
c = g.db.query(Comment).filter_by(id=cid).one_or_none()
comments = c.replies
if comments: p = comments[0].post
else: p = None
return render_template("comments.html", v=v, comments=comments, p=p, render_replies=True, ajax=True)
@app.post("/edit_post/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@auth_required
def edit_post(pid, v):
p = get_post(pid)
if p.author_id != v.id and not (v.admin_level > 1 and v.admin_level > 2): abort(403)
title = request.values.get("title", "").strip().replace('‎','')
body = request.values.get("body", "").strip().replace('‎','')
if len(body) > 20000: return {"error":"Character limit is 20000!"}, 403
if v.id == p.author_id:
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
return {"error":"You have to type more than 280 characters!"}, 403
elif v.bird and len(body) > 140:
return {"error":"You have to type less than 140 characters!"}, 403
if title != p.title:
if v.id == p.author_id and v.agendaposter and not v.marseyawarded: title = torture_ap(title, v.username)
title_html = filter_emojis_only(title, edit=True)
if v.id == p.author_id and v.marseyawarded and not marseyaward_title_regex.fullmatch(title_html):
return {"error":"You can only type marseys!"}, 403
p.title = title[:500]
p.title_html = title_html
if request.files.get("file") and request.headers.get("cf-ipcountry") != "T1":
files = request.files.getlist('file')[:4]
for file in files:
if file.content_type.startswith('image/'):
name = f'/images/{time.time()}'.replace('.','') + '.webp'
file.save(name)
2022-05-07 06:04:14 +00:00
url = process_image(v.patron, name)
2022-05-04 23:09:46 +00:00
body += f"\n\n![]({url})"
elif file.content_type.startswith('video/'):
2022-05-22 16:13:19 +00:00
value = process_video(file)
if type(value) is str: body += f"\n\n{value}"
else: return value
2022-05-22 22:15:29 +00:00
elif file.content_type.startswith('audio/'):
2022-05-23 19:00:14 +00:00
body += f"\n\n{process_audio(file)}"
2022-05-22 22:15:29 +00:00
else: return {"error": "Image/Video/Audio files only"}, 400
2022-05-04 23:09:46 +00:00
2022-05-22 10:26:59 +00:00
body = body.strip()
2022-05-04 23:09:46 +00:00
if body != p.body:
if v.id == p.author_id and v.agendaposter and not v.marseyawarded: body = torture_ap(body, v.username)
2022-05-09 09:32:55 +00:00
for i in poll_regex.finditer(body):
body = body.replace(i.group(0), "")
c = Comment(author_id=AUTOPOLLER_ID,
parent_submission=p.id,
level=1,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=p.ghost
2022-05-09 09:32:55 +00:00
)
g.db.add(c)
for i in choice_regex.finditer(body):
body = body.replace(i.group(0), "")
c = Comment(author_id=AUTOCHOICE_ID,
parent_submission=p.id,
level=1,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=p.ghost
2022-05-09 09:32:55 +00:00
)
g.db.add(c)
2022-05-04 23:09:46 +00:00
body_html = sanitize(body, edit=True)
if v.id == p.author_id and v.marseyawarded and marseyaward_body_regex.search(body_html):
return {"error":"You can only type marseys!"}, 403
p.body = body
if blackjack and any(i in f'{p.body} {p.title} {p.url}'.lower() for i in blackjack.split()):
v.shadowbanned = 'AutoJanny'
g.db.add(v)
send_repeatable_notification(CARP_ID, p.permalink)
if len(body_html) > 40000: return {"error":"Submission body_html too long! (max 40k characters)"}, 400
p.body_html = body_html
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 not p.is_banned:
p.is_banned = True
p.ban_reason = "AutoJanny"
g.db.add(p)
body = AGENDAPOSTER_MSG.format(username=v.username, type='post', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE)
body_jannied_html = sanitize(body)
c_jannied = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=p.id,
level=1,
over_18=False,
is_bot=True,
app_id=None,
2022-05-26 23:08:23 +00:00
stickied='AutoJanny',
2022-05-04 23:09:46 +00:00
distinguish_level=6,
body_html=body_jannied_html,
ghost=p.ghost
)
g.db.add(c_jannied)
g.db.flush()
c_jannied.top_comment_id = c_jannied.id
n = Notification(comment_id=c_jannied.id, user_id=v.id)
g.db.add(n)
if not p.private and not p.ghost:
notify_users = NOTIFY_USERS(f'{p.title} {p.body}', v)
if notify_users:
cid = notif_comment2(p)
for x in notify_users:
add_notif(cid, x)
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)
g.db.commit()
return redirect(p.permalink)
def archiveorg(url):
try: requests.get(f'https://web.archive.org/save/{url}', headers={'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}, timeout=100)
except: pass
def thumbnail_thread(pid):
db = db_session()
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:
return f"{post_url}{'/' if not post_url.endswith('/') else ''}{fragment_url}"
post = db.query(Submission).filter_by(id=pid).one_or_none()
if not post or not post.url:
time.sleep(5)
post = db.query(Submission).filter_by(id=pid).one_or_none()
if not post or not post.url: return
fetch_url = post.url
2022-05-25 19:45:34 +00:00
if fetch_url.startswith('/') and '\\' not in fetch_url:
2022-05-25 18:29:22 +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"}
try:
x=requests.get(fetch_url, headers=headers, timeout=5, proxies=proxies)
except:
db.close()
return
if x.status_code != 200:
db.close()
return
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:
tag = soup.find(
'meta',
attrs={
"name": tag_name,
"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
image = PILimage.open(BytesIO(image_req.content))
if image.width < 30 or image.height < 30:
continue
break
else:
db.close()
return
elif x.headers.get("Content-Type","").startswith("image/"):
image_req=x
image = PILimage.open(BytesIO(x.content))
else:
db.close()
return
size = len(image.fp.read())
if size > 8 * 1024 * 1024:
db.close()
return
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-05-07 06:04:14 +00:00
post.thumburl = process_image(0, name, resize=100)
2022-05-04 23:09:46 +00:00
db.add(post)
db.commit()
if SITE_NAME == 'rDrama':
for t in ("submission","comment"):
word = random.choice(('rdrama','marsey'))
try:
data = requests.get(f'https://api.pushshift.io/reddit/{t}/search?html_decode=true&q={word}&size=1', timeout=5).json()["data"]
except: break
for i in data:
if i["subreddit"] == 'PokemonGoRaids': continue
body_html = f'''<p>New site mention: <a href="https://old.reddit.com{i["permalink"]}?context=89" rel="nofollow noopener noreferrer" target="_blank">https://old.reddit.com{i["permalink"]}?context=89</a></p>'''
existing_comment = db.query(Comment.id).filter_by(author_id=NOTIFICATIONS_ID, parent_submission=None, body_html=body_html).one_or_none()
if existing_comment: break
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
body_html=body_html,
distinguish_level=6
)
db.add(new_comment)
db.flush()
new_comment.top_comment_id = new_comment.id
admins = db.query(User).filter(User.admin_level > 0).all()
for admin in admins:
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
db.add(notif)
k,val = random.choice(tuple(REDDIT_NOTIFS.items()))
try:
data = requests.get(f'https://api.pushshift.io/reddit/{t}/search?html_decode=true&q={k}&size=1', timeout=5).json()["data"]
except: break
for i in data:
body_html = f'''<p>New mention of you: <a href="https://old.reddit.com{i["permalink"]}?context=89" rel="nofollow noopener noreferrer" target="_blank">https://old.reddit.com{i["permalink"]}?context=89</a></p>'''
existing_comment = db.query(Comment.id).filter_by(author_id=NOTIFICATIONS_ID, parent_submission=None,body_html=body_html).one_or_none()
if existing_comment: break
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
body_html=body_html,
distinguish_level=6
)
db.add(new_comment)
db.flush()
new_comment.top_comment_id = new_comment.id
notif = Notification(comment_id=new_comment.id, user_id=val)
db.add(notif)
if SITE == 'pcmemes.net':
for t in ("submission","comment"):
try:
data = requests.get(f'https://api.pushshift.io/reddit/{t}/search?html_decode=true&q=pcmemes.net&size=1', timeout=5).json()["data"]
except: break
for i in data:
body_html = f'''<p>New site mention: <a href="https://old.reddit.com{i["permalink"]}?context=89" rel="nofollow noopener noreferrer" target="_blank">https://old.reddit.com{i["permalink"]}?context=89</a></p>'''
existing_comment = db.query(Comment.id).filter_by(author_id=NOTIFICATIONS_ID, parent_submission=None, body_html=body_html).one_or_none()
if existing_comment: break
new_comment = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=None,
body_html=body_html,
distinguish_level=6
)
db.add(new_comment)
db.flush()
new_comment.top_comment_id = new_comment.id
admins = db.query(User).filter(User.admin_level > 2).all()
for admin in admins:
notif = Notification(comment_id=new_comment.id, user_id=admin.id)
db.add(notif)
db.commit()
db.close()
stdout.flush()
return
@app.post("/is_repost")
def api_is_repost():
url = request.values.get('url')
if not url: abort(400)
for rd in ("://reddit.com", "://new.reddit.com", "://www.reddit.com", "://redd.it", "://libredd.it", "://teddit.net"):
url = url.replace(rd, "://old.reddit.com")
url = normalize_url(url)
2022-05-04 23:09:46 +00:00
parsed_url = urlparse(url)
domain = parsed_url.netloc
if domain in ('old.reddit.com','twitter.com','instagram.com','tiktok.com'):
new_url = ParseResult(scheme="https",
netloc=parsed_url.netloc,
path=parsed_url.path,
params=parsed_url.params,
query=None,
fragment=parsed_url.fragment)
else:
qd = parse_qs(parsed_url.query)
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)
url = urlunparse(new_url)
if url.endswith('/'): url = url[:-1]
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}
else: return {'permalink': ''}
@app.post("/submit")
@app.post("/h/<sub>/submit")
@limiter.limit("1/second;2/minute;10/hour;50/day")
@limiter.limit("1/second;2/minute;10/hour;50/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@auth_required
def submit_post(v, sub=None):
url = request.values.get("url", "").strip()
2022-05-25 18:29:22 +00:00
if '\\' in url: abort(400)
title = request.values.get("title", "").strip()[:500].replace('‎','')
2022-05-04 23:09:46 +00:00
body = request.values.get("body", "").strip().replace('‎','')
def error(error):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": error}, 403
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, error=error, title=title, url=url, body=body), 400
sub = request.values.get("sub")
if sub: sub = sub.replace('/h/','').replace('s/','')
if sub and sub != 'none':
sname = sub.strip().lower()
sub = g.db.query(Sub.name).filter_by(name=sname).one_or_none()
if not sub: return error(f"/h/{sname} not found!")
sub = sub[0]
if v.exiled_from(sub): return error(f"You're exiled from /h/{sub}")
else: sub = None
if v.is_suspended: return error("You can't perform this action while banned.")
if v.agendaposter and not v.marseyawarded: title = torture_ap(title, v.username)
title_html = filter_emojis_only(title, graceful=True)
if v.marseyawarded and not marseyaward_title_regex.fullmatch(title_html):
return error("You can only type marseys!")
if len(title_html) > 1500: return error("Rendered title is too big!")
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
return error("You have to type more than 280 characters!")
elif v.bird and len(body) > 140:
return error("You have to type less than 140 characters!")
embed = None
if url:
for rd in ("://reddit.com", "://new.reddit.com", "://www.reddit.com", "://redd.it", "://libredd.it", "://teddit.net"):
url = url.replace(rd, "://old.reddit.com")
url = normalize_url(url)
2022-05-04 23:09:46 +00:00
parsed_url = urlparse(url)
domain = parsed_url.netloc
if domain in ('old.reddit.com','twitter.com','instagram.com','tiktok.com'):
new_url = ParseResult(scheme="https",
netloc=parsed_url.netloc,
path=parsed_url.path,
params=parsed_url.params,
query=None,
fragment=parsed_url.fragment)
else:
qd = parse_qs(parsed_url.query)
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)
url = urlunparse(new_url)
if url.endswith('/'): url = url[:-1]
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 and SITE != 'localhost': return redirect(repost.permalink)
domain_obj = get_domain(domain)
if not domain_obj: domain_obj = get_domain(domain+parsed_url.path)
if domain_obj:
reason = f"Remove the {domain_obj.domain} link from your post and try again. {domain_obj.reason}"
return error(reason)
elif "twitter.com" == domain:
try: embed = requests.get("https://publish.twitter.com/oembed", params={"url":url, "omit_script":"t"}, timeout=5).json()["html"]
except: pass
elif url.startswith('https://youtube.com/watch?v='):
url = unquote(url).replace('?t', '&t')
yt_id = url.split('https://youtube.com/watch?v=')[1].split('&')[0].split('%')[0]
if yt_id_regex.fullmatch(yt_id):
req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={yt_id}&key={YOUTUBE_KEY}&part=contentDetails", timeout=5).json()
if req.get('items'):
params = parse_qs(urlparse(url).query)
t = params.get('t', params.get('start', [0]))[0]
if isinstance(t, str): t = t.replace('s','')
embed = f'<lite-youtube videoid="{yt_id}" params="autoplay=1&modestbranding=1'
if t:
try: embed += f'&start={int(t)}'
except: pass
embed += '"></lite-youtube>'
elif app.config['SERVER_NAME'] in domain and "/post/" in url and "context" not in url:
id = url.split("/post/")[1]
if "/" in id: id = id.split("/")[0]
embed = str(int(id))
if not url and not request.values.get("body") and not request.files.get("file") and not request.files.get("file2"):
return error("Please enter a url or some text.")
if not title:
return error("Please enter a better title.")
elif len(title) > 500:
return error("There's a 500 character limit for titles.")
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 and SITE != 'localhost': return redirect(dup.permalink)
now = int(time.time())
cutoff = now - 60 * 60 * 24
similar_posts = g.db.query(Submission).filter(
Submission.author_id == v.id,
Submission.title.op('<->')(title) < app.config["SPAM_SIMILARITY_THRESHOLD"],
Submission.created_utc > cutoff
).all()
if url:
similar_urls = g.db.query(Submission).filter(
Submission.author_id == v.id,
Submission.url.op('<->')(url) < app.config["SPAM_URL_SIMILARITY_THRESHOLD"],
Submission.created_utc > cutoff
).all()
else: similar_urls = []
threshold = app.config["SPAM_SIMILAR_COUNT_THRESHOLD"]
if v.age >= (60 * 60 * 24 * 7): threshold *= 3
elif v.age >= (60 * 60 * 24): threshold *= 2
if max(len(similar_urls), len(similar_posts)) >= threshold:
text = "Your account has been banned for **1 day** for the following reason:\n\n> Too much spam!"
send_repeatable_notification(v.id, text)
v.ban(reason="Spamming.",
days=1)
for post in similar_posts + similar_urls:
post.is_banned = True
post.is_pinned = False
post.ban_reason = "AutoJanny"
g.db.add(post)
ma=ModAction(
user_id=AUTOJANNY_ID,
target_submission_id=post.id,
kind="ban_post",
_note="spam"
)
g.db.add(ma)
return redirect("/notifications")
if len(str(body)) > 20000:
return error("There's a 20000 character limit for text body.")
if len(url) > 2048:
return error("There's a 2048 character limit for URLs.")
if v and v.admin_level > 2:
bet_options = []
for i in bet_regex.finditer(body):
bet_options.append(i.group(1))
body = body.replace(i.group(0), "")
options = []
for i in poll_regex.finditer(body):
options.append(i.group(1))
body = body.replace(i.group(0), "")
choices = []
for i in choice_regex.finditer(body):
choices.append(i.group(1))
body = body.replace(i.group(0), "")
if v.agendaposter and not v.marseyawarded: body = torture_ap(body, v.username)
if request.files.get("file2") and request.headers.get("cf-ipcountry") != "T1":
files = request.files.getlist('file2')[:4]
for file in files:
if file.content_type.startswith('image/'):
name = f'/images/{time.time()}'.replace('.','') + '.webp'
file.save(name)
2022-05-07 06:04:14 +00:00
body += f"\n\n![]({process_image(v.patron, name)})"
2022-05-04 23:09:46 +00:00
elif file.content_type.startswith('video/'):
2022-05-22 16:13:19 +00:00
value = process_video(file)
if type(value) is str: body += f"\n\n{value}"
else: return error(value['error'])
2022-05-22 22:15:29 +00:00
elif file.content_type.startswith('audio/'):
2022-05-23 19:00:14 +00:00
body += f"\n\n{process_audio(file)}"
2022-05-04 23:09:46 +00:00
else:
2022-05-22 22:15:29 +00:00
return error("Image/Video/Audio files only.")
2022-05-04 23:09:46 +00:00
2022-05-22 10:26:59 +00:00
body = body.strip()
2022-05-04 23:09:46 +00:00
body_html = sanitize(body)
if v.marseyawarded and marseyaward_body_regex.search(body_html):
return error("You can only type marseys!")
if len(body_html) > 40000: return error("Submission body_html too long! (max 40k characters)")
club = bool(request.values.get("club",""))
if embed and len(embed) > 1500: embed = None
is_bot = bool(request.headers.get("Authorization")) or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID)
if request.values.get("ghost") and v.coins >= 100:
v.coins -= 100
ghost = True
else: ghost = False
post = Submission(
private=bool(request.values.get("private","")),
club=club,
author_id=v.id,
over_18=bool(request.values.get("over_18","")),
new=bool(request.values.get("new","")),
app_id=v.client.application.id if v.client else None,
is_bot = is_bot,
url=url,
body=body[:20000],
body_html=body_html,
embed_url=embed,
title=title[:500],
title_html=title_html,
sub=sub,
ghost=ghost
)
g.db.add(post)
g.db.flush()
if blackjack and any(i in f'{post.body} {post.title} {post.url}'.lower() for i in blackjack.split()):
v.shadowbanned = 'AutoJanny'
g.db.add(v)
2022-05-27 15:28:30 +00:00
send_repeatable_notification(CARP_ID, post.permalink)
2022-05-04 23:09:46 +00:00
if v and v.admin_level > 2:
for option in bet_options:
bet_option = Comment(author_id=AUTOBETTER_ID,
parent_submission=post.id,
level=1,
body_html=filter_emojis_only(option),
upvotes=0,
is_bot=True
)
g.db.add(bet_option)
for option in options:
c = Comment(author_id=AUTOPOLLER_ID,
parent_submission=post.id,
level=1,
body_html=filter_emojis_only(option),
upvotes=0,
is_bot=True,
ghost=post.ghost
2022-05-04 23:09:46 +00:00
)
g.db.add(c)
for choice in choices:
c = Comment(author_id=AUTOCHOICE_ID,
parent_submission=post.id,
level=1,
body_html=filter_emojis_only(choice),
upvotes=0,
is_bot=True,
ghost=post.ghost
2022-05-04 23:09:46 +00:00
)
g.db.add(c)
vote = Vote(user_id=v.id,
vote_type=1,
submission_id=post.id
)
g.db.add(vote)
if request.files.get('file') and request.headers.get("cf-ipcountry") != "T1":
file = request.files['file']
if file.content_type.startswith('image/'):
name = f'/images/{time.time()}'.replace('.','') + '.webp'
file.save(name)
2022-05-07 06:04:14 +00:00
post.url = process_image(v.patron, name)
2022-05-04 23:09:46 +00:00
name2 = name.replace('.webp', 'r.webp')
copyfile(name, name2)
2022-05-07 06:04:14 +00:00
post.thumburl = process_image(v.patron, name2, resize=100)
2022-05-04 23:09:46 +00:00
elif file.content_type.startswith('video/'):
2022-05-22 16:13:19 +00:00
value = process_video(file)
if type(value) is str: post.url = value
else: return error(value['error'])
2022-05-22 22:15:29 +00:00
elif file.content_type.startswith('audio/'):
2022-05-23 19:00:14 +00:00
post.url = process_audio(file)
2022-05-04 23:09:46 +00:00
else:
2022-05-22 22:15:29 +00:00
return error("Image/Video/Audio files only.")
2022-05-04 23:09:46 +00:00
if not post.thumburl and post.url:
gevent.spawn(thumbnail_thread, post.id)
if not post.private and not post.ghost:
notify_users = NOTIFY_USERS(f'{title} {body}', v)
if notify_users:
cid = notif_comment2(post)
for x in notify_users:
add_notif(cid, x)
if (request.values.get('followers') or is_bot) and v.followers:
text = f"@{v.username} has made a new post: [{post.title}]({post.shortlink})"
if post.sub: text += f" in <a href='/h/{post.sub}'>/h/{post.sub}"
cid = notif_comment(text, autojanny=True)
for follow in v.followers:
user = get_account(follow.user_id)
if post.club and not user.paid_dues: continue
add_notif(cid, user.id)
if v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in f'{post.body}{post.title}'.lower():
post.is_banned = True
post.ban_reason = "AutoJanny"
body = AGENDAPOSTER_MSG.format(username=v.username, type='post', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE)
body_jannied_html = sanitize(body)
c_jannied = Comment(author_id=NOTIFICATIONS_ID,
parent_submission=post.id,
level=1,
over_18=False,
is_bot=True,
app_id=None,
2022-05-26 23:08:23 +00:00
stickied='AutoJanny',
2022-05-04 23:09:46 +00:00
distinguish_level=6,
body_html=body_jannied_html,
ghost=post.ghost
2022-05-04 23:09:46 +00:00
)
g.db.add(c_jannied)
g.db.flush()
c_jannied.top_comment_id = c_jannied.id
n = Notification(comment_id=c_jannied.id, user_id=v.id)
g.db.add(n)
if not (post.sub and g.db.query(Exile.user_id).filter_by(user_id=SNAPPY_ID, sub=post.sub).one_or_none()):
if post.sub == 'dankchristianmemes':
body = random.choice(christian_emojis)
elif v.id == CARP_ID:
if random.random() < 0.02: body = "i love you carp"
else: body = ":#marseyfuckoffcarp:"
elif v.id == LAWLZ_ID:
if random.random() < 0.5: body = "wow, this lawlzpost sucks!"
else: body = "wow, a good lawlzpost for once!"
else:
body = random.choice(snappyquotes)
if body.startswith('â–¼'):
body = body[1:]
vote = Vote(user_id=SNAPPY_ID,
vote_type=-1,
submission_id=post.id,
real = True
)
g.db.add(vote)
post.downvotes += 1
if body.startswith('OP is a Trump supporter'):
flag = Flag(post_id=post.id, user_id=SNAPPY_ID, reason='Trump supporter')
g.db.add(flag)
elif body.startswith('You had your chance. Downvoted and reported'):
flag = Flag(post_id=post.id, user_id=SNAPPY_ID, reason='Retard')
g.db.add(flag)
elif body.startswith('â–²'):
body = body[1:]
vote = Vote(user_id=SNAPPY_ID,
vote_type=1,
submission_id=post.id,
real = True
)
g.db.add(vote)
post.upvotes += 1
body += "\n\n"
if post.url:
if post.url.startswith('https://old.reddit.com/r/'):
rev = post.url.replace('https://old.reddit.com/', '')
rev = f"* [unddit.com](https://unddit.com/{rev})\n"
elif post.url.startswith("https://old.reddit.com/u/"):
rev = post.url.replace('https://old.reddit.com/u/', '')
rev = f"* [search.marsey.cat](https://search.marsey.cat/reddit-search/#\u007b\"author\":\"{rev}\",\"resultSize\":100\u007d)\n"
2022-05-04 23:09:46 +00:00
else: rev = ''
newposturl = post.url
if newposturl.startswith('/'): newposturl = f"{SITE_FULL}{newposturl}"
body += f"Snapshots:\n\n{rev}* [archive.org](https://web.archive.org/{newposturl})\n* [archive.ph](https://archive.ph/?url={quote(newposturl)}&run=1) (click to archive)\n* [ghostarchive.org](https://ghostarchive.org/search?term={quote(newposturl)}) (click to archive)\n\n"
gevent.spawn(archiveorg, newposturl)
captured = []
for i in list(snappy_url_regex.finditer(post.body_html))[:20]:
if i.group(0) in captured: continue
captured.append(i.group(0))
href = i.group(1)
if not href: continue
title = i.group(2)
if "Snapshots:\n\n" not in body: body += "Snapshots:\n\n"
if f'**[{title}]({href})**:\n\n' not in body:
body += f'**[{title}]({href})**:\n\n'
if href.startswith('https://old.reddit.com/r/'):
rev = href.replace('https://old.reddit.com/', '')
body += f'* [unddit.com](https://unddit.com/{rev})\n'
2022-05-04 23:09:46 +00:00
if href.startswith('https://old.reddit.com/u/'):
rev = href.replace('https://old.reddit.com/u/', '')
body += f"* [search.marsey.cat](https://search.marsey.cat/reddit-search/#\u007b\"author\":\"{rev}\",\"resultSize\":100\u007d)\n"
2022-05-04 23:09:46 +00:00
body += f'* [archive.org](https://web.archive.org/{href})\n'
body += f'* [archive.ph](https://archive.ph/?url={quote(href)}&run=1) (click to archive)\n'
body += f'* [ghostarchive.org](https://ghostarchive.org/search?term={quote(href)}) (click to archive)\n\n'
gevent.spawn(archiveorg, href)
2022-05-07 05:28:51 +00:00
if body == '!slots':
body = f'!slots{snappy.coins}'
2022-05-04 23:09:46 +00:00
body_html = sanitize(body)
if len(body_html) < 40000:
c = Comment(author_id=SNAPPY_ID,
distinguish_level=6,
parent_submission=post.id,
level=1,
over_18=False,
is_bot=True,
app_id=None,
body_html=body_html,
ghost=post.ghost
2022-05-04 23:09:46 +00:00
)
g.db.add(c)
snappy = g.db.query(User).filter_by(id = SNAPPY_ID).one_or_none()
snappy.comment_count += 1
snappy.coins += 1
g.db.add(snappy)
2022-05-07 05:28:51 +00:00
if body.startswith('!slots'):
2022-05-04 23:09:46 +00:00
check_for_slots_command(body, snappy, c)
if body.startswith(':#marseypin:') or body.startswith(':#marseypin2:'):
2022-05-10 07:20:49 +00:00
post.stickied = "Snappy"
post.stickied_utc = int(time.time()) + 3600
2022-05-04 23:09:46 +00:00
g.db.flush()
c.top_comment_id = c.id
post.comment_count += 1
post.replies = [c]
2022-05-28 00:30:07 +00:00
v.post_count = g.db.query(Submission).filter_by(author_id=v.id).count()
2022-05-04 23:09:46 +00:00
g.db.add(v)
if v.id == PIZZASHILL_ID:
for uid in PIZZA_VOTERS:
autovote = Vote(user_id=uid, submission_id=post.id, vote_type=1)
g.db.add(autovote)
v.coins += 3
v.truecoins += 3
g.db.add(v)
post.upvotes += 3
g.db.add(post)
2022-05-25 19:45:03 +00:00
if request.host == 'rdrama.net' and v.id == CARP_ID:
send_repeatable_notification(AEVANN_ID, post.permalink)
2022-05-04 23:09:46 +00:00
g.db.commit()
cache.delete_memoized(frontlist)
cache.delete_memoized(User.userpagelisting)
2022-05-07 04:13:19 +00:00
if v.admin_level > 0 and ("[changelog]" in post.title.lower() or "(changelog)" in post.title.lower()) and not post.private:
2022-05-04 23:09:46 +00:00
send_discord_message(post.permalink)
cache.delete_memoized(changeloglist)
if request.headers.get("Authorization"): return post.json
else:
post.voted = 1
if post.new or 'megathread' in post.title.lower(): sort = 'new'
else: sort = v.defaultsortingcomments
return render_template('submission.html', v=v, p=post, sort=sort, render_replies=True, offset=0, success=True, sub=post.subr)
@app.post("/delete_post/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@auth_required
def delete_post_pid(pid, v):
post = get_post(pid)
if post.author_id != v.id:
abort(403)
post.deleted_utc = int(time.time())
post.is_pinned = False
post.stickied = None
g.db.add(post)
cache.delete_memoized(frontlist)
g.db.commit()
return {"message": "Post deleted!"}
@app.post("/undelete_post/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@auth_required
def undelete_post_pid(pid, v):
post = get_post(pid)
if post.author_id != v.id: abort(403)
post.deleted_utc =0
g.db.add(post)
cache.delete_memoized(frontlist)
g.db.commit()
return {"message": "Post undeleted!"}
@app.post("/toggle_comment_nsfw/<cid>")
@auth_required
def toggle_comment_nsfw(cid, v):
comment = g.db.query(Comment).filter_by(id=cid).one_or_none()
if comment.author_id != v.id and not v.admin_level > 1: abort(403)
comment.over_18 = not comment.over_18
g.db.add(comment)
g.db.commit()
if comment.over_18: return {"message": "Comment has been marked as +18!"}
else: return {"message": "Comment has been unmarked as +18!"}
@app.post("/toggle_post_nsfw/<pid>")
@auth_required
def toggle_post_nsfw(pid, v):
post = get_post(pid)
if post.author_id != v.id and not v.admin_level > 1:
abort(403)
post.over_18 = not post.over_18
g.db.add(post)
if post.author_id!=v.id:
ma=ModAction(
kind="set_nsfw" if post.over_18 else "unset_nsfw",
user_id=v.id,
target_submission_id=post.id,
)
g.db.add(ma)
g.db.commit()
if post.over_18: return {"message": "Post has been marked as +18!"}
else: return {"message": "Post has been unmarked as +18!"}
@app.post("/save_post/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@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)
g.db.commit()
return {"message": "Post saved!"}
@app.post("/unsave_post/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@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)
g.db.commit()
return {"message": "Post unsaved!"}
@app.post("/pin/<post_id>")
@auth_required
def api_pin_post(post_id, v):
post = g.db.query(Submission).filter_by(id=post_id).one_or_none()
if post:
if v.id != post.author_id: return {"error": "Only the post author's can do that!"}
post.is_pinned = not post.is_pinned
g.db.add(post)
cache.delete_memoized(User.userpagelisting)
g.db.commit()
if post.is_pinned: return {"message": "Post pinned!"}
else: return {"message": "Post unpinned!"}
return {"error": "Post not found!"}
@app.get("/submit/title")
@limiter.limit("6/minute")
@limiter.limit("6/minute", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@auth_required
def get_post_title(v):
url = request.values.get("url")
2022-05-25 18:29:22 +00:00
if not url or '\\' in url: abort(400)
2022-05-04 23:09:46 +00:00
try: x = requests.get(url, headers=titleheaders, timeout=5, proxies=proxies)
except: abort(400)
soup = BeautifulSoup(x.content, 'lxml')
title = soup.find('title')
if not title: abort(400)
return {"url": url, "title": title.string}