rDrama/drama/classes/submission.py

612 lines
18 KiB
Python

from flask import render_template, request, g
from sqlalchemy import *
from sqlalchemy.orm import relationship, deferred
import re, random
from urllib.parse import urlparse
from .mix_ins import *
from drama.helpers.base36 import *
from drama.helpers.lazy import lazy
from drama.__main__ import Base
class SubmissionAux(Base):
__tablename__ = "submissions_aux"
# we don't care about this ID
key_id = Column(BigInteger, primary_key=True)
id = Column(BigInteger, ForeignKey("submissions.id"))
title = Column(String(500), default=None)
title_html = Column(String(500), default=None)
url = Column(String(500), default=None)
body = Column(String(10000), default="")
body_html = Column(String(20000), default="")
ban_reason = Column(String(128), default="")
embed_url = Column(String(256), default="")
meta_title=Column(String(512), default="")
meta_description=Column(String(1024), default="")
class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
__tablename__ = "submissions"
id = Column(BigInteger, primary_key=True)
submission_aux = relationship(
"SubmissionAux",
lazy="joined",
uselist=False,
innerjoin=True,
primaryjoin="Submission.id==SubmissionAux.id")
author_id = Column(BigInteger, ForeignKey("users.id"))
repost_id = Column(BigInteger, ForeignKey("submissions.id"), default=0)
edited_utc = Column(BigInteger, default=0)
created_utc = Column(BigInteger, default=0)
thumburl = Column(String, default=None)
is_banned = Column(Boolean, default=False)
views = Column(Integer, default=0)
deleted_utc = Column(Integer, default=0)
banaward = Column(String, default=None)
purged_utc = Column(Integer, default=0)
distinguish_level = Column(Integer, default=0)
gm_distinguish = Column(Integer, ForeignKey("boards.id"), default=0)
distinguished_board = relationship("Board", lazy="joined", primaryjoin="Board.id==Submission.gm_distinguish")
created_str = Column(String(255), default=None)
stickied = Column(Boolean, default=False)
is_pinned = Column(Boolean, default=False)
private = Column(Boolean, default=False)
_comments = relationship(
"Comment",
lazy="dynamic",
primaryjoin="Comment.parent_submission==Submission.id",
backref="submissions")
domain_ref = Column(Integer, ForeignKey("domains.id"))
domain_obj = relationship("Domain")
flags = relationship("Flag", backref="submission")
is_approved = Column(Integer, ForeignKey("users.id"), default=0)
approved_utc = Column(Integer, default=0)
board_id = Column(Integer, ForeignKey("boards.id"), default=None)
original_board_id = Column(Integer, ForeignKey("boards.id"), default=None)
over_18 = Column(Boolean, default=False)
original_board = relationship(
"Board", primaryjoin="Board.id==Submission.original_board_id")
creation_ip = Column(String(64), default="")
mod_approved = Column(Integer, default=None)
accepted_utc = Column(Integer, default=0)
has_thumb = Column(Boolean, default=False)
post_public = Column(Boolean, default=True)
score_hot = Column(Float, default=0)
score_top = Column(Float, default=1)
score_activity = Column(Float, default=0)
is_offensive = Column(Boolean, default=False)
is_nsfl = Column(Boolean, default=False)
board = relationship(
"Board",
lazy="joined",
innerjoin=True,
primaryjoin="Submission.board_id==Board.id")
author = relationship(
"User",
lazy="joined",
innerjoin=True,
primaryjoin="Submission.author_id==User.id")
is_pinned = Column(Boolean, default=False)
score_best = Column(Float, default=0)
reports = relationship("Report", backref="submission")
is_bot = Column(Boolean, default=False)
upvotes = Column(Integer, default=1)
downvotes = Column(Integer, default=0)
creation_region=Column(String(2), default=None)
app_id=Column(Integer, ForeignKey("oauth_apps.id"), default=None)
oauth_app=relationship("OauthApp")
approved_by = relationship(
"User",
uselist=False,
primaryjoin="Submission.is_approved==User.id")
# not sure if we need this
reposts = relationship("Submission", lazy="joined", remote_side=[id])
# These are virtual properties handled as postgres functions server-side
# There is no difference to SQLAlchemy, but they cannot be written to
ups = deferred(Column(Integer, server_default=FetchedValue()))
downs = deferred(Column(Integer, server_default=FetchedValue()))
comment_count = Column(Integer, server_default=FetchedValue())
score = deferred(Column(Float, server_default=FetchedValue()))
awards = relationship("AwardRelationship", lazy="joined")
rank_hot = deferred(Column(Float, server_default=FetchedValue()))
rank_fiery = deferred(Column(Float, server_default=FetchedValue()))
rank_activity = deferred(Column(Float, server_default=FetchedValue()))
rank_best = deferred(Column(Float, server_default=FetchedValue()))
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:
kwargs["created_utc"] = int(time.time())
kwargs["created_str"] = time.strftime(
"%I:%M %p on %d %b %Y", time.gmtime(
kwargs["created_utc"]))
kwargs["creation_ip"] = request.remote_addr
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Submission(id={self.id})>"
@property
@lazy
def board_base36id(self):
return base36encode(self.board_id)
@property
@lazy
def is_deleted(self):
return bool(self.deleted_utc)
@property
@lazy
def hotscore(self):
return 10000000*(self.upvotes - self.downvotes + 1)/(((self.age+3600)/1000)**(1.35))
@property
@lazy
def score_disputed(self):
return (self.upvotes+1) * (self.downvotes+1)
@property
def is_repost(self):
return bool(self.repost_id)
@property
def is_archived(self):
return false
@property
@lazy
def fullname(self):
return f"t2_{self.base36id}"
@property
@lazy
def permalink(self):
output = self.title.lower()
output = re.sub('&\w{2,3};', '', output)
output = [re.sub('\W', '', word) for word in output.split()]
output = [x for x in output if x][0:6]
output = '-'.join(output)
if not output:
output = '-'
return f"/post/{self.id}/{output}"
@property
def is_archived(self):
now = int(time.time())
cutoff = now - (60 * 60 * 24 * 180)
return self.created_utc < cutoff
def rendered_page(self, sort=None, comment=None, comment_info=None, v=None):
# check for banned
if v and v.admin_level >= 3:
template = "submission.html"
elif self.is_banned:
template = "submission_banned.html"
else:
template = "submission.html"
# load and tree comments
# calling this function with a comment object will do a comment
# permalink thing
if "replies" not in self.__dict__ and "_preloaded_comments" in self.__dict__:
self.tree_comments(comment=comment)
# return template
is_allowed_to_comment = self.board.can_comment(
v) and not self.is_archived
return render_template(template,
v=v,
p=self,
sort=sort,
linked_comment=comment,
comment_info=comment_info,
is_allowed_to_comment=is_allowed_to_comment,
render_replies=True,
)
@property
@lazy
def domain(self):
if not self.url:
return "text post"
domain = urlparse(self.url).netloc
if domain.startswith("www."):
domain = domain.split("www.")[1]
return domain.replace("old.reddit.com", "reddit.com")
def tree_comments(self, comment=None, v=None):
comments = self.__dict__.get('_preloaded_comments',[])
if not comments:
return
pinned_comment=[]
index = {}
for c in comments:
if c.is_pinned and c.parent_fullname==self.fullname:
pinned_comment+=[c]
continue
if c.parent_fullname in index:
index[c.parent_fullname].append(c)
else:
index[c.parent_fullname] = [c]
for c in comments:
c.__dict__["replies"] = index.get(c.fullname, [])
if comment:
self.__dict__["replies"] = [comment]
else:
self.__dict__["replies"] = pinned_comment + index.get(self.fullname, [])
@property
def active_flags(self):
if self.is_approved:
return 0
else:
return len(self.flags)
@property
#@lazy
def thumb_url(self):
if self.over_18: return f"/assets/images/nsfw.png"
elif self.has_thumb:
if self.thumburl: return self.thumburl
else: return f"https://s3.eu-central-1.amazonaws.com/i.drama.ga/posts/{self.base36id}/thumb.png"
elif self.is_image:
return self.url
else:
return None
def visibility_reason(self, v):
if not v or self.author_id == v.id:
return "this is your content."
elif self.is_pinned:
return "a guildmaster has pinned it."
elif self.board.has_mod(v):
return f"you are a guildmaster of +{self.board.name}."
elif self.board.has_contributor(v):
return f"you are an approved contributor in +{self.board.name}."
elif v.admin_level >= 4:
return "you are a Drama admin."
@property
def json_raw(self):
data = {'author_name': self.author.username if not self.author.is_deleted else None,
'permalink': self.permalink,
'is_banned': bool(self.is_banned),
'is_deleted': self.is_deleted,
'created_utc': self.created_utc,
'id': self.base36id,
'fullname': self.fullname,
'title': self.title,
'is_nsfw': self.over_18,
'is_nsfl': self.is_nsfl,
'is_bot': self.is_bot,
'thumb_url': self.thumb_url,
'domain': self.domain,
'is_archived': self.is_archived,
'url': self.url,
'body': self.body,
'body_html': self.body_html,
'created_utc': self.created_utc,
'edited_utc': self.edited_utc or 0,
'guild_name': self.board.name,
'guild_id': base36encode(self.board_id),
'comment_count': self.comment_count,
'score': self.score_fuzzed,
'upvotes': self.upvotes_fuzzed,
'downvotes': self.downvotes_fuzzed,
'award_count': self.award_count,
'is_offensive': self.is_offensive,
'meta_title': self.meta_title,
'meta_description': self.meta_description,
'voted': self.voted
}
if self.ban_reason:
data["ban_reason"]=self.ban_reason
if self.board_id != self.original_board_id and self.original_board:
data['original_guild_name'] = self.original_board.name
data['original_guild_id'] = base36encode(self.original_board_id)
return data
@property
def json_core(self):
if self.is_banned:
return {'is_banned': True,
'is_deleted': self.is_deleted,
'ban_reason': self.ban_reason,
'id': self.base36id,
'title': self.title,
'permalink': self.permalink,
'guild_name': self.board.name
}
elif self.is_deleted:
return {'is_banned': bool(self.is_banned),
'is_deleted': True,
'id': self.base36id,
'title': self.title,
'permalink': self.permalink,
'guild_name': self.board.name
}
return self.json_raw
@property
def json(self):
data=self.json_core
if self.deleted_utc > 0 or self.is_banned:
return data
data["author"]=self.author.json_core
data["guild"]=self.board.json_core
data["original_guild"]=self.original_board.json_core if not self.board_id==self.original_board_id else None
data["comment_count"]=self.comment_count
if "replies" in self.__dict__:
data["replies"]=[x.json_core for x in self.replies]
if "_voted" in self.__dict__:
data["voted"] = self._voted
return data
@property
def voted(self):
return self._voted if "_voted" in self.__dict__ else 0
@property
def user_title(self):
return self._title if "_title" in self.__dict__ else self.author.title
@property
def title(self):
return self.submission_aux.title
@title.setter
def title(self, x):
self.submission_aux.title = x
g.db.add(self.submission_aux)
@property
def url(self):
return self.submission_aux.url
@url.setter
def url(self, x):
self.submission_aux.url = x
g.db.add(self.submission_aux)
def realurl(self, v):
if v and v.agendaposter and random.randint(1, 10) < 4:
return 'https://secure.actblue.com/donate/ms_blm_homepage_2019'
elif self.url:
if v and not v.oldreddit: return self.url.replace("old.reddit.com", "reddit.com")
if self.url: return self.url
return ""
@property
def body(self):
return self.submission_aux.body
@body.setter
def body(self, x):
self.submission_aux.body = x
g.db.add(self.submission_aux)
@property
def body_html(self):
return self.submission_aux.body_html
@body_html.setter
def body_html(self, x):
self.submission_aux.body_html = x
g.db.add(self.submission_aux)
def realbody(self, v):
body = self.submission_aux.body_html
if not v or v.slurreplacer: body = body.replace(" nigger"," 🏀").replace(" Nigger"," 🏀").replace(" NIGGER"," 🏀").replace(" pedo"," libertarian").replace(" Pedo"," Libertarian ").replace(" PEDO"," LIBERTARIAN ").replace(" tranny"," 🚄").replace(" Tranny"," 🚄").replace(" TRANNY"," 🚄").replace(" fag"," cute twink").replace(" Fag"," Cute twink").replace(" FAG"," CUTE TWINK").replace(" faggot"," cute twink").replace(" Faggot"," Cute twink").replace(" FAGGOT"," CUTE TWINK").replace(" trump"," DDR").replace(" Trump"," DDR").replace(" TRUMP"," DDR").replace(" biden"," DDD").replace(" Biden"," DDD").replace(" BIDEN"," DDD").replace(" steve akins"," penny verity oaken").replace(" Steve Akins"," Penny Verity Oaken").replace(" STEVE AKINS"," PENNY VERITY OAKEN").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" kill yourself"," keep yourself safe").replace(" KILL YOURSELF"," KEEP YOURSELF SAFE")
if v and not v.oldreddit: body = body.replace("old.reddit.com", "reddit.com")
return body
@property
def title_html(self):
return self.submission_aux.title_html
@title_html.setter
def title_html(self, x):
self.submission_aux.title_html = x
g.db.add(self.submission_aux)
def realtitle(self, v):
if self.title_html: title = self.title_html
else: title = self.title
if not v or v.slurreplacer: title = title.replace(" nigger"," 🏀").replace(" Nigger"," 🏀").replace(" NIGGER"," 🏀").replace(" pedo"," libertarian").replace(" Pedo"," Libertarian ").replace(" PEDO"," LIBERTARIAN ").replace(" tranny"," 🚄").replace(" Tranny"," 🚄").replace(" TRANNY"," 🚄").replace(" fag"," cute twink").replace(" Fag"," Cute twink").replace(" FAG"," CUTE TWINK").replace(" faggot"," cute twink").replace(" Faggot"," Cute twink").replace(" FAGGOT"," CUTE TWINK").replace(" trump"," DDR").replace(" Trump"," DDR").replace(" TRUMP"," DDR").replace(" biden"," DDD").replace(" Biden"," DDD").replace(" BIDEN"," DDD").replace(" steve akins"," penny verity oaken").replace(" Steve Akins"," Penny Verity Oaken").replace(" STEVE AKINS"," PENNY VERITY OAKEN").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" RETARD"," RSLUR").replace(" rapist"," male feminist").replace(" Rapist"," Male feminist").replace(" RAPIST"," MALE FEMINIST").replace(" kill yourself"," keep yourself safe").replace(" KILL YOURSELF"," KEEP YOURSELF SAFE")
return title
@property
def ban_reason(self):
return self.submission_aux.ban_reason
@ban_reason.setter
def ban_reason(self, x):
self.submission_aux.ban_reason = x
g.db.add(self.submission_aux)
@property
def embed_url(self):
return self.submission_aux.embed_url
@embed_url.setter
def embed_url(self, x):
self.submission_aux.embed_url = x
g.db.add(self.submission_aux)
@property
def meta_title(self):
return self.submission_aux.meta_title
@meta_title.setter
def meta_title(self, x):
self.submission_aux.meta_title=x
g.db.add(self.submission_aux)
@property
def meta_description(self):
return self.submission_aux.meta_description
@meta_description.setter
def meta_description(self, x):
self.submission_aux.meta_description=x
g.db.add(self.submission_aux)
def is_guildmaster(self, perm=None):
mod=self.__dict__.get('_is_guildmaster', False)
if not mod:
return False
elif not perm:
return True
else:
return mod.perm_full or mod.__dict__[f"perm_{perm}"]
return output
@property
def is_blocking_guild(self):
return self.__dict__.get('_is_blocking_guild', False)
@property
def is_blocked(self):
return self.__dict__.get('_is_blocked', False)
@property
def is_blocking(self):
return self.__dict__.get('_is_blocking', False)
@property
def is_subscribed(self):
return self.__dict__.get('_is_subscribed', False)
@property
def is_public(self):
return self.post_public or not self.board.is_private
@property
def flag_count(self):
return len(self.flags)
@property
def report_count(self):
return len(self.reports)
@property
def award_count(self):
return len(self.awards)
@property
def embed_template(self):
return f"site_embeds/{self.domain_obj.embed_template}.html"
@property
def flagged_by(self):
return [x.user for x in self.flags]
@property
def is_image(self):
if self.url: return self.url.endswith('jpg') or self.url.endswith('png') or self.url.endswith('.gif') or self.url.endswith('jpeg') or self.url.endswith('?maxwidth=9999') or self.url.endswith('?maxwidth=8888')
else: return False
@property
def self_download_json(self):
#This property should never be served to anyone but author and admin
if not self.is_banned and self.deleted_utc == 0:
return self.json_core
data= {
"title":self.title,
"author": self.author.username,
"url": self.url,
"body": self.body,
"body_html": self.body_html,
"is_banned": bool(self.is_banned),
"deleted_utc": self.deleted_utc,
'created_utc': self.created_utc,
'id': self.base36id,
'fullname': self.fullname,
'guild_name': self.board.name,
'comment_count': self.comment_count,
'permalink': self.permalink
}
if self.original_board_id and (self.original_board_id!= self.board_id):
data['original_guild_name'] = self.original_board.name
return data
@property
def json_admin(self):
data=self.json_raw
data["creation_ip"]=self.creation_ip
data["creation_region"]=self.creation_region
return data
@property
def is_exiled_for(self):
return self.__dict__.get('_is_exiled_for', None)
class SaveRelationship(Base, Stndrd):
__tablename__="save_relationship"
id=Column(Integer, primary_key=true)
user_id=Column(Integer, ForeignKey("users.id"))
submission_id=Column(Integer, ForeignKey("submissions.id"))
type=Column(Integer)