From ed0981cbdba6223387846c4ef7dc2a2b139e65cf Mon Sep 17 00:00:00 2001 From: justcool393 Date: Sun, 4 Dec 2022 21:46:27 +0000 Subject: [PATCH] add functionality to disable poll formatting (#35) Co-authored-by: justcool393 Reviewed-on: https://fsdfsd.net/rDrama/rDrama/pulls/35 Co-authored-by: justcool393 Co-committed-by: justcool393 --- files/helpers/actions.py | 22 ++++++++ files/helpers/const.py | 2 + files/helpers/regex.py | 12 +++-- files/helpers/sanitize.py | 28 ++++++---- files/routes/comments.py | 55 +++----------------- files/routes/posts.py | 92 +++------------------------------ files/templates/formatting.html | 5 ++ 7 files changed, 71 insertions(+), 145 deletions(-) diff --git a/files/helpers/actions.py b/files/helpers/actions.py index d36d3e10c..8464baebd 100644 --- a/files/helpers/actions.py +++ b/files/helpers/actions.py @@ -1,5 +1,6 @@ import random import time +from typing import Type from urllib.parse import quote import gevent @@ -8,6 +9,7 @@ from flask import g from files.classes.flags import Flag from files.classes.mod_logs import ModAction from files.classes.notifications import Notification +from files.classes.polls import CommentOption, SubmissionOption from files.helpers.alerts import send_repeatable_notification from files.helpers.const import * @@ -492,3 +494,23 @@ def execute_lawlz_actions(v:User, p:Submission): g.db.add(ma_1) g.db.add(ma_2) g.db.add(ma_3) + +def process_poll_options(target:Union[Submission, Comment], + cls:Union[Type[SubmissionOption], Type[CommentOption]], + options:Iterable[str], exclusive:int, friendly_name:str, + db:scoped_session) -> None: + for option in options: + if len(option) > 500: abort(400, f"{friendly_name} option too long!") + if cls is SubmissionOption: + option = cls( + submission_id=target.id, + body_html=option, + exclusive=exclusive, + ) + else: + option = cls( + comment_id=target.id, + body_html=option, + exclusive=exclusive, + ) + db.add(option) diff --git a/files/helpers/const.py b/files/helpers/const.py index 7f118b22c..63e9f274a 100644 --- a/files/helpers/const.py +++ b/files/helpers/const.py @@ -1444,6 +1444,8 @@ TROLLTITLES = [ "Hey jannies can you please ban @{username}", ] +DISABLE_POLL_COMMAND = "disablepoll" + NOTIFIED_USERS = { 'aevan': AEVANN_ID, 'avean': AEVANN_ID, diff --git a/files/helpers/regex.py b/files/helpers/regex.py index 46e356502..ff413615c 100644 --- a/files/helpers/regex.py +++ b/files/helpers/regex.py @@ -177,13 +177,19 @@ commands = { "fortune": FORTUNE_REPLIES, "factcheck": FACTCHECK_REPLIES, "8ball": EIGHTBALL_REPLIES, - "roll": range(1, 9999) + "roll": range(1, 9999), + DISABLE_POLL_COMMAND: None, } -command_regex = re.compile("(\s|\n|^)#(fortune|factcheck|8ball|roll)", flags=re.A|re.I) +command_regex = re.compile(f"(\s|\n|^)#({'|'.join(commands.keys())})", flags=re.A|re.I) def command_regex_matcher(match, upper=False): - result = str(choice(commands[match.group(2).lower()])) + choices = commands[match.group(2).lower()] + if not choices: + return '' + elif isinstance(choices, str): + return choices + result = str(choice(choices)) if match.group(2) == 'roll': color = tuple(choices(range(256), k=3)) result = f'Your roll: {result}' diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py index ec34a97a7..642984d55 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -4,6 +4,7 @@ import re import signal from functools import partial from os import path +from typing import Any from urllib.parse import parse_qs, urlparse import bleach @@ -255,18 +256,14 @@ def sanitize(sanitized, golden=True, limit_pings=0, showmore=True, count_marseys sanitized = linefeeds_regex.sub(r'\1\n\n\2', sanitized) sanitized = greentext_regex.sub(r'\1\>\2', sanitized) - sanitized = image_regex.sub(r'\1![](\2)\5', sanitized) - sanitized = image_check_regex.sub(r'\1', sanitized) - sanitized = link_fix_regex.sub(r'\1https://\2', sanitized) if FEATURES['MARKUP_COMMANDS']: sanitized = command_regex.sub(command_regex_matcher, sanitized) sanitized = markdown(sanitized) - sanitized = strikethrough_regex.sub(r'\1\2', sanitized) # replacing zero width characters, overlines, fake colons @@ -417,12 +414,7 @@ def sanitize(sanitized, golden=True, limit_pings=0, showmore=True, count_marseys return sanitized.strip() - - - - def allowed_attributes_emojis(tag, name, value): - if tag == 'img': if name == 'src' and value.startswith('/') and '\\' not in value: return True if name == 'loading' and value == 'lazy': return True @@ -503,3 +495,21 @@ def validate_css(css): return False, f"The domain '{domain}' is not allowed, please use one of these domains\n\n{approved_embed_hosts}." return True, "" + +def sanitize_poll_options(v:User, body:str, allow_bets:bool) -> tuple[str, List[Any], List[Any], List[Any]]: + if FEATURES['MARKUP_COMMANDS'] and body.startswith(f"#{DISABLE_POLL_COMMAND}"): + return (body, [], [], []) + + def sanitize_poll_type(body:str, re:re.Pattern) -> tuple[str, List[str]]: + opts = [] + for i in list(re.finditer(body))[:POLL_MAX_OPTIONS]: + opts.append(filter_emojis_only(i.group(1))) + body = body.replace(i.group(0), "") + return (body, opts) + + bets = [] + if allow_bets and v and v.admin_level >= PERMS['POST_BETS']: + body, bets = sanitize_poll_type(body, bet_regex) + body, options = sanitize_poll_type(body, poll_regex) + body, choices = sanitize_poll_type(body, choice_regex) + return (body, bets, options, choices) diff --git a/files/routes/comments.py b/files/routes/comments.py index aee3f9649..7ad0944ba 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -129,15 +129,7 @@ def comment(v): if v.admin_level < PERMS['POST_COMMENT_MODERATION'] and parent.author.any_block_exists(v): abort(403, "You can't reply to users who have blocked you or users that you have blocked.") - options = [] - for i in list(poll_regex.finditer(body))[:POLL_MAX_OPTIONS]: - options.append(i.group(1)) - body = body.replace(i.group(0), "") - - choices = [] - for i in list(choice_regex.finditer(body))[:POLL_MAX_OPTIONS]: - choices.append(i.group(1)) - body = body.replace(i.group(0), "") + body, _, options, choices = sanitize_poll_options(v, body, False) if request.files.get("file") and not g.is_tor: files = request.files.getlist('file')[:4] @@ -241,25 +233,8 @@ def comment(v): if c.level == 1: c.top_comment_id = c.id else: c.top_comment_id = parent.top_comment_id - for option in options: - body_html = filter_emojis_only(option) - if len(body_html) > 500: abort(400, "Poll option too long!") - option = CommentOption( - comment_id=c.id, - body_html=body_html, - exclusive=0 - ) - g.db.add(option) - - for choice in choices: - body_html = filter_emojis_only(choice) - if len(body_html) > 500: abort(400, "Poll option too long!") - choice = CommentOption( - comment_id=c.id, - body_html=body_html, - exclusive=1 - ) - g.db.add(choice) + process_poll_options(c, CommentOption, options, 0, "Poll", g.db) + process_poll_options(c, CommentOption, choices, 1, "Poll", g.db) if SITE == 'pcmemes.net' and c.body.lower().startswith("based"): execute_basedbot(c, level, body, parent_post, v) @@ -394,27 +369,9 @@ def edit_comment(cid, v): elif v.bird and len(body) > 140: abort(403, "You have to type less than 140 characters!") - for i in list(poll_regex.finditer(body))[:POLL_MAX_OPTIONS]: - body = body.replace(i.group(0), "") - body_html = filter_emojis_only(i.group(1)) - if len(body_html) > 500: abort(400, "Poll option too long!") - option = CommentOption( - comment_id=c.id, - body_html=body_html, - exclusive = 0 - ) - g.db.add(option) - - for i in list(choice_regex.finditer(body))[:POLL_MAX_OPTIONS]: - body = body.replace(i.group(0), "") - body_html = filter_emojis_only(i.group(1)) - if len(body_html) > 500: abort(400, "Poll option too long!") - option = CommentOption( - comment_id=c.id, - body_html=body_html, - exclusive = 1 - ) - g.db.add(option) + body, _, options, choices = sanitize_poll_options(v, body, False) + process_poll_options(c, CommentOption, options, 0, "Poll", g.db) + process_poll_options(c, CommentOption, choices, 1, "Poll", g.db) execute_antispam_comment_check(body, v) diff --git a/files/routes/posts.py b/files/routes/posts.py index 770a7f1a0..a3fb4d682 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -299,39 +299,10 @@ def edit_post(pid, v): body = body.strip()[:POST_BODY_LENGTH_LIMIT] # process_files() may be adding stuff to the body if body != p.body: - if v and v.admin_level >= PERMS['POST_BETS']: - for i in bet_regex.finditer(body): - body = body.replace(i.group(0), "") - body_html = filter_emojis_only(i.group(1)) - if len(body_html) > 500: abort(400, "Bet option too long!") - bet = SubmissionOption( - submission_id=p.id, - body_html=body_html, - exclusive = 2 - ) - g.db.add(bet) - - for i in list(poll_regex.finditer(body))[:10]: - body = body.replace(i.group(0), "") - body_html = filter_emojis_only(i.group(1)) - if len(body_html) > 500: abort(400, "Poll option too long!") - option = SubmissionOption( - submission_id=p.id, - body_html=body_html, - exclusive = 0 - ) - g.db.add(option) - - for i in list(choice_regex.finditer(body))[:10]: - body = body.replace(i.group(0), "") - body_html = filter_emojis_only(i.group(1)) - if len(body_html) > 500: abort(400, "Poll option too long!") - choice = SubmissionOption( - submission_id=p.id, - body_html=body_html, - exclusive = 1 - ) - g.db.add(choice) + body, bets, options, choices = sanitize_poll_options(v, body, False) + process_poll_options(p, SubmissionOption, bets, 2, "Bet", g.db) + process_poll_options(p, SubmissionOption, options, 0, "Poll", g.db) + process_poll_options(p, SubmissionOption, choices, 1, "Poll", g.db) torture = (v.agendaposter and not v.marseyawarded and p.sub != 'chudrama' and v.id == p.author_id) @@ -700,21 +671,7 @@ def submit_post(v:User, sub=None): if len(url) > 2048: return error("There's a 2048 character limit for URLs.") - bets = [] - if v and v.admin_level >= PERMS['POST_BETS']: - for i in bet_regex.finditer(body): - bets.append(i.group(1)) - body = body.replace(i.group(0), "") - - options = [] - for i in list(poll_regex.finditer(body))[:10]: - options.append(i.group(1)) - body = body.replace(i.group(0), "") - - choices = [] - for i in list(choice_regex.finditer(body))[:10]: - choices.append(i.group(1)) - body = body.replace(i.group(0), "") + body, bets, options, choices = sanitize_poll_options(v, body, True) body += process_files(request.files, v) body = body.strip()[:POST_BODY_LENGTH_LIMIT] # process_files() adds content to the body, so we need to re-strip @@ -764,36 +721,9 @@ def submit_post(v:User, sub=None): for text in {post.body, post.title, post.url}: if not execute_blackjack(v, post, text, 'submission'): break - if v and v.admin_level >= PERMS['POST_BETS']: - for bet in bets: - body_html = filter_emojis_only(bet) - if len(body_html) > 500: abort(400, "Bet option too long!") - bet = SubmissionOption( - submission_id=post.id, - body_html=body_html, - exclusive=2 - ) - g.db.add(bet) - - for option in options: - body_html = filter_emojis_only(option) - if len(body_html) > 500: abort(400, "Poll option too long!") - option = SubmissionOption( - submission_id=post.id, - body_html=body_html, - exclusive=0 - ) - g.db.add(option) - - for choice in choices: - body_html = filter_emojis_only(choice) - if len(body_html) > 500: abort(400, "Poll option too long!") - choice = SubmissionOption( - submission_id=post.id, - body_html=body_html, - exclusive=1 - ) - g.db.add(choice) + process_poll_options(post, SubmissionOption, bets, 2, "Bet", g.db) + process_poll_options(post, SubmissionOption, options, 0, "Poll", g.db) + process_poll_options(post, SubmissionOption, choices, 1, "Poll", g.db) vote = Vote(user_id=v.id, vote_type=1, @@ -860,14 +790,9 @@ def submit_post(v:User, sub=None): n = Notification(comment_id=c_jannied.id, user_id=v.id) g.db.add(n) - - if not post.private and not (post.sub and g.db.query(Exile.user_id).filter_by(user_id=SNAPPY_ID, sub=post.sub).one_or_none()): execute_snappy(post, v) - - - v.post_count = g.db.query(Submission).filter_by(author_id=v.id, deleted_utc=0).count() g.db.add(v) @@ -896,7 +821,6 @@ def submit_post(v:User, sub=None): else: sort = v.defaultsortingcomments return render_template('submission.html', v=v, p=post, sort=sort, render_replies=True, offset=0, success=True, sub=post.subr) - @app.post("/delete_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) @auth_required diff --git a/files/templates/formatting.html b/files/templates/formatting.html index 39584c9a1..ae1a80d13 100644 --- a/files/templates/formatting.html +++ b/files/templates/formatting.html @@ -127,6 +127,11 @@ Text 2 #roll A number 1–9999. + + Disable Poll Formatting + #disablepoll + Disables poll options (useful for large code blocks that have things like &&). + {%- endif %}