import re import time from urllib.parse import urlencode, urlparse, parse_qs from flask import * from sqlalchemy import * from sqlalchemy.orm import relationship from files.__main__ import Base from files.classes.votes import CommentVote from files.helpers.const import * from files.helpers.regex import * from files.helpers.regex import * from files.helpers.lazy import lazy from .flags import CommentFlag from .votes import CommentVote from .saves import CommentSaveRelationship from random import randint from math import floor def normalize_urls_runtime(body, v): if not v: return body if v.reddit != 'old.reddit.com': body = reddit_to_vreddit_regex.sub(rf'\1https://{v.reddit}/\2/', body) if v.nitter: body = body.replace('https://twitter.com/', 'https://nitter.42l.fr/') body = body.replace('https://nitter.42l.fr/i/', 'https://twitter.com/i/') if v.imginn: body = body.replace('https://instagram.com/', 'https://imginn.com/') return body class Comment(Base): __tablename__ = "comments" id = Column(Integer, primary_key=True) author_id = Column(Integer, ForeignKey("users.id")) parent_submission = Column(Integer, ForeignKey("submissions.id")) created_utc = Column(Integer) edited_utc = Column(Integer, default=0) is_banned = Column(Boolean, default=False) ghost = Column(Boolean, default=False) bannedfor = Column(String) distinguish_level = Column(Integer, default=0) deleted_utc = Column(Integer, default=0) is_approved = Column(Integer, ForeignKey("users.id")) level = Column(Integer, default=1) parent_comment_id = Column(Integer, ForeignKey("comments.id")) top_comment_id = Column(Integer) over_18 = Column(Boolean, default=False) is_bot = Column(Boolean, default=False) stickied = Column(String) stickied_utc = Column(Integer) sentto = Column(Integer, ForeignKey("users.id")) app_id = Column(Integer, ForeignKey("oauth_apps.id")) upvotes = Column(Integer, default=1) downvotes = Column(Integer, default=0) realupvotes = Column(Integer, default=1) body = Column(String) body_html = Column(String) ban_reason = Column(String) wordle_result = Column(String) treasure_amount = Column(String) oauth_app = relationship("OauthApp") post = relationship("Submission", back_populates="comments") author = relationship("User", primaryjoin="User.id==Comment.author_id") senttouser = relationship("User", primaryjoin="User.id==Comment.sentto") parent_comment = relationship("Comment", remote_side=[id], back_populates="child_comments") child_comments = relationship("Comment", order_by="Comment.stickied, Comment.realupvotes.desc()", remote_side=[parent_comment_id], back_populates="parent_comment") awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment") flags = relationship("CommentFlag", order_by="CommentFlag.created_utc") options = relationship("CommentOption", order_by="CommentOption.id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) super().__init__(*args, **kwargs) def __repr__(self): return f"" @lazy def can_see(self, v): if SITE != 'rdrama.net': return True if not self.parent_submission: return True if self.post.sub != 'chudrama': return True if v: if v.can_see_chudrama: return True if v.id == self.author_id: return True if v.id == self.post.author_id: return True return False @property @lazy def top_comment(self): return g.db.get(Comment, self.top_comment_id) @property @lazy def controversial(self): if self.downvotes > 5 and 0.25 < self.upvotes / self.downvotes < 4: return True return False @property @lazy def created_datetime(self): return str(time.strftime("%d/%B/%Y %H:%M:%S UTC", time.gmtime(self.created_utc))) @property @lazy def age_string(self): notif_utc = self.__dict__.get("notif_utc") if notif_utc: timestamp = notif_utc elif self.created_utc: timestamp = self.created_utc else: return None age = int(time.time()) - timestamp if age < 60: return "just now" elif age < 3600: minutes = int(age / 60) return f"{minutes}m ago" elif age < 86400: hours = int(age / 3600) return f"{hours}hr ago" elif age < 2678400: days = int(age / 86400) return f"{days}d ago" now = time.gmtime() ctd = time.gmtime(timestamp) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) if now.tm_mday < ctd.tm_mday: months -= 1 if months < 12: return f"{months}mo ago" else: years = int(months / 12) return f"{years}yr ago" @property @lazy def edited_string(self): age = int(time.time()) - self.edited_utc if age < 60: return "just now" elif age < 3600: minutes = int(age / 60) return f"{minutes}m ago" elif age < 86400: hours = int(age / 3600) return f"{hours}hr ago" elif age < 2678400: days = int(age / 86400) return f"{days}d ago" now = time.gmtime() ctd = time.gmtime(self.edited_utc) months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year) if now.tm_mday < ctd.tm_mday: months -= 1 if months < 12: return f"{months}mo ago" else: years = int(months / 12) return f"{years}yr ago" @property @lazy def score(self): return self.upvotes - self.downvotes @property @lazy def fullname(self): return f"c_{self.id}" @property @lazy def parent(self): if not self.parent_submission: return None if self.level == 1: return self.post else: return g.db.get(Comment, self.parent_comment_id) @property @lazy def parent_fullname(self): if self.parent_comment_id: return f"c_{self.parent_comment_id}" elif self.parent_submission: return f"p_{self.parent_submission}" @lazy def replies(self, sort=None): if self.replies2 != None: replies = self.replies2 elif not self.parent_submission: replies = g.db.query(Comment).filter_by(parent_comment_id=self.id).order_by(Comment.id).all() else: replies = self.child_comments return [x for x in replies if not x.author.shadowbanned] @lazy def replies3(self, sort): if self.replies2 != None: return self.replies2 if not self.parent_submission: return g.db.query(Comment).filter_by(parent_comment_id=self.id).order_by(Comment.id).all() return self.child_comments @property def replies2(self): return self.__dict__.get("replies2") @replies2.setter def replies2(self, value): self.__dict__["replies2"] = value @property @lazy def shortlink(self): return f"{self.post.shortlink}/{self.id}?context=8#context" @property @lazy def permalink(self): return f"{SITE_FULL}{self.shortlink}" @property @lazy def log_link(self): return f"{SITE_FULL}/transfers/{self.id}" @property @lazy def morecomments(self): return f"{self.post.permalink}/{self.id}#context" @property @lazy def author_name(self): if self.ghost: return '👻' if self.author.earlylife: expiry = int(self.author.earlylife - time.time()) if expiry > 86400: name = self.author.username for i in range(int(expiry / 86400 + 1)): name = f'((({name})))' return name return f'((({self.author.username})))' return self.author.username @lazy def award_count(self, kind, v): if v and v.poor and kind.islower(): return 0 return len([x for x in self.awards if x.kind == kind]) @property def json(self): if self.is_banned: data = {'is_banned': True, 'ban_reason': self.ban_reason, 'id': self.id, 'post': self.post.id if self.post else 0, 'level': self.level, 'parent': self.parent_fullname } elif self.deleted_utc: data = {'deleted_utc': self.deleted_utc, 'id': self.id, 'post': self.post.id if self.post else 0, 'level': self.level, 'parent': self.parent_fullname } else: flags = {} for f in self.flags: flags[f.user.username] = f.reason data = { 'id': self.id, 'level': self.level, 'author_name': self.author_name, 'body': self.body, 'body_html': self.body_html, 'is_bot': self.is_bot, 'created_utc': self.created_utc, 'edited_utc': self.edited_utc or 0, 'is_banned': bool(self.is_banned), 'deleted_utc': self.deleted_utc, 'is_nsfw': self.over_18, 'permalink': f'/comment/{self.id}', 'stickied': self.stickied, 'distinguish_level': self.distinguish_level, 'post_id': self.post.id if self.post else 0, 'score': self.score, 'upvotes': self.upvotes, 'downvotes': self.downvotes, 'is_bot': self.is_bot, 'flags': flags, 'author': '👻' if self.ghost else self.author.json, 'replies': [x.json for x in self.replies()] } if self.level >= 2: data['parent_comment_id'] = self.parent_comment_id return data @lazy def realbody(self, v): if self.post and self.post.club and not (v and (v.paid_dues or v.id in [self.author_id, self.post.author_id])): return f"

{CC} ONLY

" body = self.body_html or "" if body: body = censor_slurs(body, v) body = normalize_urls_runtime(body, v) if v and v.controversial: captured = [] for i in controversial_regex.finditer(body): if i.group(1) in captured: continue captured.append(i.group(1)) url = i.group(1) p = urlparse(url).query p = parse_qs(p, keep_blank_values=True) if 'sort' not in p: p['sort'] = ['controversial'] url_noquery = url.split('?')[0] body = body.replace(f'"{url}"', f'"{url_noquery}?{urlencode(p, True)}"') body = body.replace(f'>{url}<', f'>{url_noquery}?{urlencode(p, True)}<') if v and v.shadowbanned and v.id == self.author_id and 86400 > time.time() - self.created_utc > 60: ti = max(int((time.time() - self.created_utc)/60), 1) maxupvotes = min(ti, 13) rand = randint(0, maxupvotes) if self.upvotes < rand: amount = randint(0, 3) if amount == 1: self.upvotes += amount g.db.add(self) 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' body += f'
''' if not self.ghost and self.author.show_sig(v): body += f"
{self.author.sig_html}" return body @lazy def plainbody(self, v): if self.post and self.post.club and not (v and (v.paid_dues or v.id in [self.author_id, self.post.author_id])): return f"

{CC} ONLY

" body = self.body if not body: return "" return censor_slurs(body, v) @lazy def collapse_for_user(self, v, path): if v and self.author_id == v.id: return False if path == '/admin/removed/comments': return False if '?context' in path or f'/{self.id}' in path: return False if self.over_18 and not (v and v.over_18) and not (self.post and self.post.over_18): return True if self.is_banned: return True if (self.wordle_result) and (not self.body or len(self.body_html) <= 100) and 9 > self.level > 1: return True if v and v.filter_words and self.body and any(x in self.body for x in v.filter_words): return True return False @property @lazy def is_op(self): return self.author_id==self.post.author_id @lazy def filtered_flags(self, v): return [f for f in self.flags if (v and v.shadowbanned) or not f.user.shadowbanned] @lazy def active_flags(self, v): return len(self.filtered_flags(v)) @lazy def wordle_html(self, v): if not self.wordle_result: return '' split_wordle_result = self.wordle_result.split('_') wordle_guesses = split_wordle_result[0] wordle_status = split_wordle_result[1] wordle_answer = split_wordle_result[2] body = f"{wordle_guesses}" if wordle_status == 'active' and v and v.id == self.author_id: body += f'''''' elif wordle_status == 'won': body += "Correct!" elif wordle_status == 'lost': body += f"Lost. The answer was: {wordle_answer}" body += '' return body