remotes/1693045480750635534/spooky-22
Aevann1 2021-10-08 04:14:54 +02:00
parent f4b6bb72c5
commit e69dbd0ed1
21 changed files with 1 additions and 129 deletions

View File

@ -98,8 +98,7 @@ def before_request():
g.timestamp = int(time.time()) g.timestamp = int(time.time())
#do not access session for static files if not request.path.startswith("/assets") and not request.path.startswith("/images") and not request.path.startswith("/hostedimages"):
if not request.path.startswith("/assets"):
session.permanent = True session.permanent = True
if not session.get("session_id"): session["session_id"] = secrets.token_hex(16) if not session.get("session_id"): session["session_id"] = secrets.token_hex(16)

View File

@ -102,9 +102,7 @@ class Comment(Base):
now = time.gmtime() now = time.gmtime()
ctd = time.gmtime(self.created_utc) ctd = time.gmtime(self.created_utc)
# compute number of months
months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year)
# remove a month count if current day of month < creation day of month
if now.tm_mday < ctd.tm_mday: if now.tm_mday < ctd.tm_mday:
months -= 1 months -= 1
@ -234,7 +232,6 @@ class Comment(Base):
'score': self.score, 'score': self.score,
'upvotes': self.upvotes, 'upvotes': self.upvotes,
'downvotes': self.downvotes, 'downvotes': self.downvotes,
#'award_count': self.award_count,
'is_bot': self.is_bot, 'is_bot': self.is_bot,
'flags': flags, 'flags': flags,
} }

View File

@ -53,9 +53,7 @@ class ModAction(Base):
now = time.gmtime() now = time.gmtime()
ctd = time.gmtime(self.created_utc) ctd = time.gmtime(self.created_utc)
# compute number of months
months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year)
# remove a month count if current day of month < creation day of month
if now.tm_mday < ctd.tm_mday: if now.tm_mday < ctd.tm_mday:
months -= 1 months -= 1

View File

@ -107,9 +107,7 @@ class Submission(Base):
now = time.gmtime() now = time.gmtime()
ctd = time.gmtime(self.created_utc) ctd = time.gmtime(self.created_utc)
# compute number of months
months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year)
# remove a month count if current day of month < creation day of month
if now.tm_mday < ctd.tm_mday: if now.tm_mday < ctd.tm_mday:
months -= 1 months -= 1
@ -266,7 +264,6 @@ class Submission(Base):
'downvotes': self.downvotes, 'downvotes': self.downvotes,
'stickied': self.stickied, 'stickied': self.stickied,
'distinguish_level': self.distinguish_level, 'distinguish_level': self.distinguish_level,
#'award_count': self.award_count,
'voted': self.voted if hasattr(self, 'voted') else 0, 'voted': self.voted if hasattr(self, 'voted') else 0,
'flags': flags, 'flags': flags,
} }

View File

@ -613,9 +613,7 @@ class ViewerRelationship(Base):
now = time.gmtime() now = time.gmtime()
ctd = time.gmtime(self.created_utc) ctd = time.gmtime(self.created_utc)
# compute number of months
months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year)
# remove a month count if current day of month < creation day of month
if now.tm_mday < ctd.tm_mday: if now.tm_mday < ctd.tm_mday:
months -= 1 months -= 1

View File

@ -9,7 +9,6 @@ from .const import *
def send_notification(vid, user, text): def send_notification(vid, user, text):
# for when working outside request context
if isinstance(user, int): if isinstance(user, int):
uid = user uid = user
else: else:

View File

@ -20,7 +20,6 @@ def filter_comment_html(html_text):
domain = urlparse(href).netloc domain = urlparse(href).netloc
# parse domain into all possible subdomains
parts = domain.split(".") parts = domain.split(".")
for i in range(len(parts)): for i in range(len(parts)):
new_domain = parts[i] new_domain = parts[i]
@ -29,7 +28,6 @@ def filter_comment_html(html_text):
domain_list.add(new_domain) domain_list.add(new_domain)
# search db for domain rules that prohibit commenting
bans = [x for x in g.db.query(BannedDomain).options(lazyload('*')).filter(BannedDomain.domain.in_(list(domain_list))).all()] bans = [x for x in g.db.query(BannedDomain).options(lazyload('*')).filter(BannedDomain.domain.in_(list(domain_list))).all()]
if bans: if bans:

View File

@ -250,7 +250,6 @@ def get_comments(cids, v=None, load_parent=False):
def get_domain(s): def get_domain(s):
# parse domain into all possible subdomains
parts = s.split(".") parts = s.split(".")
domain_list = set([]) domain_list = set([])
for i in range(len(parts)): for i in range(len(parts)):
@ -267,8 +266,6 @@ def get_domain(s):
if not doms: if not doms:
return None return None
# return the most specific domain - the one with the longest domain
# property
doms = sorted(doms, key=lambda x: len(x.domain), reverse=True) doms = sorted(doms, key=lambda x: len(x.domain), reverse=True)
return doms[0] return doms[0]

View File

@ -148,7 +148,6 @@ def sanitize(sanitized, noimages=False):
tag.wrap(link) tag.wrap(link)
#disguised link preventer
for tag in soup.find_all("a"): for tag in soup.find_all("a"):
tag["target"] = "_blank" tag["target"] = "_blank"
@ -160,16 +159,13 @@ def sanitize(sanitized, noimages=False):
except: except:
tag.string = "" tag.string = ""
#clean up tags in code
for tag in soup.find_all("code"): for tag in soup.find_all("code"):
tag.contents=[x.string for x in tag.contents if x.string] tag.contents=[x.string for x in tag.contents if x.string]
#whatever else happens with images, there are only two sets of classes allowed
for tag in soup.find_all("img"): for tag in soup.find_all("img"):
if 'profile-pic-20' not in tag.attrs.get("class",""): if 'profile-pic-20' not in tag.attrs.get("class",""):
tag.attrs['class']="in-comment-image rounded-sm my-2" tag.attrs['class']="in-comment-image rounded-sm my-2"
#table format
for tag in soup.find_all("table"): for tag in soup.find_all("table"):
tag.attrs['class']="table table-striped" tag.attrs['class']="table table-striped"

View File

@ -107,7 +107,6 @@ def auth_desired(f):
def auth_required(f): def auth_required(f):
# decorator for any view that requires login (ex. settings)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
@ -120,7 +119,6 @@ def auth_required(f):
g.v = v g.v = v
# an ugly hack to make api work
resp = make_response(f(*args, v=v, **kwargs)) resp = make_response(f(*args, v=v, **kwargs))
return resp return resp
@ -129,7 +127,6 @@ def auth_required(f):
def is_not_banned(f): def is_not_banned(f):
# decorator that enforces lack of ban
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):

View File

@ -658,7 +658,6 @@ def admin_image_ban(v):
i=request.files['file'] i=request.files['file']
#make phash
tempname = f"admin_image_ban_{v.username}_{int(time.time())}" tempname = f"admin_image_ban_{v.username}_{int(time.time())}"
i.save(tempname) i.save(tempname)
@ -668,20 +667,17 @@ def admin_image_ban(v):
value = int(str(h), 16) value = int(str(h), 16)
bindigits = [] bindigits = []
# Seed digit: 2**0
digit = (value % 2) digit = (value % 2)
value //= 2 value //= 2
bindigits.append(digit) bindigits.append(digit)
while value > 0: while value > 0:
# Next power of 2**n
digit = (value % 2) digit = (value % 2)
value //= 2 value //= 2
bindigits.append(digit) bindigits.append(digit)
h = ''.join([str(d) for d in bindigits]) h = ''.join([str(d) for d in bindigits])
#check db for existing
badpic = g.db.query(BadPic).options(lazyload('*')).filter_by( badpic = g.db.query(BadPic).options(lazyload('*')).filter_by(
phash=h phash=h
).first() ).first()
@ -874,7 +870,6 @@ def ban_user(user_id, v):
if user.admin_level >= v.admin_level: abort(403) if user.admin_level >= v.admin_level: abort(403)
# check for number of days for suspension
if 'form' in request.values: if 'form' in request.values:
days = float(request.values.get("days")) if request.values.get('days') else 0 days = float(request.values.get("days")) if request.values.get('days') else 0
reason = sanitize(request.values.get("reason", "")) reason = sanitize(request.values.get("reason", ""))

View File

@ -143,7 +143,6 @@ def api_comment(v):
level = parent.level + 1 level = parent.level + 1
else: abort(400) else: abort(400)
#process and sanitize
body = request.values.get("body", "")[:10000] body = request.values.get("body", "")[:10000]
body = body.strip() body = body.strip()
@ -162,7 +161,6 @@ def api_comment(v):
body_md = CustomRenderer().render(mistletoe.Document(body_md)) body_md = CustomRenderer().render(mistletoe.Document(body_md))
body_html = sanitize(body_md) body_html = sanitize(body_md)
# Run safety filter
bans = filter_comment_html(body_html) bans = filter_comment_html(body_html)
if bans: if bans:
@ -171,14 +169,12 @@ def api_comment(v):
if ban.reason: if ban.reason:
reason += f" {ban.reason}" reason += f" {ban.reason}"
#auto ban for digitally malicious content
if any([x.reason==4 for x in bans]): if any([x.reason==4 for x in bans]):
v.ban(days=30, reason="Digitally malicious content") v.ban(days=30, reason="Digitally malicious content")
if any([x.reason==7 for x in bans]): if any([x.reason==7 for x in bans]):
v.ban( reason="Sexualizing minors") v.ban( reason="Sexualizing minors")
return {"error": reason}, 401 return {"error": reason}, 401
# check existing
existing = g.db.query(Comment).options(lazyload('*')).filter(Comment.author_id == v.id, existing = g.db.query(Comment).options(lazyload('*')).filter(Comment.author_id == v.id,
Comment.deleted_utc == 0, Comment.deleted_utc == 0,
Comment.parent_comment_id == parent_comment_id, Comment.parent_comment_id == parent_comment_id,
@ -191,10 +187,8 @@ def api_comment(v):
if parent.author.any_block_exists(v) and not v.admin_level>=3: if parent.author.any_block_exists(v) and not v.admin_level>=3:
return {"error": "You can't reply to users who have blocked you, or users you have blocked."}, 403 return {"error": "You can't reply to users who have blocked you, or users you have blocked."}, 403
# get bot status
is_bot = request.headers.get("X-User-Type","")=="Bot" is_bot = request.headers.get("X-User-Type","")=="Bot"
# check spam - this should hopefully be faster
if not is_bot: if not is_bot:
now = int(time.time()) now = int(time.time())
cutoff = now - 60 * 60 * 24 cutoff = now - 60 * 60 * 24
@ -503,7 +497,6 @@ def api_comment(v):
if not v.shadowbanned: if not v.shadowbanned:
# queue up notification for parent author
notify_users = set() notify_users = set()
for x in g.db.query(Subscription.user_id).options(lazyload('*')).filter_by(submission_id=c.parent_submission).all(): for x in g.db.query(Subscription.user_id).options(lazyload('*')).filter_by(submission_id=c.parent_submission).all():
@ -547,7 +540,6 @@ def api_comment(v):
# create auto upvote
vote = CommentVote(user_id=v.id, vote = CommentVote(user_id=v.id,
comment_id=c.id, comment_id=c.id,
vote_type=1 vote_type=1
@ -599,7 +591,6 @@ def edit_comment(cid, v):
ban = bans[0] ban = bans[0]
reason = f"Remove the {ban.domain} link from your comment and try again." reason = f"Remove the {ban.domain} link from your comment and try again."
#auto ban for digitally malicious content
if any([x.reason==4 for x in bans]): if any([x.reason==4 for x in bans]):
v.ban(days=30, reason="Digitally malicious content is not allowed.") v.ban(days=30, reason="Digitally malicious content is not allowed.")
return {"error":"Digitally malicious content is not allowed."} return {"error":"Digitally malicious content is not allowed."}
@ -614,7 +605,6 @@ def edit_comment(cid, v):
body=body, body=body,
v=v v=v
) )
# check spam - this should hopefully be faster
now = int(time.time()) now = int(time.time())
cutoff = now - 60 * 60 * 24 cutoff = now - 60 * 60 * 24
@ -741,7 +731,6 @@ def edit_comment(cid, v):
g.db.flush() g.db.flush()
# queue up notifications for username mentions
notify_users = set() notify_users = set()
soup = BeautifulSoup(body_html, features="html.parser") soup = BeautifulSoup(body_html, features="html.parser")
mentions = soup.find_all("a", href=re.compile("^/@(\w+)")) mentions = soup.find_all("a", href=re.compile("^/@(\w+)"))

View File

@ -36,7 +36,6 @@ def join_discord(v):
def discord_redirect(v): def discord_redirect(v):
#validate state
now=int(time.time()) now=int(time.time())
state=request.values.get('state','').split('.') state=request.values.get('state','').split('.')
@ -50,7 +49,6 @@ def discord_redirect(v):
if not validate_hash(f"{timestamp}+{v.id}+discord", state): if not validate_hash(f"{timestamp}+{v.id}+discord", state):
abort(400) abort(400)
#get discord token
code = request.values.get("code","") code = request.values.get("code","")
if not code: if not code:
abort(400) abort(400)
@ -79,7 +77,6 @@ def discord_redirect(v):
abort(403) abort(403)
#get user ID
url="https://discord.com/api/users/@me" url="https://discord.com/api/users/@me"
headers={ headers={
'Authorization': f"Bearer {token}" 'Authorization': f"Bearer {token}"
@ -90,13 +87,11 @@ def discord_redirect(v):
#add user to discord
headers={ headers={
'Authorization': f"Bot {BOT_TOKEN}", 'Authorization': f"Bot {BOT_TOKEN}",
'Content-Type': "application/json" 'Content-Type': "application/json"
} }
#remove existing user if applicable
if v.discord_id and v.discord_id != x['id']: if v.discord_id and v.discord_id != x['id']:
url=f"https://discord.com/api/guilds/{SERVER_ID}/members/{v.discord_id}" url=f"https://discord.com/api/guilds/{SERVER_ID}/members/{v.discord_id}"
requests.delete(url, headers=headers) requests.delete(url, headers=headers)

View File

@ -121,7 +121,6 @@ def front_all(v):
try: page = int(request.values.get("page") or 1) try: page = int(request.values.get("page") or 1)
except: abort(400) except: abort(400)
# prevent invalid paging
page = max(page, 1) page = max(page, 1)
if v: if v:
@ -243,11 +242,9 @@ def changelog(v):
v=v, v=v,
) )
# check existence of next page
next_exists = (len(ids) > 25) next_exists = (len(ids) > 25)
ids = ids[:25] ids = ids[:25]
# check if ids exist
posts = get_posts(ids, v=v) posts = get_posts(ids, v=v)
if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists} if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists}

View File

@ -22,12 +22,10 @@ def login_get(v):
def check_for_alts(current_id): def check_for_alts(current_id):
# account history
past_accs = set(session.get("history", [])) past_accs = set(session.get("history", []))
past_accs.add(current_id) past_accs.add(current_id)
session["history"] = list(past_accs) session["history"] = list(past_accs)
# record alts
for past_id in session["history"]: for past_id in session["history"]:
if past_id == current_id: if past_id == current_id:
@ -95,7 +93,6 @@ def login_post():
time.sleep(random.uniform(0, 2)) time.sleep(random.uniform(0, 2))
return render_template("login.html", failed=True) return render_template("login.html", failed=True)
# test password
if request.values.get("password"): if request.values.get("password"):
@ -141,7 +138,6 @@ def login_post():
account.unban_utc = 0 account.unban_utc = 0
g.db.add(account) g.db.add(account)
# set session and user id
session["user_id"] = account.id session["user_id"] = account.id
session["session_id"] = token_hex(16) session["session_id"] = token_hex(16)
session["login_nonce"] = account.login_nonce session["login_nonce"] = account.login_nonce
@ -149,7 +145,6 @@ def login_post():
check_for_alts(account.id) check_for_alts(account.id)
# check for previous page
redir = request.values.get("redirect", "/").replace("/logged_out", "") redir = request.values.get("redirect", "/").replace("/logged_out", "")
@ -189,7 +184,6 @@ def sign_up_get(v):
agent = request.headers.get("User-Agent", None) agent = request.headers.get("User-Agent", None)
if not agent: abort(403) if not agent: abort(403)
# check for referral in link
ref = request.values.get("ref", None) ref = request.values.get("ref", None)
if ref: if ref:
ref_user = g.db.query(User).options(lazyload('*')).filter(User.username.ilike(ref)).first() ref_user = g.db.query(User).options(lazyload('*')).filter(User.username.ilike(ref)).first()
@ -200,14 +194,12 @@ def sign_up_get(v):
if ref_user and (ref_user.id in session.get("history", [])): if ref_user and (ref_user.id in session.get("history", [])):
return render_template("sign_up_failed_ref.html") return render_template("sign_up_failed_ref.html")
# Make a unique form key valid for one account creation
now = int(time.time()) now = int(time.time())
token = token_hex(16) token = token_hex(16)
session["signup_token"] = token session["signup_token"] = token
formkey_hashstr = str(now) + token + agent formkey_hashstr = str(now) + token + agent
# formkey is a hash of session token, timestamp, and IP address
formkey = hmac.new(key=bytes(environ.get("MASTER_KEY"), "utf-16"), formkey = hmac.new(key=bytes(environ.get("MASTER_KEY"), "utf-16"),
msg=bytes(formkey_hashstr, "utf-16"), msg=bytes(formkey_hashstr, "utf-16"),
digestmod='md5' digestmod='md5'
@ -258,8 +250,6 @@ def sign_up_post(v):
username = request.values.get("username").strip() username = request.values.get("username").strip()
# define function that takes an error message and generates a new signup
# form
def new_signup(error): def new_signup(error):
args = {"error": error} args = {"error": error}
@ -337,7 +327,6 @@ def sign_up_post(v):
if id_1 == 0 and users_count < 6: admin_level=6 if id_1 == 0 and users_count < 6: admin_level=6
else: admin_level=0 else: admin_level=0
# make new user
new_user = User( new_user = User(
username=username, username=username,
original_username = username, original_username = username,
@ -354,14 +343,11 @@ def sign_up_post(v):
g.db.add(new_user) g.db.add(new_user)
g.db.flush() g.db.flush()
# check alts
check_for_alts(new_user.id) check_for_alts(new_user.id)
# send welcome/verify email
if email: send_verification_email(new_user) if email: send_verification_email(new_user)
# send welcome message
if "rdrama" in request.host: send_notification(NOTIFICATIONS_ACCOUNT, new_user, "Dude bussy lmao") if "rdrama" in request.host: send_notification(NOTIFICATIONS_ACCOUNT, new_user, "Dude bussy lmao")
session["user_id"] = new_user.id session["user_id"] = new_user.id
@ -402,7 +388,6 @@ def post_forgot():
User.email.ilike(email)).first() User.email.ilike(email)).first()
if user: if user:
# generate url
now = int(time.time()) now = int(time.time())
token = generate_hash(f"{user.id}+{now}+forgot+{user.login_nonce}") token = generate_hash(f"{user.id}+{now}+forgot+{user.login_nonce}")
url = f"https://{app.config['SERVER_NAME']}/reset?id={user.id}&time={now}&token={token}" url = f"https://{app.config['SERVER_NAME']}/reset?id={user.id}&time={now}&token={token}"
@ -533,7 +518,6 @@ def request_2fa_disable():
title="Removal request received", title="Removal request received",
message="If username, password, and email match, we will send you an email.") message="If username, password, and email match, we will send you an email.")
#compute token
valid=int(time.time()) valid=int(time.time())
token=generate_hash(f"{user.id}+{user.username}+disable2fa+{valid}+{user.mfa_secret}+{user.login_nonce}") token=generate_hash(f"{user.id}+{user.username}+disable2fa+{valid}+{user.mfa_secret}+{user.login_nonce}")
@ -569,7 +553,6 @@ def reset_2fa():
if not validate_hash(f"{user.id}+{user.username}+disable2fa+{t}+{user.mfa_secret}+{user.login_nonce}", token): if not validate_hash(f"{user.id}+{user.username}+disable2fa+{t}+{user.mfa_secret}+{user.login_nonce}", token):
abort(403) abort(403)
#validation successful, remove 2fa
user.mfa_secret=None user.mfa_secret=None
g.db.add(user) g.db.add(user)

View File

@ -211,7 +211,6 @@ def edit_post(pid, v):
body_md = CustomRenderer().render(mistletoe.Document(body)) body_md = CustomRenderer().render(mistletoe.Document(body))
body_html = sanitize(body_md) body_html = sanitize(body_md)
# Run safety filter
bans = filter_comment_html(body_html) bans = filter_comment_html(body_html)
if bans: if bans:
ban = bans[0] ban = bans[0]
@ -219,7 +218,6 @@ def edit_post(pid, v):
if ban.reason: if ban.reason:
reason += f" {ban.reason}" reason += f" {ban.reason}"
#auto ban for digitally malicious content
if any([x.reason==4 for x in bans]): if any([x.reason==4 for x in bans]):
v.ban(days=30, reason="Digitally malicious content is not allowed.") v.ban(days=30, reason="Digitally malicious content is not allowed.")
abort(403) abort(403)
@ -347,7 +345,6 @@ def filter_title(title):
title = title.replace("\r", "") title = title.replace("\r", "")
title = title.replace("\t", "") title = title.replace("\t", "")
# sanitize title
title = bleach.clean(title, tags=[]) title = bleach.clean(title, tags=[])
for i in re.finditer('(?<!"):([^ ]{1,30}?):', title): for i in re.finditer('(?<!"):([^ ]{1,30}?):', title):
@ -370,7 +367,6 @@ def thumbnail_thread(pid):
def expand_url(post_url, fragment_url): def expand_url(post_url, fragment_url):
# convert src into full url
if fragment_url.startswith("https://"): if fragment_url.startswith("https://"):
return fragment_url return fragment_url
elif fragment_url.startswith("http://"): elif fragment_url.startswith("http://"):
@ -393,7 +389,6 @@ def thumbnail_thread(pid):
fetch_url=post.url fetch_url=post.url
#mimic chrome browser agent
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"} 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: try:
@ -407,17 +402,12 @@ def thumbnail_thread(pid):
return return
#if content is image, stick with that. Otherwise, parse html.
if x.headers.get("Content-Type","").startswith("text/html"): if x.headers.get("Content-Type","").startswith("text/html"):
#parse html, find image, load image
soup=BeautifulSoup(x.content, 'html.parser') soup=BeautifulSoup(x.content, 'html.parser')
#parse html
#create list of urls to check
thumb_candidate_urls=[] thumb_candidate_urls=[]
#iterate through desired meta tags
meta_tags = [ meta_tags = [
"ruqqus:thumbnail", "ruqqus:thumbnail",
"twitter:image", "twitter:image",
@ -446,12 +436,10 @@ def thumbnail_thread(pid):
if tag: if tag:
thumb_candidate_urls.append(expand_url(post.url, tag['content'])) thumb_candidate_urls.append(expand_url(post.url, tag['content']))
#parse html doc for <img> elements
for tag in soup.find_all("img", attrs={'src':True}): for tag in soup.find_all("img", attrs={'src':True}):
thumb_candidate_urls.append(expand_url(post.url, tag['src'])) thumb_candidate_urls.append(expand_url(post.url, tag['src']))
#now we have a list of candidate urls to try
for url in thumb_candidate_urls: for url in thumb_candidate_urls:
try: try:
@ -475,14 +463,12 @@ def thumbnail_thread(pid):
break break
else: else:
#getting here means we are out of candidate urls (or there never were any)
db.close() db.close()
return return
elif x.headers.get("Content-Type","").startswith("image/"): elif x.headers.get("Content-Type","").startswith("image/"):
#image is originally loaded fetch_url
image_req=x image_req=x
image = PILimage.open(BytesIO(x.content)) image = PILimage.open(BytesIO(x.content))
@ -569,7 +555,6 @@ def submit_post(v):
url = "" url = ""
body = request.values.get("body", "") body = request.values.get("body", "")
# check for duplicate
dup = g.db.query(Submission).options(lazyload('*')).filter( dup = g.db.query(Submission).options(lazyload('*')).filter(
Submission.author_id == v.id, Submission.author_id == v.id,
@ -583,13 +568,11 @@ def submit_post(v):
return redirect(dup.permalink) return redirect(dup.permalink)
# check for domain specific rules
parsed_url = urlparse(url) parsed_url = urlparse(url)
domain = parsed_url.netloc domain = parsed_url.netloc
# check ban status
domain_obj = get_domain(domain) domain_obj = get_domain(domain)
if domain_obj: if domain_obj:
if domain_obj.reason==4: if domain_obj.reason==4:
@ -620,7 +603,6 @@ def submit_post(v):
else: embed = None else: embed = None
# similarity check
now = int(time.time()) now = int(time.time())
cutoff = now - 60 * 60 * 24 cutoff = now - 60 * 60 * 24
@ -628,34 +610,18 @@ def submit_post(v):
similar_posts = g.db.query(Submission).options( similar_posts = g.db.query(Submission).options(
lazyload('*') lazyload('*')
).filter( ).filter(
#or_(
# and_(
Submission.author_id == v.id, Submission.author_id == v.id,
Submission.title.op('<->')(title) < app.config["SPAM_SIMILARITY_THRESHOLD"], Submission.title.op('<->')(title) < app.config["SPAM_SIMILARITY_THRESHOLD"],
Submission.created_utc > cutoff Submission.created_utc > cutoff
# ),
# and_(
# Submission.title.op('<->')(title) < app.config["SPAM_SIMILARITY_THRESHOLD"]/2,
# Submission.created_utc > cutoff
# )
#)
).all() ).all()
if url: if url:
similar_urls = g.db.query(Submission).options( similar_urls = g.db.query(Submission).options(
lazyload('*') lazyload('*')
).filter( ).filter(
#or_(
# and_(
Submission.author_id == v.id, Submission.author_id == v.id,
Submission.url.op('<->')(url) < app.config["SPAM_URL_SIMILARITY_THRESHOLD"], Submission.url.op('<->')(url) < app.config["SPAM_URL_SIMILARITY_THRESHOLD"],
Submission.created_utc > cutoff Submission.created_utc > cutoff
# ),
# and_(
# Submission.url.op('<->')(url) < app.config["SPAM_URL_SIMILARITY_THRESHOLD"]/2,
# Submission.created_utc > cutoff
# )
#)
).all() ).all()
else: else:
similar_urls = [] similar_urls = []
@ -692,7 +658,6 @@ def submit_post(v):
g.db.add(ma) g.db.add(ma)
return redirect("/notifications") return redirect("/notifications")
# catch too-long body
if len(str(body)) > 10000: if len(str(body)) > 10000:
if request.headers.get("Authorization"): return {"error":"10000 character limit for text body."}, 400 if request.headers.get("Authorization"): return {"error":"10000 character limit for text body."}, 400
@ -703,7 +668,6 @@ def submit_post(v):
if request.headers.get("Authorization"): return {"error":"2048 character limit for URLs."}, 400 if request.headers.get("Authorization"): return {"error":"2048 character limit for URLs."}, 400
else: return render_template("submit.html", v=v, error="2048 character limit for URLs.", title=title, url=url,body=request.values.get("body", "")), 400 else: return render_template("submit.html", v=v, error="2048 character limit for URLs.", title=title, url=url,body=request.values.get("body", "")), 400
# render text
for i in re.finditer('^(https:\/\/.*\.(png|jpg|jpeg|gif|webp|PNG|JPG|JPEG|GIF|WEBP|9999))', body, re.MULTILINE): for i in re.finditer('^(https:\/\/.*\.(png|jpg|jpeg|gif|webp|PNG|JPG|JPEG|GIF|WEBP|9999))', body, re.MULTILINE):
if "wikipedia" not in i.group(1): body = body.replace(i.group(1), f'![]({i.group(1)})') if "wikipedia" not in i.group(1): body = body.replace(i.group(1), f'![]({i.group(1)})')
body = re.sub('([^\n])\n([^\n])', r'\1\n\n\2', body) body = re.sub('([^\n])\n([^\n])', r'\1\n\n\2', body)
@ -720,7 +684,6 @@ def submit_post(v):
if len(body_html) > 20000: abort(400) if len(body_html) > 20000: abort(400)
# Run safety filter
bans = filter_comment_html(body_html) bans = filter_comment_html(body_html)
if bans: if bans:
ban = bans[0] ban = bans[0]
@ -728,7 +691,6 @@ def submit_post(v):
if ban.reason: if ban.reason:
reason += f" {ban.reason}" reason += f" {ban.reason}"
#auto ban for digitally malicious content
if any([x.reason==4 for x in bans]): if any([x.reason==4 for x in bans]):
v.ban(days=30, reason="Digitally malicious content is not allowed.") v.ban(days=30, reason="Digitally malicious content is not allowed.")
abort(403) abort(403)
@ -736,7 +698,6 @@ def submit_post(v):
if request.headers.get("Authorization"): return {"error": reason}, 403 if request.headers.get("Authorization"): return {"error": reason}, 403
else: return render_template("submit.html", v=v, error=reason, title=title, url=url, body=request.values.get("body", "")), 403 else: return render_template("submit.html", v=v, error=reason, title=title, url=url, body=request.values.get("body", "")), 403
# check for embeddable video
domain = parsed_url.netloc domain = parsed_url.netloc
if v.paid_dues: club = bool(request.values.get("club","")) if v.paid_dues: club = bool(request.values.get("club",""))

View File

@ -14,7 +14,6 @@ valid_params=[
def searchparse(text): def searchparse(text):
#takes test in filter:term format and returns data
criteria = {x[0]:x[1] for x in query_regex.findall(text)} criteria = {x[0]:x[1] for x in query_regex.findall(text)}

View File

@ -107,7 +107,6 @@ def settings_profile_post(v):
if "wikipedia" not in i.group(1): bio = bio.replace(i.group(1), f'![]({i.group(1)})') if "wikipedia" not in i.group(1): bio = bio.replace(i.group(1), f'![]({i.group(1)})')
bio = re.sub('([^\n])\n([^\n])', r'\1\n\n\2', bio) bio = re.sub('([^\n])\n([^\n])', r'\1\n\n\2', bio)
# check for uploaded image
if request.files.get('file') and request.headers.get("cf-ipcountry") != "T1": if request.files.get('file') and request.headers.get("cf-ipcountry") != "T1":
file = request.files['file'] file = request.files['file']
@ -123,7 +122,6 @@ def settings_profile_post(v):
bio_html = CustomRenderer().render(mistletoe.Document(bio)) bio_html = CustomRenderer().render(mistletoe.Document(bio))
bio_html = sanitize(bio_html) bio_html = sanitize(bio_html)
# Run safety filter
bans = filter_comment_html(bio_html) bans = filter_comment_html(bio_html)
if len(bio_html) > 10000: if len(bio_html) > 10000:
@ -137,7 +135,6 @@ def settings_profile_post(v):
if ban.reason: if ban.reason:
reason += f" {ban.reason}" reason += f" {ban.reason}"
#auto ban for digitally malicious content
if any([x.reason==4 for x in bans]): if any([x.reason==4 for x in bans]):
v.ban(days=30, reason="Digitally malicious content is not allowed.") v.ban(days=30, reason="Digitally malicious content is not allowed.")
return {"error": reason}, 401 return {"error": reason}, 401
@ -416,7 +413,6 @@ def settings_security_post(v):
if new_email == v.email: if new_email == v.email:
return redirect("/settings/security?error=That email is already yours!") return redirect("/settings/security?error=That email is already yours!")
# check to see if email is in use
existing = g.db.query(User).options(lazyload('*')).filter(User.id != v.id, existing = g.db.query(User).options(lazyload('*')).filter(User.id != v.id,
func.lower(User.email) == new_email.lower()).first() func.lower(User.email) == new_email.lower()).first()
if existing: if existing:
@ -492,10 +488,8 @@ def settings_log_out_others(v):
if not v.verifyPass(submitted_password): return render_template("settings_security.html", v=v, error="Incorrect Password"), 401 if not v.verifyPass(submitted_password): return render_template("settings_security.html", v=v, error="Incorrect Password"), 401
# increment account's nonce
v.login_nonce += 1 v.login_nonce += 1
# update cookie accordingly
session["login_nonce"] = v.login_nonce session["login_nonce"] = v.login_nonce
g.db.add(v) g.db.add(v)
@ -865,7 +859,6 @@ def settings_title_change(v):
new_name=request.values.get("title").strip()[:100].replace("𒐪","") new_name=request.values.get("title").strip()[:100].replace("𒐪","")
#make sure name is different
if new_name==v.customtitle: if new_name==v.customtitle:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,

View File

@ -100,7 +100,6 @@ def cached_chart():
comment_stats = [g.db.query(Comment.id).options(lazyload('*')).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.is_banned == False, Comment.author_id != 1).count() for i in range(len(day_cutoffs) - 1)][2:][::-1] comment_stats = [g.db.query(Comment.id).options(lazyload('*')).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.is_banned == False, Comment.author_id != 1).count() for i in range(len(day_cutoffs) - 1)][2:][::-1]
# create multiple charts
signup_chart = plt.subplot2grid((20, 4), (0, 0), rowspan=5, colspan=4) signup_chart = plt.subplot2grid((20, 4), (0, 0), rowspan=5, colspan=4)
posts_chart = plt.subplot2grid((20, 4), (7, 0), rowspan=5, colspan=4) posts_chart = plt.subplot2grid((20, 4), (7, 0), rowspan=5, colspan=4)
comments_chart = plt.subplot2grid((20, 4), (14, 0), rowspan=5, colspan=4) comments_chart = plt.subplot2grid((20, 4), (14, 0), rowspan=5, colspan=4)

View File

@ -395,13 +395,10 @@ def u_username(username, v=None):
if v and request.path.startswith('/logged_out'): v = None if v and request.path.startswith('/logged_out'): v = None
# username is unique so at most this returns one result. Otherwise 404
# case insensitive search
u = get_user(username, v=v) u = get_user(username, v=v)
# check for wrong cases
if username != u.username: if username != u.username:
return redirect(request.path.replace(username, u.username)) return redirect(request.path.replace(username, u.username))
@ -410,7 +407,6 @@ def u_username(username, v=None):
if request.headers.get("Authorization"): return {"error": f"That username is reserved for: {u.reserved}"} if request.headers.get("Authorization"): return {"error": f"That username is reserved for: {u.reserved}"}
else: return render_template("userpage_reserved.html", u=u, v=v) else: return render_template("userpage_reserved.html", u=u, v=v)
# viewers
if v and u.id != v.id: if v and u.id != v.id:
view = g.db.query(ViewerRelationship).options(lazyload('*')).filter( view = g.db.query(ViewerRelationship).options(lazyload('*')).filter(
and_( and_(
@ -457,7 +453,6 @@ def u_username(username, v=None):
ids = u.userpagelisting(v=v, page=page, sort=sort, t=t) ids = u.userpagelisting(v=v, page=page, sort=sort, t=t)
# we got 26 items just to see if a next page exists
next_exists = (len(ids) > 25) next_exists = (len(ids) > 25)
ids = ids[:25] ids = ids[:25]
@ -508,13 +503,10 @@ def u_username_comments(username, v=None):
if v and request.path.startswith('/logged_out'): v = None if v and request.path.startswith('/logged_out'): v = None
# username is unique so at most this returns one result. Otherwise 404
# case insensitive search
user = get_user(username, v=v) user = get_user(username, v=v)
# check for wrong cases
if username != user.username: return redirect(f'{user.url}/comments') if username != user.username: return redirect(f'{user.url}/comments')
@ -589,7 +581,6 @@ def u_username_comments(username, v=None):
comments = comments.offset(25 * (page - 1)).limit(26).all() comments = comments.offset(25 * (page - 1)).limit(26).all()
ids = [x.id for x in comments] ids = [x.id for x in comments]
# we got 26 items just to see if a next page exists
next_exists = (len(ids) > 25) next_exists = (len(ids) > 25)
ids = ids[:25] ids = ids[:25]
@ -624,7 +615,6 @@ def follow_user(username, v):
if target.id==v.id: return {"error": "You can't follow yourself!"}, 400 if target.id==v.id: return {"error": "You can't follow yourself!"}, 400
# check for existing follow
if g.db.query(Follow).options(lazyload('*')).filter_by(user_id=v.id, target_id=target.id).first(): return {"message": "User followed!"} if g.db.query(Follow).options(lazyload('*')).filter_by(user_id=v.id, target_id=target.id).first(): return {"message": "User followed!"}
new_follow = Follow(user_id=v.id, target_id=target.id) new_follow = Follow(user_id=v.id, target_id=target.id)
@ -650,7 +640,6 @@ def unfollow_user(username, v):
if target.id == 995: abort(403) if target.id == 995: abort(403)
# check for existing follow
follow = g.db.query(Follow).options(lazyload('*')).filter_by(user_id=v.id, target_id=target.id).first() follow = g.db.query(Follow).options(lazyload('*')).filter_by(user_id=v.id, target_id=target.id).first()
if not follow: return {"message": "User unfollowed!"} if not follow: return {"message": "User unfollowed!"}
@ -674,7 +663,6 @@ def unfollow_user(username, v):
def remove_follow(username, v): def remove_follow(username, v):
target = get_user(username) target = get_user(username)
# check for existing follow
follow = g.db.query(Follow).options(lazyload('*')).filter_by(user_id=target.id, target_id=v.id).first() follow = g.db.query(Follow).options(lazyload('*')).filter_by(user_id=target.id, target_id=v.id).first()
if not follow: return {"message": "Follower removed!"} if not follow: return {"message": "Follower removed!"}

View File

@ -62,14 +62,12 @@ def api_vote_post(post_id, new, v):
if new not in ["-1", "0", "1"]: abort(400) if new not in ["-1", "0", "1"]: abort(400)
# disallow bots
if request.headers.get("X-User-Type","") == "Bot": abort(403) if request.headers.get("X-User-Type","") == "Bot": abort(403)
new = int(new) new = int(new)
post = get_post(post_id) post = get_post(post_id)
# check for existing vote
existing = g.db.query(Vote).options(lazyload('*')).filter_by(user_id=v.id, submission_id=post.id).first() existing = g.db.query(Vote).options(lazyload('*')).filter_by(user_id=v.id, submission_id=post.id).first()
if existing and existing.vote_type == new: return "", 204 if existing and existing.vote_type == new: return "", 204
@ -127,7 +125,6 @@ def api_vote_comment(comment_id, new, v):
comment = get_comment(comment_id) comment = get_comment(comment_id)
# check for existing vote
existing = g.db.query(CommentVote).options(lazyload('*')).filter_by(user_id=v.id, comment_id=comment.id).first() existing = g.db.query(CommentVote).options(lazyload('*')).filter_by(user_id=v.id, comment_id=comment.id).first()
if existing and existing.vote_type == new: return "", 204 if existing and existing.vote_type == new: return "", 204