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
parent
892e35b1c3
commit
ed0981cbdb
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -127,6 +127,11 @@ Text 2
|
||||||
<td>#roll</td>
|
<td>#roll</td>
|
||||||
<td>A number 1–9999.</td>
|
<td>A number 1–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 &&).</td>
|
||||||
|
</tr>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
Loading…
Reference in New Issue