add functionality to disable poll formatting (#35)

Co-authored-by: justcool393 <justcool393@gmail.com>
Reviewed-on: #35
Co-authored-by: justcool393 <justcool393@noreply.fsdfsd.net>
Co-committed-by: justcool393 <justcool393@noreply.fsdfsd.net>
pull/46/head
justcool393 2022-12-04 21:46:27 +00:00 committed by Snakes
parent 892e35b1c3
commit ed0981cbdb
7 changed files with 71 additions and 145 deletions

View File

@ -1,5 +1,6 @@
import random import random
import time import time
from typing import Type
from urllib.parse import quote from urllib.parse import quote
import gevent import gevent
@ -8,6 +9,7 @@ from flask import g
from files.classes.flags import Flag from files.classes.flags import Flag
from files.classes.mod_logs import ModAction from files.classes.mod_logs import ModAction
from files.classes.notifications import Notification from files.classes.notifications import Notification
from files.classes.polls import CommentOption, SubmissionOption
from files.helpers.alerts import send_repeatable_notification from files.helpers.alerts import send_repeatable_notification
from files.helpers.const import * 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_1)
g.db.add(ma_2) g.db.add(ma_2)
g.db.add(ma_3) 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)

View File

@ -1444,6 +1444,8 @@ TROLLTITLES = [
"Hey jannies can you please ban @{username}", "Hey jannies can you please ban @{username}",
] ]
DISABLE_POLL_COMMAND = "disablepoll"
NOTIFIED_USERS = { NOTIFIED_USERS = {
'aevan': AEVANN_ID, 'aevan': AEVANN_ID,
'avean': AEVANN_ID, 'avean': AEVANN_ID,

View File

@ -177,13 +177,19 @@ commands = {
"fortune": FORTUNE_REPLIES, "fortune": FORTUNE_REPLIES,
"factcheck": FACTCHECK_REPLIES, "factcheck": FACTCHECK_REPLIES,
"8ball": EIGHTBALL_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): 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': if match.group(2) == 'roll':
color = tuple(choices(range(256), k=3)) color = tuple(choices(range(256), k=3))
result = f'<b style="color:rgb{color}">Your roll: {result}</b>' result = f'<b style="color:rgb{color}">Your roll: {result}</b>'

View File

@ -4,6 +4,7 @@ import re
import signal import signal
from functools import partial from functools import partial
from os import path from os import path
from typing import Any
from urllib.parse import parse_qs, urlparse from urllib.parse import parse_qs, urlparse
import bleach 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 = linefeeds_regex.sub(r'\1\n\n\2', sanitized)
sanitized = greentext_regex.sub(r'\1<g>\>\2</g>', sanitized) sanitized = greentext_regex.sub(r'\1<g>\>\2</g>', sanitized)
sanitized = image_regex.sub(r'\1![](\2)\5', sanitized) sanitized = image_regex.sub(r'\1![](\2)\5', sanitized)
sanitized = image_check_regex.sub(r'\1', sanitized) sanitized = image_check_regex.sub(r'\1', sanitized)
sanitized = link_fix_regex.sub(r'\1https://\2', sanitized) sanitized = link_fix_regex.sub(r'\1https://\2', sanitized)
if FEATURES['MARKUP_COMMANDS']: if FEATURES['MARKUP_COMMANDS']:
sanitized = command_regex.sub(command_regex_matcher, sanitized) sanitized = command_regex.sub(command_regex_matcher, sanitized)
sanitized = markdown(sanitized) sanitized = markdown(sanitized)
sanitized = strikethrough_regex.sub(r'\1<del>\2</del>', sanitized) sanitized = strikethrough_regex.sub(r'\1<del>\2</del>', sanitized)
# replacing zero width characters, overlines, fake colons # 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() return sanitized.strip()
def allowed_attributes_emojis(tag, name, value): def allowed_attributes_emojis(tag, name, value):
if tag == 'img': if tag == 'img':
if name == 'src' and value.startswith('/') and '\\' not in value: return True if name == 'src' and value.startswith('/') and '\\' not in value: return True
if name == 'loading' and value == 'lazy': 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 False, f"The domain '{domain}' is not allowed, please use one of these domains\n\n{approved_embed_hosts}."
return True, "" 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)

View File

@ -129,15 +129,7 @@ def comment(v):
if v.admin_level < PERMS['POST_COMMENT_MODERATION'] and parent.author.any_block_exists(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.") abort(403, "You can't reply to users who have blocked you or users that you have blocked.")
options = [] body, _, options, choices = sanitize_poll_options(v, body, False)
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), "")
if request.files.get("file") and not g.is_tor: if request.files.get("file") and not g.is_tor:
files = request.files.getlist('file')[:4] files = request.files.getlist('file')[:4]
@ -241,25 +233,8 @@ def comment(v):
if c.level == 1: c.top_comment_id = c.id if c.level == 1: c.top_comment_id = c.id
else: c.top_comment_id = parent.top_comment_id else: c.top_comment_id = parent.top_comment_id
for option in options: process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
body_html = filter_emojis_only(option) process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
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)
if SITE == 'pcmemes.net' and c.body.lower().startswith("based"): if SITE == 'pcmemes.net' and c.body.lower().startswith("based"):
execute_basedbot(c, level, body, parent_post, v) execute_basedbot(c, level, body, parent_post, v)
@ -394,27 +369,9 @@ def edit_comment(cid, v):
elif v.bird and len(body) > 140: elif v.bird and len(body) > 140:
abort(403, "You have to type less than 140 characters!") abort(403, "You have to type less than 140 characters!")
for i in list(poll_regex.finditer(body))[:POLL_MAX_OPTIONS]: body, _, options, choices = sanitize_poll_options(v, body, False)
body = body.replace(i.group(0), "") process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
body_html = filter_emojis_only(i.group(1)) process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
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)
execute_antispam_comment_check(body, v) execute_antispam_comment_check(body, v)

View File

@ -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 body = body.strip()[:POST_BODY_LENGTH_LIMIT] # process_files() may be adding stuff to the body
if body != p.body: if body != p.body:
if v and v.admin_level >= PERMS['POST_BETS']: body, bets, options, choices = sanitize_poll_options(v, body, False)
for i in bet_regex.finditer(body): process_poll_options(p, SubmissionOption, bets, 2, "Bet", g.db)
body = body.replace(i.group(0), "") process_poll_options(p, SubmissionOption, options, 0, "Poll", g.db)
body_html = filter_emojis_only(i.group(1)) process_poll_options(p, SubmissionOption, choices, 1, "Poll", g.db)
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)
torture = (v.agendaposter and not v.marseyawarded and p.sub != 'chudrama' and v.id == p.author_id) 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: if len(url) > 2048:
return error("There's a 2048 character limit for URLs.") return error("There's a 2048 character limit for URLs.")
bets = [] body, bets, options, choices = sanitize_poll_options(v, body, True)
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 += process_files(request.files, v) 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 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}: for text in {post.body, post.title, post.url}:
if not execute_blackjack(v, post, text, 'submission'): break if not execute_blackjack(v, post, text, 'submission'): break
if v and v.admin_level >= PERMS['POST_BETS']: process_poll_options(post, SubmissionOption, bets, 2, "Bet", g.db)
for bet in bets: process_poll_options(post, SubmissionOption, options, 0, "Poll", g.db)
body_html = filter_emojis_only(bet) process_poll_options(post, SubmissionOption, choices, 1, "Poll", g.db)
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)
vote = Vote(user_id=v.id, vote = Vote(user_id=v.id,
vote_type=1, 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) n = Notification(comment_id=c_jannied.id, user_id=v.id)
g.db.add(n) 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()): 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) execute_snappy(post, v)
v.post_count = g.db.query(Submission).filter_by(author_id=v.id, deleted_utc=0).count() v.post_count = g.db.query(Submission).filter_by(author_id=v.id, deleted_utc=0).count()
g.db.add(v) g.db.add(v)
@ -896,7 +821,6 @@ def submit_post(v:User, sub=None):
else: sort = v.defaultsortingcomments 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) return render_template('submission.html', v=v, p=post, sort=sort, render_replies=True, offset=0, success=True, sub=post.subr)
@app.post("/delete_post/<pid>") @app.post("/delete_post/<pid>")
@limiter.limit(DEFAULT_RATELIMIT_SLOWER) @limiter.limit(DEFAULT_RATELIMIT_SLOWER)
@auth_required @auth_required

View File

@ -127,6 +127,11 @@ Text 2
<td>#roll</td> <td>#roll</td>
<td>A number 1&ndash;9999.</td> <td>A number 1&ndash;9999.</td>
</tr> </tr>
<tr>
<td>Disable Poll Formatting</td>
<td>#disablepoll</td>
<td>Disables poll options (useful for large code blocks that have things like &amp;&amp;).</td>
</tr>
{%- endif %} {%- endif %}
<tr> <tr>
<td> <td>