Merge branch 'frost' of https://github.com/Aevann1/rDrama into frost

master
Aevann1 2022-10-11 18:41:12 +02:00
commit 466867afd1
32 changed files with 301 additions and 544 deletions

View File

@ -218,6 +218,7 @@ function post_toast(t, url, button1, button2, classname, extra_actions) {
} else {
document.getElementById('toast-post-error-text').innerText = "Error, please try again later."
if (data && data["error"]) document.getElementById('toast-post-error-text').innerText = data["error"];
if (data && data["details"]) document.getElementById('toast-post-error-text').innerText = data["details"];
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
}
if (!isShopConfirm)

View File

@ -257,6 +257,73 @@ FEATURES = {
'PATRON_ICONS': False,
}
WERKZEUG_ERROR_DESCRIPTIONS = {
400: "The browser (or proxy) sent a request that this server could not understand.",
401: "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.",
403: "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.",
404: "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.",
405: "The method is not allowed for the requested URL.",
406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.",
409: "A conflict happened while processing the request. The resource might have been modified while the request was being processed.",
413: "The data value transmitted exceeds the capacity limit.",
414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.",
415: "The server does not support the media type transmitted in the request.",
417: "The server could not meet the requirements of the Expect header",
418: "This server is a teapot, not a coffee machine",
429: "This user has exceeded an allotted request count. Try again later.",
500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.",
}
ERROR_TITLES = {
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Too Many Pings",
409: "Conflict",
413: "Max image/audio size is 8 MB (16 MB for paypigs)",
414: "Max video size is 32 MB (64 MB for paypigs)",
415: "Unsupported Media Type",
417: "Image already exists!",
418: "WEBM videos are not allowed",
429: "Too Many Requests",
500: "Internal Server Error",
}
ERROR_MSGS = {
400: "That request was bad and you should feel bad.",
401: "What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.",
403: "YOU AREN'T WELCOME HERE GO AWAY",
404: "Someone typed something wrong and it was probably you, please do better.",
405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist<BR>the original error text here talked about internet gremlins and wtf",
406: "Max limit is 5 for comments and 50 for posts",
409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do. So maybe like... don't try and do that? Sorry not sorry",
413: "Max image/audio size is 8 MB (16 MB for paypigs)",
414: "Max video size is 32 MB (64 MB for paypigs)",
415: "Please upload only Image, Video, or Audio files!",
417: "Image already exists!",
418: "Please convert your video to MP4 and re-upload it!",
429: "go spam somewhere else nerd",
500: "Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page <I>but not other pages</I>, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily &lt;3",
}
ERROR_MARSEYS = {
400: "marseybrainlet",
401: "marseydead",
403: "marseytroll",
404: "marseyconfused",
405: "marseyretard",
406: "marseyrage",
409: "marseynoyou",
413: "marseyretard",
414: "marseychonker2",
415: "marseydetective",
418: "marseytea",
429: "marseyrentfree",
500: "marseycarp3",
}
EMOJI_MARSEYS = True
EMOJI_SRCS = ['files/assets/emojis.json']
@ -389,6 +456,8 @@ elif SITE == 'pcmemes.net':
PIN_LIMIT = 10
FEATURES['REPOST_DETECTION'] = False
FEATURES['GAMBLING'] = False
ERROR_MSGS[500] = "Hiiiii it's <b>nigger</b>! I think this error means that there's a <b>nigger</b> error. And I think that means something took too long to load so it decided to be a <b>nigger</b>. If you keep seeing this on the same page but not other pages, then something its probably a <b>niggerfaggot</b>. It may not be called a <b>nigger</b>, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily &lt;3"
ERROR_MARSEYS[500] = "wholesome"
POST_RATE_LIMIT = '1/second;4/minute;20/hour;100/day'
HOLE_COST = 2000

View File

@ -196,7 +196,7 @@ def remove_admin(v, username):
@admin_level_required(PERMS['POST_BETS_DISTRIBUTE'])
def distribute(v, option_id):
autojanny = get_account(AUTOJANNY_ID)
if autojanny.coins == 0: return {"error": "@AutoJanny has 0 coins"}, 400
if autojanny.coins == 0: abort(400, "@AutoJanny has 0 coins")
try: option_id = int(option_id)
except: abort(400)
@ -303,7 +303,7 @@ def revert_actions(v, username):
def club_allow(v, username):
u = get_user(username, v=v)
if u.admin_level >= v.admin_level: return {"error": "noob"}, 400
if u.admin_level >= v.admin_level: abort(403, 'noob')
u.club_allowed = True
g.db.add(u)
@ -329,7 +329,7 @@ def club_allow(v, username):
def club_ban(v, username):
u = get_user(username, v=v)
if u.admin_level >= v.admin_level: return {"error": "noob"}, 400
if u.admin_level >= v.admin_level: abort(403, 'noob')
u.club_allowed = False
@ -490,7 +490,7 @@ def purge_cache(v):
g.db.add(ma)
if response == "<Response [200]>": return {"message": "Cache purged!"}
return {"error": "Failed to purge cache."}, 400
abort(400, 'Failed to purge cache.')
@app.post("/admin/under_attack")
@ -507,7 +507,7 @@ def under_attack(v):
response = str(requests.patch(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, data='{"value":"high"}', timeout=5))
if response == "<Response [200]>": return {"message": "Under attack mode disabled!"}
return {"error": "Failed to disable under attack mode."}, 400
abort(400, "Failed to disable under attack mode.")
else:
ma = ModAction(
kind="enable_under_attack",
@ -517,7 +517,7 @@ def under_attack(v):
response = str(requests.patch(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, data='{"value":"under_attack"}', timeout=5))
if response == "<Response [200]>": return {"message": "Under attack mode enabled!"}
return {"error": "Failed to enable under attack mode."}, 400
abort(400, "Failed to enable under attack mode.")
@app.get("/admin/badge_grant")
@admin_level_required(PERMS['USER_BADGES'])
@ -1158,7 +1158,7 @@ def approve_post(post_id, v):
post = get_post(post_id)
if post.author.id == v.id and post.author.agendaposter and AGENDAPOSTER_PHRASE not in post.body.lower() and post.sub != 'chudrama':
return {"error": "You can't bypass the chud award!"}, 400
abort(400, "You can't bypass the chud award!")
if post.is_banned:
ma=ModAction(
@ -1223,7 +1223,7 @@ def sticky_post(post_id, v):
if v.admin_level >= PERMS['BYPASS_PIN_LIMIT']:
post.stickied = v.username
post.stickied_utc = int(time.time()) + 3600
else: return {"error": f"Can't exceed {PIN_LIMIT} pinned posts limit!"}, 403
else: abort(403, f"Can't exceed {PIN_LIMIT} pinned posts limit!")
else: post.stickied = v.username
g.db.add(post)
@ -1246,7 +1246,7 @@ def unsticky_post(post_id, v):
post = get_post(post_id)
if post.stickied:
if post.stickied.endswith('(pin award)'): return {"error": "Can't unpin award pins!"}, 403
if post.stickied.endswith('(pin award)'): abort(403, "Can't unpin award pins!")
post.stickied = None
post.stickied_utc = None
@ -1296,7 +1296,7 @@ def unsticky_comment(cid, v):
comment = get_comment(cid, v=v)
if comment.stickied:
if comment.stickied.endswith("(pin award)"): return {"error": "Can't unpin award pins!"}, 403
if comment.stickied.endswith("(pin award)"): abort(403, "Can't unpin award pins!")
comment.stickied = None
g.db.add(comment)
@ -1344,7 +1344,7 @@ def approve_comment(c_id, v):
if not comment: abort(404)
if comment.author.id == v.id and comment.author.agendaposter and AGENDAPOSTER_PHRASE not in comment.body.lower() and comment.post.sub != 'chudrama':
return {"error": "You can't bypass the chud award!"}, 400
abort(400, "You can't bypass the chud award!")
if comment.is_banned:
ma=ModAction(

View File

@ -96,27 +96,27 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'):
@admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_MARSEYS'])
def approve_marsey(v, name):
if AEVANN_ID and v.id not in (AEVANN_ID, CARP_ID, SNAKES_ID):
return {"error": "Only Carp can approve marseys!"}, 403
abort(403, "Only Carp can approve marseys!")
name = name.lower().strip()
marsey = g.db.query(Marsey).filter_by(name=name).one_or_none()
if not marsey:
return {"error": f"This marsey '{name}' doesn't exist!"}, 404
abort(404, f"This marsey '{name}' doesn't exist!")
tags = request.values.get('tags').lower().strip()
if not tags:
return {"error": "You need to include tags!"}, 400
abort(400, "You need to include tags!")
new_name = request.values.get('name').lower().strip()
if not new_name:
return {"error": "You need to include name!"}, 400
abort(400, "You need to include name!")
if not marsey_regex.fullmatch(new_name):
return {"error": "Invalid name!"}, 400
abort(400, "Invalid name!")
if not tags_regex.fullmatch(tags):
return {"error": "Invalid tags!"}, 400
abort(400, "Invalid tags!")
marsey.name = new_name
@ -168,10 +168,10 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'):
marsey = g.db.query(Marsey).filter_by(name=name).one_or_none()
if not marsey:
return {"error": f"This marsey '{name}' doesn't exist!"}, 404
abort(404, f"This marsey '{name}' doesn't exist!")
if v.id not in (marsey.submitter_id, AEVANN_ID, CARP_ID):
return {"error": "Only Carp can remove marseys!"}, 403
abort(403, "Only Carp can remove marseys!")
if v.id != marsey.submitter_id:
msg = f"@{v.username} has rejected a marsey you submitted: `'{marsey.name}'`"
@ -257,27 +257,20 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'):
@admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_HATS'])
def approve_hat(v, name):
if AEVANN_ID and v.id not in (AEVANN_ID, CARP_ID, SNAKES_ID):
return {"error": "Only Carp can approve hats!"}, 403
abort(403, "Only Carp can approve hats!")
name = name.strip()
hat = g.db.query(HatDef).filter_by(name=name).one_or_none()
if not hat:
return {"error": f"This hat '{name}' doesn't exist!"}, 404
if not hat: abort(404, f"This hat '{name}' doesn't exist!")
description = request.values.get('description').strip()
if not description:
return {"error": "You need to include description!"}, 400
if not description: abort(400, "You need to include a description!")
new_name = request.values.get('name').strip()
if not new_name:
return {"error": "You need to include name!"}, 400
if not hat_regex.fullmatch(new_name):
return {"error": "Invalid name!"}, 400
if not description_regex.fullmatch(description):
return {"error": "Invalid description!"}, 400
if not new_name: abort(400, "You need to include a name!")
if not hat_regex.fullmatch(new_name): abort(400, "Invalid name!")
if not description_regex.fullmatch(description): abort(400, "Invalid description!")
hat.price = int(request.values.get('price'))
hat.name = new_name
@ -332,11 +325,9 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'):
name = name.strip()
hat = g.db.query(HatDef).filter_by(name=name).one_or_none()
if not hat:
return {"error": f"This hat '{name}' doesn't exist!"}, 404
if not hat: abort(404, f"This hat '{name}' doesn't exist!")
if v.id not in (hat.submitter_id, AEVANN_ID, CARP_ID):
return {"error": "Only Carp can remove hats!"}, 403
abort(403, 'Only Carp can remove hats!')
if v.id != hat.submitter_id:
msg = f"@{v.username} has rejected a hat you submitted: `'{hat.name}'`"

View File

@ -49,10 +49,10 @@ def buy(v, award):
if award == 'benefactor' and not request.values.get("mb"):
return {"error": "You can only buy the Benefactor award with marseybux."}, 403
abort(403, "You can only buy the Benefactor award with marseybux.")
if award == 'ghost' and v.admin_level < PERMS['BUY_GHOST_AWARD']:
return {"error": "Only admins can buy this award."}, 403
abort(403, "Only admins can buy this award")
AWARDS = deepcopy(AWARDS2)
@ -67,15 +67,15 @@ def buy(v, award):
if request.values.get("mb"):
if award == "grass":
return {"error": "You can't buy the grass award with marseybux."}, 403
abort(403, "You can't buy the grass award with marseybux.")
charged = v.charge_account('procoins', price)
if not charged:
return {"error": "Not enough marseybux."}, 400
abort(400, "Not enough marseybux.")
else:
charged = v.charge_account('coins', price)
if not charged:
return {"error": "Not enough coins."}, 400
abort(400, "Not enough coins.")
v.coins_spent += price
if v.coins_spent >= 1000000:
@ -129,8 +129,6 @@ def buy(v, award):
@is_not_permabanned
@feature_required('BADGES')
def award_thing(v, thing_type, id):
if thing_type == 'post': thing = get_post(id)
else: thing = get_comment(id)
@ -142,8 +140,7 @@ def award_thing(v, thing_type, id):
if v.house:
AWARDS[v.house] = HOUSE_AWARDS[v.house]
if kind not in AWARDS:
return {"error": "This award doesn't exist."}, 404
if kind not in AWARDS: abort(404, "This award doesn't exist")
award = g.db.query(AwardRelationship).filter(
AwardRelationship.kind == kind,
@ -152,8 +149,7 @@ def award_thing(v, thing_type, id):
AwardRelationship.comment_id == None
).first()
if not award:
return {"error": "You don't have that award."}, 404
if not award: abort(404, "You don't have that award")
if thing_type == 'post': award.submission_id = thing.id
else: award.comment_id = thing.id
@ -167,13 +163,13 @@ def award_thing(v, thing_type, id):
if author.shadowbanned: abort(404)
if SITE == 'rdrama.net' and author.id in (PIZZASHILL_ID, CARP_ID):
return {"error": "This user is immune to awards."}, 403
abort(403, "This user is immune to awards.")
if kind == "benefactor" and author.id == v.id:
return {"error": "You can't use this award on yourself."}, 400
abort(400, "You can't use this award on yourself.")
if kind == 'marsify' and author.marsify == 1:
return {"error": "User is already permanently marsified!"}, 403
abort(403, "User is already permanently marsified!")
if v.id != author.id:
safe_username = "👻" if thing.ghost else f"@{author.username}"
@ -254,7 +250,7 @@ def award_thing(v, thing_type, id):
g.db.add(thing)
elif kind == "agendaposter" and not (author.agendaposter and author.agendaposter == 0):
if author.marseyawarded:
return {"error": "This user is the under the effect of a conflicting award: Marsey award."}, 404
abort(400, "This user is under the effect of a conflicting award: Marsey award.")
if author.agendaposter and time.time() < author.agendaposter: author.agendaposter += 86400
else: author.agendaposter = int(time.time()) + 86400
@ -284,13 +280,13 @@ def award_thing(v, thing_type, id):
badge_grant(user=author, badge_id=98)
elif kind == "pizzashill":
if author.bird:
return {"error": "This user is the under the effect of a conflicting award: Bird Site award."}, 404
abort(400, "This user is under the effect of a conflicting award: Bird Site award.")
if author.longpost: author.longpost += 86400
else: author.longpost = int(time.time()) + 86400
badge_grant(user=author, badge_id=97)
elif kind == "bird":
if author.longpost:
return {"error": "This user is the under the effect of a conflicting award: Pizzashill award."}, 404
abort(400, "This user is under the effect of a conflicting award: Pizzashill award.")
if author.bird: author.bird += 86400
else: author.bird = int(time.time()) + 86400
badge_grant(user=author, badge_id=95)
@ -317,7 +313,7 @@ def award_thing(v, thing_type, id):
else: author.progressivestack = int(time.time()) + 21600
badge_grant(user=author, badge_id=94)
elif kind == "benefactor":
if author.patron: return {"error": "This user is already a paypig!"}, 400
if author.patron: abort(400, "This user is already a paypig!")
author.patron = 1
if author.patron_utc: author.patron_utc += 2629746
else: author.patron_utc = int(time.time()) + 2629746

View File

@ -55,8 +55,8 @@ def casino_game_page(v, game):
@auth_required
@feature_required('GAMBLING')
def casino_game_feed(v, game):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
if v.rehab:
abort(403, "You are under Rehab award effect!")
elif game not in CASINO_GAME_KINDS:
abort(404)
@ -83,27 +83,27 @@ def lottershe(v):
@feature_required('GAMBLING')
def pull_slots(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
wager = int(request.values.get("wager"))
except:
return {"error": "Invalid wager."}, 400
abort(400, "Invalid wager.")
try:
currency = request.values.get("currency")
except:
return {"error": "Invalid currency (expected 'coin' or 'marseybux')."}, 400
abort(400, "Invalid currency (expected 'coin' or 'marseybux').")
if (currency == "coin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins):
return {"error": f"Not enough {currency} to make that bet."}, 400
abort(400, f"Not enough {currency} to make that bet")
success, game_state = casino_slot_pull(v, wager, currency)
if success:
return {"game_state": game_state, "gambler": {"coins": v.coins, "procoins": v.procoins}}
else:
return {"error": f"Wager must be more than 5 {currency}."}, 400
abort(400, f"Wager must be more than 5 {currency}")
# 21
@ -113,7 +113,7 @@ def pull_slots(v):
@feature_required('GAMBLING')
def blackjack_deal_to_player(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
wager = int(request.values.get("wager"))
@ -124,7 +124,7 @@ def blackjack_deal_to_player(v):
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except Exception as e:
return {"error": str(e)}, 400
abort(400, str(e))
@app.post("/casino/twentyone/hit")
@ -133,14 +133,14 @@ def blackjack_deal_to_player(v):
@feature_required('GAMBLING')
def blackjack_player_hit(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
state = dispatch_action(v, BlackjackAction.HIT)
feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except:
return {"error": "Unable to hit."}, 400
abort(400, "Unable to hit.")
@app.post("/casino/twentyone/stay")
@ -149,14 +149,14 @@ def blackjack_player_hit(v):
@feature_required('GAMBLING')
def blackjack_player_stay(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
state = dispatch_action(v, BlackjackAction.STAY)
feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except:
return {"error": "Unable to stay."}, 400
abort(400, "Unable to stay.")
@app.post("/casino/twentyone/double-down")
@ -165,14 +165,14 @@ def blackjack_player_stay(v):
@feature_required('GAMBLING')
def blackjack_player_doubled_down(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
state = dispatch_action(v, BlackjackAction.DOUBLE_DOWN)
feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except:
return {"error": "Unable to double down."}, 400
abort(400, "Unable to double down.")
@app.post("/casino/twentyone/buy-insurance")
@ -181,14 +181,14 @@ def blackjack_player_doubled_down(v):
@feature_required('GAMBLING')
def blackjack_player_bought_insurance(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
state = dispatch_action(v, BlackjackAction.BUY_INSURANCE)
feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except:
return {"error": "Unable to buy insurance."}, 400
abort(403, "Unable to buy insurance.")
# Roulette
@app.get("/casino/roulette/bets")
@ -197,7 +197,7 @@ def blackjack_player_bought_insurance(v):
@feature_required('GAMBLING')
def roulette_get_bets(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
bets = get_roulette_bets()
@ -210,7 +210,7 @@ def roulette_get_bets(v):
@feature_required('GAMBLING')
def roulette_player_placed_bet(v):
if v.rehab:
return {"error": "You are under Rehab award effect!"}, 400
abort(403, "You are under Rehab award effect!")
try:
bet = request.values.get("bet")
@ -227,4 +227,4 @@ def roulette_player_placed_bet(v):
return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except:
return {"error": "Unable to place a bet."}, 400
abort(400, "Unable to place a bet.")

View File

@ -56,7 +56,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None):
post = get_post(pid, v=v)
if post.over_18 and not (v and v.over_18) and not session.get('over_18', 0) >= int(time.time()):
if request.headers.get("Authorization"): return {"error": 'This content is not suitable for some users and situations.'}, 403
if request.headers.get("Authorization"): abort(403, "This content is not suitable for some users and situations.")
else: return render_template("errors/nsfw.html", v=v)
try: context = min(int(request.values.get("context", 0)), 8)
@ -126,17 +126,17 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None):
@limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
def comment(v):
if v.is_suspended: return {"error": "You can't perform this action while banned."}, 403
if v.is_suspended: abort(403, "You can't perform this action while banned.")
parent_submission = request.values.get("submission").strip()
parent_fullname = request.values.get("parent_fullname").strip()
parent_post = get_post(parent_submission, v=v)
sub = parent_post.sub
if sub and v.exiled_from(sub): return {"error": f"You're exiled from /h/{sub}"}, 403
if sub and v.exiled_from(sub): abort(403, f"You're exiled from /h/{sub}")
if sub in ('furry','vampire','racist','femboy') and not v.client and not v.house.lower().startswith(sub):
return {"error": f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}"}, 400
abort(403, f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}")
if parent_post.club and not (v and (v.paid_dues or v.id == parent_post.author_id)): abort(403)
@ -157,18 +157,17 @@ def comment(v):
if not parent.can_see(v): abort(404)
if parent.deleted_utc != 0: abort(404)
if level > COMMENT_MAX_DEPTH:
return {"error": f"Max comment level is {COMMENT_MAX_DEPTH}"}, 400
if level > COMMENT_MAX_DEPTH: abort(400, f"Max comment level is {COMMENT_MAX_DEPTH}")
body = sanitize_raw_body(request.values.get("body", ""), False)
if parent_post.id not in ADMIGGERS:
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
return {"error":"You have to type more than 280 characters!"}, 403
abort(403, "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!"}, 403
abort(403, "You have to type less than 140 characters!")
if not body and not request.files.get('file'): return {"error":"You need to actually write something!"}, 400
if not body and not request.files.get('file'): abort(400, "You need to actually write something!")
options = []
for i in poll_regex.finditer(body):
@ -187,7 +186,7 @@ def comment(v):
oldname = f'/images/{time.time()}'.replace('.','') + '.webp'
file.save(oldname)
image = process_image(oldname, patron=v.patron)
if image == "": return {"error":"Image upload failed"}, 400
if image == "": abort(400, "Image upload failed")
if v.admin_level >= PERMS['SITE_SETTINGS_SIDEBARS_BANNERS_BADGES'] and level == 1:
if parent_post.id == SIDEBAR_THREAD:
li = sorted(os.listdir(f'files/assets/images/{SITE_NAME}/sidebar'),
@ -210,7 +209,7 @@ def comment(v):
name = badge_def["name"]
existing = g.db.query(BadgeDef).filter_by(name=name).one_or_none()
if existing: return {"error": "A badge with this name already exists!"}, 403
if existing: abort(403, "A badge with this name already exists!")
badge = BadgeDef(name=name, description=badge_def["description"])
g.db.add(badge)
@ -221,7 +220,7 @@ def comment(v):
requests.post(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS,
data=f'{{"files": ["https://{SITE}/assets/images/badges/{badge.id}.webp"]}}', timeout=5)
except Exception as e:
return {"error": str(e)}, 400
abort(400, str(e))
body += f"\n\n![]({image})"
elif file.content_type.startswith('video/'):
body += f"\n\n{process_video(file)}"
@ -257,7 +256,7 @@ def comment(v):
if existing: return {"error": f"You already made that comment: /comment/{existing.id}"}, 409
if parent.author.any_block_exists(v) and v.admin_level < PERMS['POST_COMMENT_MODERATION']:
return {"error": "You can't reply to users who have blocked you or users that you have blocked."}, 403
abort(403, "You can't reply to users who have blocked you or users that you have blocked.")
is_bot = v.id != BBBB_ID and (bool(request.headers.get("Authorization")) or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID))
@ -297,7 +296,7 @@ def comment(v):
g.db.add(ma)
g.db.commit()
return {"error": "Too much spam!"}, 403
abort(403, "Too much spam!")
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
@ -442,7 +441,7 @@ def comment(v):
g.db.add(c)
if v.marseyawarded and parent_post.id not in ADMIGGERS and marseyaward_body_regex.search(body_html):
return {"error":"You can only type marseys!"}, 403
abort(403, "You can only type marseys!")
check_for_treasure(body, c)
@ -469,7 +468,7 @@ def edit_comment(cid, v):
c = get_comment(cid, v=v)
if time.time() - c.created_utc > 7*24*60*60 and not (c.post and c.post.private):
return {"error":"You can't edit comments older than 1 week!"}, 403
abort(403, "You can't edit comments older than 1 week!")
if c.author_id != v.id: abort(403)
if not c.post: abort(403)
@ -477,13 +476,13 @@ def edit_comment(cid, v):
body = sanitize_raw_body(request.values.get("body", ""), False)
if len(body) < 1 and not (request.files.get("file") and request.headers.get("cf-ipcountry") != "T1"):
return {"error":"You have to actually type something!"}, 400
abort(400, "You have to actually type something!")
if body != c.body or request.files.get("file") and request.headers.get("cf-ipcountry") != "T1":
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
return {"error":"You have to type more than 280 characters!"}, 403
abort(403, "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!"}, 403
abort(403, "You have to type less than 140 characters!")
for i in poll_regex.finditer(body):
body = body.replace(i.group(0), "")
@ -535,7 +534,7 @@ def edit_comment(cid, v):
g.db.add(comment)
g.db.commit()
return {"error": "Too much spam!"}, 403
abort(403, "Too much spam!")
body += process_files()
body = body.strip()[:COMMENT_BODY_LENGTH_LIMIT] # process_files potentially adds characters to the post
@ -553,7 +552,7 @@ def edit_comment(cid, v):
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
if v.marseyawarded and marseyaward_body_regex.search(body_html):
return {"error":"You can only type marseys!"}, 403
abort(403, "You can only type marseys!")
c.body = body
c.body_html = body_html
@ -568,7 +567,7 @@ def edit_comment(cid, v):
g.db.add(notif)
if v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and c.post.sub != 'chudrama':
return {"error": f'You have to include "{AGENDAPOSTER_PHRASE}" in your comment!'}, 403
abort(403, f'You have to include "{AGENDAPOSTER_PHRASE}" in your comment!')
if int(time.time()) - c.created_utc > 60 * 3: c.edited_utc = int(time.time())
@ -745,7 +744,6 @@ def diff_words(answer, guess):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
def handle_wordle_action(cid, v):
comment = get_comment(cid)
if v.id != comment.author_id:
@ -757,8 +755,7 @@ def handle_wordle_action(cid, v):
try: guess = request.values.get("thing").strip().lower()
except: abort(400)
if len(guess) != 5:
return {"error": "Not a valid guess!"}, 400
if len(guess) != 5: abort(400, "Not a valid guess!")
if status == "active":
guesses += "".join(cg + WORDLE_COLOR_MAPPINGS[diff] for cg, diff in zip(guess, diff_words(answer, guess)))

View File

@ -8,15 +8,10 @@ import requests
@app.get("/discord")
@is_not_permabanned
def join_discord(v):
if v.shadowbanned: return {"error": "Internal server error"}, 400
if v.shadowbanned: return {"error": "Internal Server Error"}, 500
now=int(time.time())
state=generate_hash(f"{now}+{v.id}+discord")
state=f"{now}.{state}"
return redirect(f"https://discord.com/api/oauth2/authorize?client_id={DISCORD_CLIENT_ID}&redirect_uri=https%3A%2F%2F{SITE}%2Fdiscord_redirect&response_type=code&scope=identify%20guilds.join&state={state}")

View File

@ -4,17 +4,39 @@ from urllib.parse import quote, urlencode
import time
from files.__main__ import app, limiter
# If you're adding an error, go here:
# https://github.com/pallets/werkzeug/blob/main/src/werkzeug/exceptions.py
# and copy the description for the error code you're adding and add it to
# the constant WERKZEUG_ERROR_DESCRIPTIONS so that the default error message
# doesn't show up on the message. Be exact or it won't work properly.
@app.errorhandler(400)
def error_400(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "400 Bad Request"}, 400
else: return render_template('errors/400.html', err=True), 400
@app.errorhandler(403)
@app.errorhandler(404)
@app.errorhandler(405)
@app.errorhandler(406)
@app.errorhandler(409)
@app.errorhandler(413)
@app.errorhandler(414)
@app.errorhandler(415)
@app.errorhandler(417)
@app.errorhandler(418)
@app.errorhandler(429)
def error(e):
title = ERROR_TITLES.get(e.code, str(e.code))
msg = ERROR_MSGS.get(e.code, str(e.code))
details = e.description
if WERKZEUG_ERROR_DESCRIPTIONS.get(e.code, None) == details:
details = None
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"error": title, "code": e.code, "description": msg, "details": details}
img = ERROR_MARSEYS.get(e.code, 'marseyl')
return render_template('errors/error.html', err=True, title=title, msg=msg, details=details, img=img), e.code
@app.errorhandler(401)
def error_401(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "401 Not Authorized"}, 401
if request.headers.get("Authorization") or request.headers.get("xhr"): return error(e)
else:
path = request.path
qs = urlencode(dict(request.values))
@ -23,75 +45,10 @@ def error_401(e):
if session.get("history"): return redirect(f"/login?redirect={argval}")
else: return redirect(f"/signup?redirect={argval}")
@app.errorhandler(406)
def error_406(e):
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"error": "Too many pings: max limit is 5 for comments and 50 for posts"}, 406
else: return render_template('errors/406.html', err=True), 406
@app.errorhandler(403)
def error_403(e):
description = e.description
if description == "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.": description = ''
if request.headers.get("Authorization") or request.headers.get("xhr"):
if not description: description = "403 Forbidden"
return {"error": description}, 403
else:
if not description: description = "YOU AREN'T WELCOME HERE GO AWAY"
return render_template('errors/403.html', description=description, err=True), 403
@app.errorhandler(404)
def error_404(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "404 Not Found"}, 404
else: return render_template('errors/404.html', err=True), 404
@app.errorhandler(405)
def error_405(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "405 Method Not Allowed"}, 405
else: return render_template('errors/405.html', err=True), 405
@app.errorhandler(413)
def error_413(e):
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"error": "Max image/audio size is 8 MB (16 MB for paypigs)"}, 413
else: return render_template('errors/413.html', err=True), 413
@app.errorhandler(414)
def error_414(e):
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"error": "Max video size is 32 MB (64 MB for paypigs)"}, 414
else: return render_template('errors/414.html', err=True), 414
@app.errorhandler(415)
def error_415(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "Please upload only Image, Video, or Audio files!"}, 415
else: return render_template('errors/415.html', err=True), 415
@app.errorhandler(417)
def error_417(e):
return {"error": "Image already exists!"}, 417
@app.errorhandler(418)
def error_418(e):
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"error": "WEBM videos are not allowed, please convert your video to MP4 and re-upload it!"}, 418
else: return render_template('errors/418.html', err=True), 418
@app.errorhandler(429)
def error_429(e):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "429 Too Many Requests"}, 429
else: return render_template('errors/429.html', err=True), 429
@app.errorhandler(500)
def error_500(e):
g.db.rollback()
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "500 Internal Server Error"}, 500
else: return render_template('errors/500.html', err=True), 500
return error(e)
@app.post("/allow_nsfw")

View File

@ -36,25 +36,23 @@ def hats(v):
@feature_required('HATS')
def buy_hat(v, hat_id):
try: hat_id = int(hat_id)
except: return {"error": "Hat not found!"}, 400
except: abort(404, "Hat not found!")
hat = g.db.query(HatDef).filter_by(submitter_id=None, id=hat_id).one_or_none()
if not hat: return {"error": "Hat not found!"}, 400
if not hat: abort(404, "Hat not found!")
existing = g.db.query(Hat).filter_by(user_id=v.id, hat_id=hat.id).one_or_none()
if existing: return {"error": "You already own this hat!"}, 400
if existing: abort(400, "You already own this hat!")
if request.values.get("mb"):
charged = v.charge_account('procoins', hat.price)
if not charged:
return {"error": "Not enough marseybux."}, 400
if not charged: abort(400, "Not enough marseybux.")
hat.author.procoins += hat.price * 0.1
currency = "marseybux"
else:
charged = v.charge_account('coins', hat.price)
if not charged:
return {"error": "Not enough coins."}, 400
if not charged: abort(400, "Not enough coins.")
v.coins_spent_on_hats += hat.price
hat.author.coins += hat.price * 0.1
@ -86,10 +84,10 @@ def buy_hat(v, hat_id):
@feature_required('HATS')
def equip_hat(v, hat_id):
try: hat_id = int(hat_id)
except: return {"error": "Hat not found!"}, 400
except: abort(404, "Hat not found!")
hat = g.db.query(Hat).filter_by(hat_id=hat_id, user_id=v.id).one_or_none()
if not hat: return {"error": "You don't own this hat!"}, 400
if not hat: abort(403, "You don't own this hat!")
hat.equipped = True
g.db.add(hat)
@ -101,10 +99,10 @@ def equip_hat(v, hat_id):
@feature_required('HATS')
def unequip_hat(v, hat_id):
try: hat_id = int(hat_id)
except: return {"error": "Hat not found!"}, 400
except: abort(404, "Hat not found!")
hat = g.db.query(Hat).filter_by(hat_id=hat_id, user_id=v.id).one_or_none()
if not hat: return {"error": "You don't own this hat!"}, 400
if not hat: abort(403, "You don't own this hat!")
hat.equipped = False
g.db.add(hat)

View File

@ -29,7 +29,7 @@ def lottery_start(v):
@feature_required('GAMBLING')
def lottery_buy(v):
try: quantity = int(request.values.get("quantity"))
except: return {"error": "Invalid ticket quantity."}, 400
except: abort(400, "Invalid ticket quantity.")
success, message = purchase_lottery_tickets(v, quantity)
lottery, participants = get_active_lottery_stats()

View File

@ -19,10 +19,10 @@ def vote_option(option_id, v):
sub = option.post.sub
if sub in ('furry','vampire','racist','femboy') and not v.house.lower().startswith(sub):
return {"error": f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}"}, 400
abort(403, f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}")
if option.exclusive == 2:
if v.coins < POLL_BET_COINS: return {"error": f"You don't have {POLL_BET_COINS} coins!"}, 400
if v.coins < POLL_BET_COINS: abort(400, f"You don't have {POLL_BET_COINS} coins!")
v.coins -= POLL_BET_COINS
g.db.add(v)
autojanny = get_account(AUTOJANNY_ID)
@ -35,7 +35,7 @@ def vote_option(option_id, v):
SubmissionOptionVote.submission_id==option.submission_id,
SubmissionOption.exclusive==option.exclusive).one_or_none()
if vote:
if option.exclusive == 2: return {"error": "You already voted on this bet!"}, 400
if option.exclusive == 2: abort(400, "You already voted on this bet!")
g.db.delete(vote)
existing = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none()
@ -85,7 +85,7 @@ def vote_option_comment(option_id, v):
sub = option.comment.post.sub
if sub in ('furry','vampire','racist','femboy') and not v.house.lower().startswith(sub):
return {"error": f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}"}, 400
abort(403, f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}")
if option.exclusive:
vote = g.db.query(CommentOptionVote).join(CommentOption).filter(

View File

@ -414,26 +414,26 @@ def edit_post(pid, v):
# 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']):
return {"error": "You can't edit posts older than 1 week!"}, 403
abort(403, "You can't edit posts older than 1 week!")
title = sanitize_raw_title(request.values.get("title", ""))
body = sanitize_raw_body(request.values.get("body", ""), True)
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
abort(403, "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!"}, 403
abort(403, "You have to type less than 140 characters!")
if not title:
return {"error": "Please enter a better title."}, 400
abort(400, "Please enter a better title.")
if title != p.title:
torture = (v.agendaposter and not v.marseyawarded and p.sub != 'chudrama' and v.id == p.author_id)
title_html = filter_emojis_only(title, golden=False, torture=torture)
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
abort(403, "You can only type marseys!")
p.title = title
p.title_html = title_html
@ -466,7 +466,7 @@ def edit_post(pid, v):
body_html = sanitize(body, golden=False, limit_pings=100, showmore=False, torture=torture)
if v.id == p.author_id and v.marseyawarded and marseyaward_body_regex.search(body_html):
return {"error":"You can only type marseys!"}, 403
abort(403, "You can only type marseys!")
p.body = body
@ -477,12 +477,13 @@ def edit_post(pid, v):
g.db.add(v)
send_repeatable_notification(CARP_ID, p.permalink)
if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT: return {"error":f"Submission body_html too long! (max {POST_BODY_HTML_LENGTH_LIMIT} characters)"}, 400
if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT:
abort(400, f"Submission body_html too long! (max {POST_BODY_HTML_LENGTH_LIMIT} characters)")
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 p.sub != 'chudrama':
return {"error": f'You have to include "{AGENDAPOSTER_PHRASE}" in your post!'}, 403
abort(403, f'You have to include "{AGENDAPOSTER_PHRASE}" in your post!')
if not p.private and not p.ghost:
@ -702,7 +703,7 @@ def submit_post(v, sub=None):
body = sanitize_raw_body(request.values.get("body", ""), True)
def error(error):
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": error}, 400
if request.headers.get("Authorization") or request.headers.get("xhr"): abort(400, error)
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
@ -1204,7 +1205,7 @@ def pin_post(post_id, v):
post = get_post(post_id)
if post:
if v.id != post.author_id: return {"error": "Only the post author's can do that!"}, 400
if v.id != post.author_id: abort(400, "Only the post author's can do that!")
post.is_pinned = not post.is_pinned
g.db.add(post)
@ -1212,7 +1213,7 @@ def pin_post(post_id, v):
if post.is_pinned: return {"message": "Post pinned!"}
else: return {"message": "Post unpinned!"}
return {"error": "Post not found!"}, 400
return abort(404, "Post not found!")
extensions = (

View File

@ -21,14 +21,13 @@ def flag_post(pid, v):
if not v.is_banned: v.ban_reason = 'Blackjack'
send_repeatable_notification(CARP_ID, f"reports on {post.permalink}")
if v.is_muted:
return {"error": "You are forbidden from making reports."}, 400
if v.is_muted: abort(400, "You are forbidden from making reports.")
reason = reason[:100]
reason = filter_emojis_only(reason)
if len(reason) > 350: return {"error": "Too long."}, 400
if len(reason) > 350: abort(400, "Too long.")
if reason.startswith('!') and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or post.sub and v.mods(post.sub)):
post.flair = reason[1:]
@ -58,16 +57,16 @@ def flag_post(pid, v):
sub_to = g.db.get(Sub, sub_to)
sub_to = sub_to.name if sub_to else None
if sub_from == sub_to: {"error": f"Post is already in /h/{sub_to}"}, 400
if sub_from == sub_to: abort(400, f"Post is already in /h/{sub_to}")
if post.author.exiled_from(sub_to):
return {"error": f"User is exiled from this {HOLE_NAME}!"}, 400
abort(400, f"User is exiled from this {HOLE_NAME}!")
if sub_to in ('furry','vampire','racist','femboy') and not v.client and not post.author.house.lower().startswith(sub_to):
if v.id == post.author_id:
return {"error": f"You need to be a member of House {sub.capitalize()} to post in /h/{sub}"}, 403
abort(403, f"You need to be a member of House {sub_to.capitalize()} to post in /h/{sub_to}")
else:
return {"error": f"@{post.author.username} needs to be a member of House {sub.capitalize()} for their post to be moved to /h/{sub}"}, 400
abort(403, f"@{post.author.username} needs to be a member of House {sub_to.capitalize()} for their post to be moved to /h/{sub_to}")
post.sub = sub_to
g.db.add(post)
@ -103,8 +102,7 @@ def flag_post(pid, v):
return {"message": f"Post moved to /h/{post.sub}"}
else:
existing = g.db.query(Flag.post_id).filter_by(user_id=v.id, post_id=post.id).one_or_none()
if existing:
return {"error": "You already reported this post!"}, 409
if existing: abort(409, "You already reported this post!")
flag = Flag(post_id=post.id, user_id=v.id, reason=reason)
g.db.add(flag)
@ -121,8 +119,7 @@ def flag_comment(cid, v):
comment = get_comment(cid)
existing = g.db.query(CommentFlag.comment_id).filter_by(user_id=v.id, comment_id=comment.id).one_or_none()
if existing:
return {"error": "You already reported this comment!"}, 409
if existing: abort(409, "You already reported this comment!")
reason = request.values.get("reason", "").strip()
@ -135,7 +132,7 @@ def flag_comment(cid, v):
reason = filter_emojis_only(reason)
if len(reason) > 350: return {"error": "Too long."}, 400
if len(reason) > 350: abort(400, "Too long.")
flag = CommentFlag(comment_id=comment.id, user_id=v.id, reason=reason)

View File

@ -75,7 +75,7 @@ def searchposts(v):
author = get_user(criteria['author'], v=v, include_shadowbanned=False)
if author.is_private and author.id != v.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye:
if request.headers.get("Authorization"):
return {"error": f"@{author.username}'s profile is private; You can't use the 'author' syntax on them"}, 400
abort(403, f"@{author.username}'s profile is private; You can't use the 'author' syntax on them")
return render_template("search.html",
v=v,
query=query,
@ -193,7 +193,7 @@ def searchcomments(v):
if 'post' in criteria:
try: post = int(criteria['post'])
except: return {"error": f"Post with id {post} does not exist."}, 400
except: abort(404, f"Post with id {post} does not exist.")
comments = comments.filter(Comment.parent_submission == post)
@ -202,7 +202,7 @@ def searchcomments(v):
author = get_user(criteria['author'], v=v, include_shadowbanned=False)
if author.is_private and author.id != v.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye:
if request.headers.get("Authorization"):
return {"error": f"@{author.username}'s profile is private; You can't use the 'author' syntax on them"}, 400
abort(403, f"@{author.username}'s profile is private; You can't use the 'author' syntax on them")
return render_template("search_comments.html", v=v, query=query, total=0, page=page, comments=[], sort=sort, t=t, next_exists=False, error=f"@{author.username}'s profile is private; You can't use the 'author' syntax on them.")

View File

@ -263,7 +263,7 @@ def settings_profile_post(v):
if theme:
if theme in {"4chan","classic","classic_dark","coffee","dark","dramblr","light","midnight","transparent","tron","win98"}:
if theme == "transparent" and not v.background:
return {"error": "You need to set a background to use the transparent theme!"}, 400
abort(400, "You need to set a background to use the transparent theme!")
v.theme = theme
if theme == "win98": v.themecolor = "30409f"
updated = True
@ -294,7 +294,7 @@ def settings_profile_post(v):
return {"message": "Your settings have been updated."}
else:
return {"error": "You didn't change anything."}, 400
abort(400, "You didn't change anything.")
@app.post("/settings/filters")
@ -348,23 +348,23 @@ def themecolor(v):
@auth_required
def gumroad(v):
if not (v.email and v.is_activated):
return {"error": f"You must have a verified email to verify {patron} status and claim your rewards!"}, 400
abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!")
data = {'access_token': GUMROAD_TOKEN, 'email': v.email}
response = requests.get('https://api.gumroad.com/v2/sales', data=data, timeout=5).json()["sales"]
if len(response) == 0: return {"error": "Email not found"}, 404
if len(response) == 0: abort(404, "Email not found")
response = [x for x in response if x['variants_and_quantity']]
response = response[0]
tier = tiers[response["variants_and_quantity"]]
if v.patron == tier: return {"error": f"{patron} rewards already claimed"}, 400
if v.patron == tier: abort(400, f"{patron} rewards already claimed")
procoins = procoins_li[tier] - procoins_li[v.patron]
if procoins < 0: return {"error": f"{patron} rewards already claimed"}, 400
if procoins < 0: abort(400, f"{patron} rewards already claimed")
existing = g.db.query(User.id).filter(User.email == v.email, User.is_activated == True, User.patron >= tier).first()
if existing: return {"error": f"{patron} rewards already claimed on another account"}, 400
if existing: abort(400, f"{patron} rewards already claimed on another account")
v.patron = tier
if v.discord_id: add_role(v, f"{tier}")
@ -513,7 +513,7 @@ def settings_log_out_others(v):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
def settings_images_profile(v):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.")
file = request.files["profile"]
@ -549,9 +549,7 @@ def settings_images_profile(v):
@auth_required
@feature_required('USERS_PROFILE_BANNER')
def settings_images_banner(v):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.")
file = request.files["banner"]
@ -586,12 +584,12 @@ def settings_css_get(v):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
def settings_css(v):
if v.agendaposter: return {"error": "Agendapostered users can't edit css!"}, 400
if v.agendaposter: abort(400, "Agendapostered users can't edit CSS!")
css = request.values.get("css").strip().replace('\\', '').strip()[:4000]
if '</style' in css.lower():
return {"error": "Please message @Aevann if you get this error"}, 400
abort(400, "Please message @Aevann if you get this error")
v.css = css
g.db.add(v)
@ -625,21 +623,16 @@ def settings_profilecss(v):
def settings_block_user(v):
user = get_user(request.values.get("username"), graceful=True)
if not user: return {"error": "This user doesn't exist."}, 404
if not user: abort(404, "This user doesn't exist.")
if user.unblockable:
if not v.shadowbanned:
send_notification(user.id, f"@{v.username} has tried to block you and failed because of your unblockable status!")
return {"error": "This user is unblockable."}, 403
abort(403, "This user is unblockable.")
if user.id == v.id:
return {"error": "You can't block yourself."}, 409
if v.has_blocked(user):
return {"error": f"You have already blocked @{user.username}."}, 409
if user.id == AUTOJANNY_ID:
return {"error": "You can't block this user."}, 409
if user.id == v.id: abort(400, "You can't block yourself")
if user.id == AUTOJANNY_ID: abort(403, "You can't block this user")
if v.has_blocked(user): abort(409, f"You have already blocked @{user.username}")
new_block = UserBlock(user_id=v.id,
target_id=user.id,
@ -660,13 +653,9 @@ def settings_block_user(v):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
def settings_unblock_user(v):
user = get_user(request.values.get("username"))
x = v.has_blocked(user)
if not x: abort(409)
if not x: abort(409, "You can't unblock someone you haven't blocked")
g.db.delete(x)
if not v.shadowbanned and user.admin_level >= PERMS['USER_BLOCKS_VISIBLE']:

View File

@ -245,7 +245,7 @@ def add_mod(v, sub):
user = get_user(user, v=v, include_shadowbanned=False)
if sub in ('furry','vampire','racist','femboy') and not v.client and not user.house.lower().startswith(sub):
return {"error": f"@{user.username} needs to be a member of House {sub.capitalize()} to be added as a mod there!"}, 400
abort(403, f"@{user.username} needs to be a member of House {sub.capitalize()} to be added as a mod there!")
existing = g.db.query(Mod).filter_by(user_id=user.id, sub=sub).one_or_none()
@ -470,7 +470,7 @@ def get_sub_css(sub):
@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@is_not_permabanned
def sub_banner(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.")
sub = get_sub_by_name(sub)
if not v.mods(sub.name): abort(403)
@ -503,7 +503,7 @@ def sub_banner(v, sub):
@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@is_not_permabanned
def sub_sidebar(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.")
sub = get_sub_by_name(sub)
if not v.mods(sub.name): abort(403)
@ -535,7 +535,7 @@ def sub_sidebar(v, sub):
@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@is_not_permabanned
def sub_marsey(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.")
sub = get_sub_by_name(sub)
if not v.mods(sub.name): abort(403)

View File

@ -264,9 +264,9 @@ def transfer_coins(v, username):
amount = int(amount) if amount.isdigit() else None
reason = request.values.get("reason", "").strip()
if amount is None or amount <= 0: return {"error": "Invalid amount of coins."}, 400
if v.coins < amount: return {"error": "You don't have enough coins."}, 400
if amount < 100: return {"error": "You have to gift at least 100 coins."}, 400
if amount is None or amount <= 0: abort(400, "Invalid amount of coins.")
if v.coins < amount: abort(400, "You don't have enough coins.")
if amount < 100: abort(400, "You have to gift at least 100 coins.")
if not v.patron and not receiver.patron and not v.alts_patron and not receiver.alts_patron: tax = math.ceil(amount*0.03)
else: tax = 0
@ -291,8 +291,7 @@ def transfer_coins(v, username):
g.db.add(v)
return {"message": f"{amount-tax} coins have been transferred to @{receiver.username}"}, 200
return {"message": "You can't transfer coins to yourself!"}, 400
abort(400, "You can't transfer coins to yourself!")
@app.post("/@<username>/transfer_bux")
@ -307,9 +306,9 @@ def transfer_bux(v, username):
amount = int(amount) if amount.isdigit() else None
reason = request.values.get("reason", "").strip()
if not amount or amount < 0: return {"error": "Invalid amount of marseybux."}, 400
if v.procoins < amount: return {"error": "You don't have enough marseybux"}, 400
if amount < 100: return {"error": "You have to gift at least 100 marseybux."}, 400
if not amount or amount < 0: abort(400, "Invalid amount of marseybux.")
if v.procoins < amount: abort(400, "You don't have enough marseybux")
if amount < 100: abort(400, "You have to gift at least 100 marseybux.")
v.procoins -= amount
@ -331,7 +330,7 @@ def transfer_bux(v, username):
g.db.add(v)
return {"message": f"{amount} marseybux have been transferred to @{receiver.username}"}, 200
return {"message": "You can't transfer marseybux to yourself!"}, 400
abort(400, "You can't transfer marseybux to yourslef!")
@app.get("/leaderboard")
@ -500,16 +499,16 @@ def message2(v, username):
user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False)
if hasattr(user, 'is_blocking') and user.is_blocking:
return {"error": "You're blocking this user."}, 403
abort(403, "You're blocking this user.")
if v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] and hasattr(user, 'is_blocked') and user.is_blocked:
return {"error": "This user is blocking you."}, 403
abort(403, "This user is blocking you.")
message = request.values.get("message", "").strip()[:10000].strip()
if not message: return {"error": "Message is empty!"}, 400
if not message: abort(400, "Message is empty!")
if 'linkedin.com' in message: return {"error": "This domain 'linkedin.com' is banned."}, 403
if 'linkedin.com' in message: abort(403, "This domain 'linkedin.com' is banned.")
body_html = sanitize(message)
@ -519,7 +518,7 @@ def message2(v, username):
Comment.body_html == body_html,
).first()
if existing: return {"error": "Message already exists."}, 403
if existing: abort(403, "Message already exists.")
c = Comment(author_id=v.id,
parent_submission=None,
@ -573,18 +572,18 @@ def messagereply(v):
body = request.values.get("body", "").strip().replace('','')
body = body.replace('\r\n', '\n')[:COMMENT_BODY_LENGTH_LIMIT]
if not body and not request.files.get("file"): return {"error": "Message is empty!"}, 400
if not body and not request.files.get("file"): abort(400, "Message is empty!")
if 'linkedin.com' in body: return {"error": "this domain 'linkedin.com' is banned"}, 400
if 'linkedin.com' in body: abort(400, "this domain 'linkedin.com' is banned")
id = int(request.values.get("parent_id"))
parent = get_comment(id, v=v)
user_id = parent.author.id
if v.is_suspended_permanently and parent.sentto != 2:
return {"error": "You are permabanned and may not reply to messages."}, 400
abort(400, "You are permabanned and may not reply to messages.")
elif v.is_muted and parent.sentto == 2:
return {"error": "You are forbidden from replying to modmail."}, 400
abort(400, "You are forbidden from replying to modmail.")
if parent.sentto == 2: user_id = None
elif v.id == user_id: user_id = parent.sentto
@ -791,14 +790,14 @@ def u_username(username, v=None):
if u.is_private and (not v or (v.id != u.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye)):
if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"):
return {"error": "This userpage is private"}, 403
abort(403, "This userpage is private")
return render_template("userpage_private.html", u=u, v=v)
if v and hasattr(u, 'is_blocking') and u.is_blocking:
if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"):
return {"error": f"You are blocking @{u.username}."}, 403
abort(403, f"You are blocking @{u.username}.")
return render_template("userpage_blocking.html", u=u, v=v)
@ -877,12 +876,12 @@ def u_username_comments(username, v=None):
if u.is_private and (not v or (v.id != u.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye)):
if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"):
return {"error": "This userpage is private"}, 403
abort(403, "This userpage is private")
return render_template("userpage_private.html", u=u, v=v)
if v and hasattr(u, 'is_blocking') and u.is_blocking:
if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"):
return {"error": f"You are blocking @{u.username}."}, 403
abort(403, f"You are blocking @{u.username}.")
return render_template("userpage_blocking.html", u=u, v=v)
try: page = max(int(request.values.get("page", "1")), 1)
@ -933,9 +932,9 @@ def u_username_info(username, v=None):
user=get_user(username, v=v, include_blocks=True, include_shadowbanned=False)
if hasattr(user, 'is_blocking') and user.is_blocking:
return {"error": "You're blocking this user."}, 401
abort(401, "You're blocking this user.")
elif hasattr(user, 'is_blocked') and user.is_blocked:
return {"error": "This user is blocking you."}, 403
abort(403, "This user is blocking you.")
return user.json
@ -946,9 +945,9 @@ def u_user_id_info(id, v=None):
user=get_account(id, v=v, include_blocks=True, include_shadowbanned=False)
if hasattr(user, 'is_blocking') and user.is_blocking:
return {"error": "You're blocking this user."}, 401
abort(403, "You're blocking this user.")
elif hasattr(user, 'is_blocked') and user.is_blocked:
return {"error": "This user is blocking you."}, 403
abort(403, "This user is blocking you.")
return user.json
@ -961,10 +960,10 @@ def follow_user(username, v):
target = get_user(username, v=v, include_shadowbanned=False)
if target.id==v.id:
return {"error": "You can't follow yourself!"}, 400
abort(400, "You can't follow yourself!")
if target.is_nofollow:
return {"error": "This user has disallowed other users from following them!"}, 403
abort(403, "This user has disallowed other users from following them!")
if g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).one_or_none():
return {"message": f"@{target.username} has been followed!"}
@ -993,7 +992,7 @@ def unfollow_user(username, v):
if target.fish:
if not v.shadowbanned:
send_notification(target.id, f"@{v.username} has tried to unfollow you and failed because of your fish award!")
return {"error": "You can't unfollow this user!"}, 400
abort(400, "You can't unfollow this user!")
follow = g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).one_or_none()
@ -1223,15 +1222,15 @@ kofi_tiers={
@auth_required
def settings_kofi(v):
if not (v.email and v.is_activated):
return {"error": f"You must have a verified email to verify {patron} status and claim your rewards!"}, 400
abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!")
transaction = g.db.query(Transaction).filter_by(email=v.email).order_by(Transaction.created_utc.desc()).first()
if not transaction:
return {"error": "Email not found"}, 404
abort(404, "Email not found")
if transaction.claimed:
return {"error": f"{patron} rewards already claimed"}, 400
abort(400, f"{patron} rewards already claimed")
tier = kofi_tiers[transaction.amount]

View File

@ -49,7 +49,7 @@ def vote_info_get(v, link):
@is_not_permabanned
def vote_post(post_id, new, v):
if new == "-1" and DISABLE_DOWNVOTES: return {"error": "forbidden."}, 403
if new == "-1" and DISABLE_DOWNVOTES: abort(403)
if new not in ["-1", "0", "1"]: abort(400)
@ -126,7 +126,7 @@ def vote_post(post_id, new, v):
@is_not_permabanned
def vote_comment(comment_id, new, v):
if new == "-1" and DISABLE_DOWNVOTES: return {"error": "forbidden."}, 403
if new == "-1" and DISABLE_DOWNVOTES: abort(403, "forbidden.")
if new not in ["-1", "0", "1"]: abort(400)

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>400 Bad Request</title>
{% endblock %}
{% block pagetype %}error-400{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseybrainlet:" loading="lazy" src="/e/marseybrainlet.webp">
<pre></pre>
<h1 class="h5">400 Bad Request</h1>
<p class="text-muted mb-5">That request was bad and you should feel bad.</p>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,24 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>401 Not Authorized</title>
{% endblock %}
{% block pagetype %}error-401{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseydead:" loading="lazy" src="/e/marseydead.webp">
<pre></pre>
<h1 class="h5">401 Not Authorized</h1>
<p class="text-muted mb-5">What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.</p>
<div><a href="/signup" class="btn btn-primary mb-2">Create an account</a></div>
<div><a href="/login" class="text-muted text-small">Or sign in</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>403 Forbidden</title>
{% endblock %}
{% block pagetype %}error-403{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseytroll:" loading="lazy" src="/e/marseytroll.webp">
<pre></pre>
<h1 class="h5">403 Forbidden</h1>
<p class="text-muted mb-5">{{description}}</p>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>404 Page Not Found</title>
{% endblock %}
{% block pagetype %}error-404{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyconfused" loading="lazy" src="/e/marseyconfused.webp">
<pre></pre>
<h1 class="h5">404 Page Not Found</h1>
<p class="text-muted mb-5">Someone typed something wrong and it was probably you, please do better.</p>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>405 Method Not Allowed</title>
{% endblock %}
{% block pagetype %}error-405{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyretard:" loading="lazy" src="/e/marseyretard.webp">
<pre></pre>
<h1 class="h5">405 Method Not Allowed</h1>
<p class="text-muted mb-5">idk how anyone gets this error but if you see this, remember to follow @carpathianflorist<BR>the original error text here talked about internet gremlins and wtf</p>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>Too many pings</title>
{% endblock %}
{% block pagetype %}Too many pings{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyrage" loading="lazy" src="/e/marseyrage.webp">
<pre></pre>
<h1 class="h5">Too many pings</h1>
<p class="text-muted mb-5">Max limit is 5 for comments and 50 for posts</p>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>Max image/audio size is 8 MB (16 MB for paypigs)</title>
{% endblock %}
{% block pagetype %}error-413{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyretard:" loading="lazy" src="/e/marseychonker2.webp">
<pre></pre>
<h1 class="h5">Max image/audio size is 8 MB (16 MB for paypigs)</h1>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>Max video size is 32 MB (64 MB for paypigs)</title>
{% endblock %}
{% block pagetype %}error-414{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyretard:" loading="lazy" src="/e/marseychonker2.webp">
<pre></pre>
<h1 class="h5">Max video size is 32 MB (64 MB for paypigs)</h1>
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>415 Unsupported Media Type</title>
{% endblock %}
{% block pagetype %}error-415{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseydetective:" loading="lazy" src="/e/marseydetective.webp">
<pre></pre>
<h1 class="h5">415 Unsupported Media Type</h1>
<p class="text-muted mb-5">Please upload only Image, Video, or Audio files!</p>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>WEBM videos are not allowed</title>
{% endblock %}
{% block pagetype %}error-418{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseytea:" loading="lazy" src="/e/marseytea.webp">
<pre></pre>
<h1 class="h5">WEBM videos are not allowed</h1>
<div><a href="/" class="btn btn-primary">Please convert your video to MP4 and re-upload it!</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>429 Too Many Requests</title>
{% endblock %}
{% block pagetype %}error-429{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
<img alt=":#marseyrentfree:" loading="lazy" src="/e/marseyrentfree.webp">
<pre></pre>
<h1 class="h5">429 Too Many Requests</h1>
<p class="text-muted mb-5">go spam somewhere else nerd</p>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,31 +0,0 @@
{% extends "default.html" %}
{% block title %}
<title>500 Internal Server Error</title>
{% endblock %}
{% block pagetype %}error-500{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
{% if SITE_NAME == 'PCM' %}
<img alt="wholesome" loading="lazy" src="/i/wholesome.webp">
{% else %}
<img alt=":#marseycarp3:" loading="lazy" src="/e/marseycarp3.webp">
{% endif %}
<pre></pre>
<h1 class="h5">500 Internal Server Error</h1>
<p class="text-muted mb-5">
{% if SITE_NAME == 'PCM' %}
Hiiiii it's <b>nigger</b>! I think this error means that there's a <b>nigger</b> error. And I think that means something took too long to load so it decided to be a <b>nigger</b>. If you keep seeing this on the same page but not other pages, then something its probably a <b>niggerfaggot</b>. It may not be called a <b>nigger</b>, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily &lt;3
{% else %}
Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page <I>but not other pages</I>, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily &lt;3
{% endif %}
</p>
<div><a href="/" class="btn btn-primary">Go to the frontpage</a></div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "default.html" %}
{% block title %}
<title>{{code}} {{title}}</title>
{% endblock %}
{% block pagetype %}error-{{code}}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5">
<div class="text-center px-3 my-8">
{% if img -%}
<img alt=":#{{img}}:" loading="lazy" src="/e/{{img}}.webp">
{%- endif %}
<pre></pre>
<h1 class="h5">{{code}} {{title}}</h1>
<p class="text-muted error-description">{{msg|safe}}</p>
{% if details -%}
<blockquote class="error-details mb-5">{{details|safe}}</blockquote>
{%- endif %}
<div><a href="/" class="btn btn-primary">Go to frontpage</a></div>
</div>
</div>
</div>
{% endblock %}