diff --git a/files/assets/emojis.json b/files/assets/emojis.json index 8d1e8de3e..c08a95f63 100644 --- a/files/assets/emojis.json +++ b/files/assets/emojis.json @@ -839,6 +839,7 @@ {"name":"gigachad","tags": ["chad"],"class":"Misc"}, {"name":"gigachad2", "tags": ["chad"], "class":"Misc"}, {"name":"gigachad3", "tags": ["chad"], "class":"Misc"}, + {"name":"pedobear", "class":"Misc"}, {"name":"kippy","class":"Misc"}, {"name":"onerat","class":"Misc"}, {"name":"gunt","class":"Misc"} diff --git a/files/assets/emojis.lgbdropthet.json b/files/assets/emojis.lgbdropthet.json index c0924615d..6ae21957a 100644 --- a/files/assets/emojis.lgbdropthet.json +++ b/files/assets/emojis.lgbdropthet.json @@ -378,5 +378,6 @@ {"name":"wholesomeseal","class":"Misc"}, {"name":"gigachadglow","tags": ["chad", "glow", "fbi"],"class":"Misc"}, {"name":"gigachad","tags": ["chad"],"class":"Misc"}, - {"name":"gigachad2", "tags": ["chad"], "class":"Misc"} + {"name":"gigachad2", "tags": ["chad"], "class":"Misc"}, + {"name":"pedobear", "class":"Misc"} ] diff --git a/files/assets/images/emojis/pedobear.webp b/files/assets/images/emojis/pedobear.webp new file mode 100644 index 000000000..dd797e574 Binary files /dev/null and b/files/assets/images/emojis/pedobear.webp differ diff --git a/files/assets/js/comments+submission_listing.js b/files/assets/js/comments+submission_listing.js index 49056b0f1..c081efc81 100644 --- a/files/assets/js/comments+submission_listing.js +++ b/files/assets/js/comments+submission_listing.js @@ -36,13 +36,15 @@ function popclick(e) { let popover = document.getElementsByClassName("popover") popover = popover[popover.length-1] - const badgesDOM = popover.getElementsByClassName('pop-badges')[0]; - badgesDOM.innerHTML = ""; - for (const badge of author["badges"]) { - const badgeDOM = popClickBadgeTemplateDOM.cloneNode(); - badgeDOM.src = badge + "?v=1021"; + if (popover.getElementsByClassName('pop-badges').length > 0) { + const badgesDOM = popover.getElementsByClassName('pop-badges')[0]; + badgesDOM.innerHTML = ""; + for (const badge of author["badges"]) { + const badgeDOM = popClickBadgeTemplateDOM.cloneNode(); + badgeDOM.src = badge + "?v=1021"; - badgesDOM.append(badgeDOM); + badgesDOM.append(badgeDOM); + } } popover.getElementsByClassName('pop-banner')[0].src = author["bannerurl"] diff --git a/files/classes/user.py b/files/classes/user.py index 738255561..74a75fe28 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -187,7 +187,7 @@ class User(Base): @lazy def mod_date(self, sub): - if self.id == AEVANN_ID: return 1 + if self.admin_level >= 3: return 1 mod = g.db.query(Mod).filter_by(user_id=self.id, sub=sub).one_or_none() if not mod: return None return mod.created_utc diff --git a/files/helpers/actions.py b/files/helpers/actions.py index d150167a2..418a19a6c 100644 --- a/files/helpers/actions.py +++ b/files/helpers/actions.py @@ -152,7 +152,7 @@ def execute_snappy(post, v): if body.startswith('!slots'): check_for_slots_command(body, snappy, c) - if PIN_ENABLED and (body.startswith(':#marseypin:') or body.startswith(':#marseypin2:')): + if FEATURES['PINS'] and (body.startswith(':#marseypin:') or body.startswith(':#marseypin2:')): post.stickied = "Snappy" post.stickied_utc = int(time.time()) + 3600 diff --git a/files/helpers/blackjack.py b/files/helpers/blackjack.py index c0c753059..48dda1dd9 100644 --- a/files/helpers/blackjack.py +++ b/files/helpers/blackjack.py @@ -1,6 +1,7 @@ from json.encoder import INFINITY import random from math import floor +from files.helpers.const import * deck_count = 4 ranks = ("2", "3", "4", "5", "6", "7", "8", "9", "X", "J", "Q", "K", "A") @@ -51,7 +52,7 @@ def format_all(player_hand, dealer_hand, deck, status, wager, kind, is_insured=0 def check_for_blackjack_commands(in_text, from_user, from_comment): - if not from_user.can_gamble: + if not FEATURES['GAMBLING'] or not from_user.can_gamble: return for command_word in (coins_command_word, marseybux_command_word): diff --git a/files/helpers/const.py b/files/helpers/const.py index db685dba3..1ca362444 100644 --- a/files/helpers/const.py +++ b/files/helpers/const.py @@ -133,17 +133,31 @@ AGENDAPOSTER_MSG_HTML = """

Hi week).count(), } - if site == 'rDrama': + if site == 'rDrama' or FEATURES['HOUSES']: stats2 = { "House furry members": g.db.query(User).filter(User.house.like('Furry%')).count(), "House femboy members": g.db.query(User).filter(User.house.like('Femboy%')).count(), diff --git a/files/helpers/treasure.py b/files/helpers/treasure.py index 2c23e76a3..ad29855f7 100644 --- a/files/helpers/treasure.py +++ b/files/helpers/treasure.py @@ -12,7 +12,7 @@ lotterizer_rate = 33 def check_for_treasure(in_text, from_comment): user = from_comment.author - if not user.can_gamble: + if not FEATURES['GAMBLING'] or not user.can_gamble: return if '!slots' not in in_text and '!blackjack' not in in_text and '!wordle' not in in_text: diff --git a/files/routes/admin.py b/files/routes/admin.py index d2c468812..66508b789 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -507,6 +507,9 @@ def under_attack(v): @app.get("/admin/badge_grant") @admin_level_required(2) def badge_grant_get(v): + if not FEATURES['BADGES']: + abort(404) + badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all() return render_template("admin/badge_grant.html", v=v, badge_types=badges) @@ -515,6 +518,9 @@ def badge_grant_get(v): @limiter.limit("1/second;30/minute;200/hour;1000/day") @admin_level_required(2) def badge_grant_post(v): + if not FEATURES['BADGES']: + abort(404) + badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all() user = get_user(request.values.get("username").strip(), graceful=True) @@ -562,6 +568,9 @@ def badge_grant_post(v): @app.get("/admin/badge_remove") @admin_level_required(2) def badge_remove_get(v): + if not FEATURES['BADGES']: + abort(404) + badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all() return render_template("admin/badge_remove.html", v=v, badge_types=badges) @@ -571,6 +580,9 @@ def badge_remove_get(v): @limiter.limit("1/second;30/minute;200/hour;1000/day") @admin_level_required(2) def badge_remove_post(v): + if not FEATURES['BADGES']: + abort(404) + badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all() user = get_user(request.values.get("username").strip(), graceful=True) @@ -1196,7 +1208,7 @@ def api_distinguish_post(post_id, v): @app.post("/sticky/") @admin_level_required(2) def sticky_post(post_id, v): - if not PIN_ENABLED: + if not FEATURES['PINS']: abort(403) post = get_post(post_id) diff --git a/files/routes/awards.py b/files/routes/awards.py index 417eb03e1..1dcf2e836 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -16,6 +16,9 @@ from copy import deepcopy @app.get("/settings/shop") @auth_required def shop(v): + if not FEATURES['AWARDS']: + abort(404) + AWARDS = deepcopy(AWARDS2) for val in AWARDS.values(): val["owned"] = 0 @@ -34,6 +37,9 @@ def shop(v): @app.post("/buy/") @auth_required def buy(v, award): + if not FEATURES['AWARDS']: + abort(404) + if award == 'benefactor' and not request.values.get("mb"): return {"error": "You can only buy the Benefactor award with marseybux."}, 403 @@ -106,6 +112,8 @@ def buy(v, award): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @is_not_permabanned def award_thing(v, thing_type, id): + if not FEATURES['AWARDS']: + abort(404) if thing_type == 'post': thing = get_post(id) else: thing = get_comment(id) @@ -199,7 +207,7 @@ def award_thing(v, thing_type, id): if note: text += f" ({note})" notify_mod_action(v.id, text) elif kind == "pin": - if not PIN_ENABLED: + if not FEATURES['PINS']: abort(403) if thing.stickied and thing.stickied_utc: thing.stickied_utc += 3600 @@ -275,7 +283,7 @@ def award_thing(v, thing_type, id): author.fish = True badge_grant(badge_id=90, user=author) elif kind == "progressivestack": - if not PIN_ENABLED: + if not FEATURES['PINS']: abort(403) if author.progressivestack: author.progressivestack += 21600 else: author.progressivestack = int(time.time()) + 21600 @@ -315,6 +323,9 @@ def award_thing(v, thing_type, id): @app.get("/admin/awards") @admin_level_required(2) def admin_userawards_get(v): + if not FEATURES['AWARDS']: + abort(404) + if SITE == 'pcmemes.net' and v.admin_level < 3: abort(403) if v.admin_level != 3: @@ -326,6 +337,9 @@ def admin_userawards_get(v): @limiter.limit("1/second;30/minute;200/hour;1000/day") @admin_level_required(2) def admin_userawards_post(v): + if not FEATURES['AWARDS']: + abort(404) + if SITE == 'pcmemes.net' and v.admin_level < 3: abort(403) try: u = request.values.get("username").strip() diff --git a/files/routes/comments.py b/files/routes/comments.py index 39f8931fc..5db2cb83c 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -846,7 +846,7 @@ def undelete_comment(cid, v): @app.post("/pin_comment/") @auth_required def pin_comment(cid, v): - if not PIN_ENABLED: + if not FEATURES['PINS']: abort(403) comment = get_comment(cid, v=v) @@ -890,7 +890,7 @@ def unpin_comment(cid, v): @app.post("/mod_pin/") @auth_required def mod_pin(cid, v): - if not PIN_ENABLED: + if not FEATURES['PINS']: abort(403) comment = get_comment(cid, v=v) diff --git a/files/routes/settings.py b/files/routes/settings.py index 978ac64c9..24f65a49c 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -260,7 +260,7 @@ def settings_profile_post(v): else: abort(400) house = request.values.get("house") - if house and house in ("None","Furry","Femboy","Vampire","Racist"): + if house and house in ("None","Furry","Femboy","Vampire","Racist") and FEATURES['HOUSES']: if v.house: cost = 2000 else: cost = 500 @@ -877,6 +877,8 @@ def settings_title_change(v): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def settings_pronouns_change(v): + if not FEATURES['PRONOUNS']: + abort(403) pronouns = request.values.get("pronouns").replace("𒐪","").lower().strip() diff --git a/files/routes/static.py b/files/routes/static.py index 928056b6c..244f1ef5e 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -323,11 +323,14 @@ def badge_list(site): @app.get("/badges") @auth_required def badges(v): + if not FEATURES['BADGES']: + abort(404) + badges, counts = badge_list(SITE) return render_template("badges.html", v=v, badges=badges, counts=counts) @app.get("/blocks") -@auth_required +@admin_level_required(PERMS['USER_BLOCKS_VISIBLE']) def blocks(v): @@ -403,4 +406,16 @@ def transfers(v): comments = comments.offset(25 * (page - 1)).limit(26).all() next_exists = len(comments) > 25 comments = comments[:25] - return render_template("transfers.html", v=v, page=page, comments=comments, standalone=True, next_exists=next_exists) \ No newline at end of file + return render_template("transfers.html", v=v, page=page, comments=comments, standalone=True, next_exists=next_exists) + +@app.get("/kb/") +@auth_desired +def knowledgebase(v, page): + if not knowledgebase_page_regex.fullmatch(page): + abort(404) + + template_path = f'kb/{SITE_NAME}/{page}.html' + if not os.path.exists('files/templates/' + template_path): + abort(404) + + return render_template(template_path, v=v) diff --git a/files/routes/users.py b/files/routes/users.py index b946144d4..efbd3bd59 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -62,6 +62,7 @@ gevent.spawn(leaderboard_thread()) def upvoters_posts(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -83,6 +84,7 @@ def upvoters_posts(v, username, uid): def upvoters_comments(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -104,6 +106,7 @@ def upvoters_comments(v, username, uid): def downvoters_posts(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -125,6 +128,7 @@ def downvoters_posts(v, username, uid): def downvoters_comments(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -149,6 +153,7 @@ def downvoters_comments(v, username, uid): def upvoting_posts(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -170,6 +175,7 @@ def upvoting_posts(v, username, uid): def upvoting_comments(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -191,6 +197,7 @@ def upvoting_comments(v, username, uid): def downvoting_posts(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -212,6 +219,7 @@ def downvoting_posts(v, username, uid): def downvoting_comments(v, username, uid): u = get_user(username) if u.is_private and (not v or (v.id != u.id and v.admin_level < 2 and not v.eye)): abort(403) + if not (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): abort(403) id = u.id uid = int(uid) @@ -255,6 +263,8 @@ def agendaposters(v): @auth_required def upvoters(v, username): id = get_user(username).id + if not (v.id == id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): + abort(403) votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission).filter(Submission.ghost == False, Submission.is_banned == False, Submission.deleted_utc == 0, Vote.vote_type==1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all() @@ -286,6 +296,8 @@ def upvoters(v, username): @auth_required def downvoters(v, username): id = get_user(username).id + if not (v.id == id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): + abort(403) votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission).filter(Submission.ghost == False, Submission.is_banned == False, Submission.deleted_utc == 0, Vote.vote_type==-1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all() @@ -315,6 +327,8 @@ def downvoters(v, username): @auth_required def upvoting(v, username): id = get_user(username).id + if not (v.id == id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): + abort(403) votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote).filter(Submission.ghost == False, Submission.is_banned == False, Submission.deleted_utc == 0, Vote.vote_type==1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all() @@ -344,6 +358,8 @@ def upvoting(v, username): @auth_required def downvoting(v, username): id = get_user(username).id + if not (v.id == id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']): + abort(403) votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote).filter(Submission.ghost == False, Submission.is_banned == False, Submission.deleted_utc == 0, Vote.vote_type==-1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all() @@ -376,6 +392,9 @@ def downvoting(v, username): @limiter.limit("1/second;5/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def suicide(v, username): + if not FEATURES['USERS_SUICIDE']: + abort(403) + user = get_user(username) suicide = f"Hi there,\n\nA [concerned user](/id/{v.id}) reached out to us about you.\n\nWhen you're in the middle of something painful, it may feel like you don't have a lot of options. But whatever you're going through, you deserve help and there are people who are here for you.\n\nThere are resources available in your area that are free, confidential, and available 24/7:\n\n- Call, Text, or Chat with Canada's [Crisis Services Canada](https://www.crisisservicescanada.ca/en/)\n- Call, Email, or Visit the UK's [Samaritans](https://www.samaritans.org/)\n- Text CHAT to America's [Crisis Text Line](https://www.crisistextline.org/) at 741741.\nIf you don't see a resource in your area above, the moderators keep a comprehensive list of resources and hotlines for people organized by location. Find Someone Now\n\nIf you think you may be depressed or struggling in another way, don't ignore it or brush it aside. Take yourself and your feelings seriously, and reach out to someone.\n\nIt may not feel like it, but you have options. There are people available to listen to you, and ways to move forward.\n\nYour fellow users care about you and there are people who want to help." if not v.shadowbanned: @@ -854,6 +873,9 @@ def redditor_moment_redirect(username, v): @auth_required def followers(username, v): u = get_user(username, v=v) + if not (v.id == u.id or v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']): + abort(403) + users = g.db.query(User).join(Follow, Follow.target_id == u.id) \ .filter(Follow.user_id == User.id) \ .order_by(Follow.created_utc).all() @@ -863,6 +885,9 @@ def followers(username, v): @auth_required def following(username, v): u = get_user(username, v=v) + if not (v.id == u.id or v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']): + abort(403) + users = g.db.query(User).join(Follow, Follow.user_id == u.id) \ .filter(Follow.target_id == User.id) \ .order_by(Follow.created_utc).all() diff --git a/files/routes/votes.py b/files/routes/votes.py index efb15f62c..d198c4fe3 100644 --- a/files/routes/votes.py +++ b/files/routes/votes.py @@ -6,7 +6,7 @@ from flask import * from files.__main__ import app, limiter, cache @app.get("/votes/") -@auth_required +@admin_level_required(PERMS['VOTES_VISIBLE']) def vote_info_get(v, link): try: if "t2_" in link: thing = get_post(int(link.split("t2_")[1]), v=v) @@ -199,4 +199,4 @@ def api_vote_comment(comment_id, new, v): comment.realupvotes = g.db.query(CommentVote).filter_by(comment_id=comment.id, real=True).count() if comment.author.progressivestack: comment.realupvotes *= 2 g.db.add(comment) - return "", 204 \ No newline at end of file + return "", 204 diff --git a/files/templates/admin/admin_home.html b/files/templates/admin/admin_home.html index 1a026e238..2ed7ad5c4 100644 --- a/files/templates/admin/admin_home.html +++ b/files/templates/admin/admin_home.html @@ -12,6 +12,7 @@

Content

@@ -35,12 +40,18 @@
  • Multi Vote Analysis
  • +{% if FEATURES['BADGES'] or FEATURES['AWARDS'] -%}

    Grant

    +{%- endif %}

    API Access Control