diff --git a/files/assets/js/comments+submission_listing.js b/files/assets/js/comments+submission_listing.js index cd14c95dd..e713dc948 100644 --- a/files/assets/js/comments+submission_listing.js +++ b/files/assets/js/comments+submission_listing.js @@ -87,11 +87,12 @@ function poll_vote_1(oid, parentid, kind) { curr.value = full_oid } -function bet_vote(t, oid) { - postToast(t, `/vote/post/option/${oid}`, +function bet_vote(t, oid, kind) { + postToast(t, `/vote/${kind}/option/${oid}`, { }, () => { + t.disabled = true; for(let el of document.getElementsByClassName('bet')) { el.disabled = true; } diff --git a/files/assets/js/core.js b/files/assets/js/core.js index 27f68c9a9..9dfd9f6aa 100644 --- a/files/assets/js/core.js +++ b/files/assets/js/core.js @@ -52,7 +52,6 @@ function postToast(t, url, data, extraActionsOnSuccess, method="POST") { let result let message; let success = xhr[0].status >= 200 && xhr[0].status < 300; - if (success && extraActionsOnSuccess) result = extraActionsOnSuccess(xhr[0]); if (typeof result == "string") { message = result; } else { @@ -65,6 +64,7 @@ function postToast(t, url, data, extraActionsOnSuccess, method="POST") { t.disabled = false; t.classList.remove("disabled"); } + if (success && extraActionsOnSuccess) result = extraActionsOnSuccess(xhr[0]); return success; }; xhr[0].send(xhr[1]); diff --git a/files/classes/comment.py b/files/classes/comment.py index 1e3ad1397..c6c100729 100644 --- a/files/classes/comment.py +++ b/files/classes/comment.py @@ -28,6 +28,68 @@ def normalize_urls_runtime(body, v): body = body.replace('https://instagram.com/p/', 'https://imginn.com/p/') return body + +def add_options(self, body, v): + if isinstance(self, Comment): + kind = 'comment' + else: + kind = 'post' + + if self.options: + curr = [x for x in self.options if x.exclusive and x.voted(v)] + if curr: curr = f" value={kind}-" + str(curr[0].id) + else: curr = '' + body += f'' + winner = [x for x in self.options if x.exclusive == 3] + + for o in self.options: + option_body = '' + + if o.exclusive > 1: + option_body += f'''
= POLL_BET_COINS) or self.total_bet_voted(v): option_body += " disabled " + + option_body += f'''>" + + if o.exclusive == 3: + option_body += " - WINNER!" + + if not winner and v and v.admin_level >= PERMS['POST_BETS_DISTRIBUTE']: + option_body += f'''''' + option_body += "
" + else: + input_type = 'radio' if o.exclusive else 'checkbox' + option_body += f'
''' + + if o.exclusive > 1: s = '##' + elif o.exclusive: s = '&&' + else: s = '$$' + + if f'{s}{o.body_html}{s}' in body: + body = body.replace(f'{s}{o.body_html}{s}', option_body, 1) + elif not o.created_utc or o.created_utc < 1677622270: + body += option_body + + return body + + class Comment(Base): __tablename__ = "comments" @@ -122,12 +184,6 @@ class Comment(Base): def fullname(self): return f"c_{self.id}" - @lazy - def parent(self, db:scoped_session): - if not self.parent_submission: return None - if self.level == 1: return self.post - else: return db.get(Comment, self.parent_comment_id) - @property @lazy def parent_fullname(self): @@ -238,6 +294,15 @@ class Comment(Base): return data + @lazy + def total_bet_voted(self, v): + if "closed" in self.body.lower(): return True + if v: + for o in self.options: + if o.exclusive == 3: return True + if o.exclusive == 2 and o.voted(v): return True + return False + @lazy def total_poll_voted(self, v): if v: @@ -252,37 +317,7 @@ class Comment(Base): body = self.body_html or "" - if self.options: - curr = [x for x in self.options if x.exclusive and x.voted(v)] - if curr: curr = " value=comment-" + str(curr[0].id) - else: curr = '' - body += f'' - - for o in self.options: - input_type = 'radio' if o.exclusive else 'checkbox' - option_body = f'
''' - - if o.exclusive > 1: s = '!!' - elif o.exclusive: s = '&&' - else: s = '$$' - - if f'{s}{o.body_html}{s}' in body: - body = body.replace(f'{s}{o.body_html}{s}', option_body, 1) - elif not o.created_utc or o.created_utc < 1677622270: - body += option_body + body = add_options(self, body, v) if body: body = censor_slurs(body, v) diff --git a/files/classes/polls.py b/files/classes/polls.py index 154cf4fc3..a840984f3 100644 --- a/files/classes/polls.py +++ b/files/classes/polls.py @@ -17,7 +17,7 @@ class SubmissionOption(Base): created_utc = Column(Integer) votes = relationship("SubmissionOptionVote") - post = relationship("Submission", back_populates="options") + parent = relationship("Submission", back_populates="options") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -36,11 +36,6 @@ class SubmissionOption(Base): if not v: return False return v.id in [x.user_id for x in self.votes] - @property - @lazy - def parent(self): - return self.post - class SubmissionOptionVote(Base): @@ -72,7 +67,7 @@ class CommentOption(Base): created_utc = Column(Integer) votes = relationship("CommentOptionVote") - comment = relationship("Comment", back_populates="options") + parent = relationship("Comment", back_populates="options") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -91,11 +86,6 @@ class CommentOption(Base): if not v: return False return v.id in [x.user_id for x in self.votes] - @property - @lazy - def parent(self): - return self.comment - class CommentOptionVote(Base): __tablename__ = "comment_option_votes" diff --git a/files/classes/submission.py b/files/classes/submission.py index f320ad36e..8895a88d0 100644 --- a/files/classes/submission.py +++ b/files/classes/submission.py @@ -13,7 +13,7 @@ from files.helpers.lazy import lazy from files.helpers.regex import * from files.helpers.sorting_and_time import make_age_string -from .comment import normalize_urls_runtime +from .comment import normalize_urls_runtime, add_options from .polls import * from .sub import * from .subscriptions import * @@ -290,57 +290,7 @@ class Submission(Base): body = self.body_html or "" - if self.options: - curr = [x for x in self.options if x.exclusive and x.voted(v)] - if curr: curr = " value=post-" + str(curr[0].id) - else: curr = '' - body += f'' - winner = [x for x in self.options if x.exclusive == 3] - - for o in self.options: - option_body = '' - - if o.exclusive > 1: - option_body += f'''
= POLL_BET_COINS) or self.total_bet_voted(v): option_body += " disabled " - - option_body += f'''>" - - if o.exclusive == 3: - option_body += " - WINNER!" - - if not winner and v and v.admin_level >= PERMS['POST_BETS_DISTRIBUTE']: - option_body += f'''''' - option_body += "
" - else: - input_type = 'radio' if o.exclusive else 'checkbox' - option_body += f'
''' - - if o.exclusive > 1: s = '!!' - elif o.exclusive: s = '&&' - else: s = '$$' - - if f'{s}{o.body_html}{s}' in body: - body = body.replace(f'{s}{o.body_html}{s}', option_body, 1) - elif not o.created_utc or o.created_utc < 1677622270: - body += option_body + body = add_options(self, body, v) body = censor_slurs(body, v) body = normalize_urls_runtime(body, v) diff --git a/files/helpers/actions.py b/files/helpers/actions.py index 76461cb22..eecd4f987 100644 --- a/files/helpers/actions.py +++ b/files/helpers/actions.py @@ -497,7 +497,7 @@ def process_poll_options(v:User, target:Union[Submission, Comment]): patterns = [(poll_regex, 0), (choice_regex, 1)] - if isinstance(target, Submission) and v and v.admin_level >= PERMS['POST_BETS']: + if v.admin_level >= PERMS['POST_BETS']: patterns.append((bet_regex, 2)) option_count = 0 diff --git a/files/routes/admin.py b/files/routes/admin.py index 1ce81a7ed..91f5e0238 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -146,30 +146,32 @@ def remove_admin(v:User, username): return {"message": f"@{user.username} has been removed as admin!"} -@app.post("/distribute/") +@app.post("/distribute//") @limiter.limit('1/second', scope=rpath) @limiter.limit(DEFAULT_RATELIMIT) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_BETS_DISTRIBUTE']) -def distribute(v:User, option_id): +def distribute(v:User, kind, option_id): autojanny = get_account(AUTOJANNY_ID) if autojanny.coins == 0: abort(400, "@AutoJanny has 0 coins") try: option_id = int(option_id) except: abort(400) - try: option = g.db.get(SubmissionOption, option_id) - except: abort(404) + if kind == 'post': cls = SubmissionOption + else: cls = CommentOption + + option = g.db.get(cls, option_id) if option.exclusive != 2: abort(403) option.exclusive = 3 g.db.add(option) - post = option.post + parent = option.parent pool = 0 - for o in post.options: + for o in parent.options: if o.exclusive >= 2: pool += o.upvotes pool *= POLL_BET_COINS @@ -180,27 +182,35 @@ def distribute(v:User, option_id): votes = option.votes coinsperperson = int(pool / len(votes)) - text = f"You won {coinsperperson} coins betting on [{post.title}]({post.shortlink}) :marseyparty:" + text = f"You won {coinsperperson} coins betting on {parent.permalink} :marseyparty:" cid = notif_comment(text) for vote in votes: u = vote.user u.pay_account('coins', coinsperperson) add_notif(cid, u.id, text) - text = f"You lost the {POLL_BET_COINS} coins you bet on [{post.title}]({post.shortlink}) :marseylaugh:" + text = f"You lost the {POLL_BET_COINS} coins you bet on {parent.permalink} :marseylaugh:" cid = notif_comment(text) losing_voters = [] - for o in post.options: + for o in parent.options: if o.exclusive == 2: losing_voters.extend([x.user_id for x in o.votes]) for uid in losing_voters: add_notif(cid, uid, text) - ma = ModAction( - kind="distribute", - user_id=v.id, - target_submission_id=post.id - ) + if isinstance(parent, Submission): + ma = ModAction( + kind="distribute", + user_id=v.id, + target_submission_id=parent.id + ) + else: + ma = ModAction( + kind="distribute", + user_id=v.id, + target_comment_id=parent.id + ) + g.db.add(ma) return {"message": f"Each winner has received {coinsperperson} coins!"} diff --git a/files/routes/polls.py b/files/routes/polls.py index 7dfc01b0d..8b5d881ee 100644 --- a/files/routes/polls.py +++ b/files/routes/polls.py @@ -17,13 +17,13 @@ def vote_option(option_id, v): abort(404) option = g.db.get(SubmissionOption, option_id) if not option: abort(404) - sub = option.post.sub + sub = option.parent.sub if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): 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 option.post.total_bet_voted(v): + if option.parent.total_bet_voted(v): abort(403, "You can't participate in a closed bet!") if not v.charge_account('combined', POLL_BET_COINS): abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!") @@ -55,6 +55,56 @@ def vote_option(option_id, v): return {"message": "Bet successful!"} +@app.post("/vote/comment/option/") +@limiter.limit('1/second', scope=rpath) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) +@is_not_permabanned +def vote_option_comment(option_id, v): + try: + option_id = int(option_id) + except: + abort(404) + option = g.db.get(CommentOption, option_id) + if not option: abort(404) + sub = option.parent.post.sub + if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): + 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 option.parent.total_bet_voted(v): + abort(403, "You can't participate in a closed bet!") + if not v.charge_account('combined', POLL_BET_COINS): + abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!") + g.db.add(v) + autojanny = get_account(AUTOJANNY_ID) + autojanny.pay_account('coins', POLL_BET_COINS) + g.db.add(autojanny) + + if option.exclusive: + vote = g.db.query(CommentOptionVote).join(CommentOption).filter( + CommentOptionVote.user_id==v.id, + CommentOptionVote.comment_id==option.parent_id, + CommentOption.exclusive==1).one_or_none() + if vote: + if option.exclusive == 2: abort(400, "You already voted on this bet!") + for x in vote: + g.db.delete(x) + + existing = g.db.query(CommentOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none() + if not existing: + vote = CommentOptionVote( + option_id=option_id, + user_id=v.id, + comment_id=option.parent_id, + ) + g.db.add(vote) + elif existing: + g.db.delete(existing) + + return {"message": "Bet successful!"} + + @app.get("/votes/post/option/") @limiter.limit(DEFAULT_RATELIMIT) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @@ -67,7 +117,7 @@ def option_votes(option_id, v): option = g.db.get(SubmissionOption, option_id) if not option: abort(404) - if option.post.ghost and v.admin_level < PERMS['SEE_GHOST_VOTES']: + if option.parent.ghost and v.admin_level < PERMS['SEE_GHOST_VOTES']: abort(403) ups = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id).order_by(SubmissionOptionVote.created_utc).all() @@ -90,44 +140,6 @@ def option_votes(option_id, v): ) - -@app.post("/vote/comment/option/") -@limiter.limit('1/second', scope=rpath) -@limiter.limit(DEFAULT_RATELIMIT) -@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) -@is_not_permabanned -def vote_option_comment(option_id, v): - try: - option_id = int(option_id) - except: - abort(404) - option = g.db.get(CommentOption, option_id) - if not option: abort(404) - sub = option.comment.post.sub - if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): - 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( - CommentOptionVote.user_id==v.id, - CommentOptionVote.comment_id==option.parent_id, - CommentOption.exclusive==1).one_or_none() - if vote: - g.db.delete(vote) - - existing = g.db.query(CommentOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none() - if not existing: - vote = CommentOptionVote( - option_id=option_id, - user_id=v.id, - comment_id=option.parent_id, - ) - g.db.add(vote) - elif existing: - g.db.delete(existing) - - return "", 204 - @app.get("/votes/comment/option/") @limiter.limit(DEFAULT_RATELIMIT) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @@ -141,7 +153,7 @@ def option_votes_comment(option_id, v): if not option: abort(404) - if option.comment.ghost and v.admin_level < PERMS['SEE_GHOST_VOTES']: + if option.parent.ghost and v.admin_level < PERMS['SEE_GHOST_VOTES']: abort(403) ups = g.db.query(CommentOptionVote).filter_by(option_id=option_id).order_by(CommentOptionVote.created_utc).all()