Merge branch 'frost' of https://github.com/Aevann1/rDrama into frost

remotes/1693176582716663532/tmp_refs/heads/watchparty
Aevann1 2022-09-29 10:23:03 +00:00
commit 97e66b8726
82 changed files with 761 additions and 267 deletions

View File

@ -0,0 +1 @@

View File

@ -137,4 +137,4 @@ elif "load_chat" in argv:
else:
from files.routes import *
stdout.flush()
stdout.flush()

View File

@ -154,4 +154,4 @@ blockquote a {
h5.post-title a:visited {
color: #949494 !important;
}
}

View File

@ -87,4 +87,4 @@
51% {transform: translate(105vw,-86vh) rotate(180deg);}
100% {transform: translate(-70vw,-86vh) rotate(180deg);}
}
}
}

View File

@ -106,4 +106,4 @@ blockquote {
h5.post-title a:visited {
color: #949494 !important;
}
}

View File

@ -94,4 +94,4 @@ pre {
h5.post-title a:visited {
color: #7a7a7a !important;
}
}

View File

@ -145,4 +145,4 @@ color: var(--gray-700);
h5.post-title a:visited {
color: #6e6e6e !important;
}
}

View File

@ -82,4 +82,4 @@ blockquote {
h5.post-title a:visited {
color: #7a7a7a !important;
}
}

View File

@ -6031,6 +6031,9 @@ g {
.fa-hat-cowboy:before{content:"\f8c0"}
.fa-cloud-rainbow:before{content:"\f73e"}
.fa-telegram:before{content:"\f2c6"}
.fa-css3-alt:before{content:"\f38b"}
.fa-landscape:before{content:"\e1b5"}
.fa-user-ninja:before{content:"\f504"}
.pronouns {
font-size: 9px;
@ -6283,4 +6286,4 @@ div.markdown {
.bug {
pointer-events: none !important;
}
}

View File

@ -63,4 +63,4 @@ body, .navbar-light, .navbar-dark, .card, .modal-content, .comment-write textare
h5.post-title a:visited {
color: #6e6e6e !important;
}
}

View File

@ -78,4 +78,4 @@ pre {
h5.post-title a:visited {
color: #7a7a7a !important;
}
}

View File

@ -232,4 +232,4 @@
h5.post-title a:visited {
color: #b0b0b0 !important;
}
}

View File

@ -168,4 +168,4 @@ blockquote {
h5.post-title a:visited {
color: #5c5c5c !important;
}
}

File diff suppressed because one or more lines are too long

View File

@ -25,4 +25,5 @@ from .casino_game import *
from .hats import *
from .marsey import *
from .transactions import *
from .streamers import *
from .streamers import *
from .sub_logs import *

View File

@ -76,4 +76,4 @@ class ClientAuth(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<ClientAuth(user_id={self.user_id}, oauth_client={self.oauth_client})>"
return f"<ClientAuth(user_id={self.user_id}, oauth_client={self.oauth_client})>"

View File

@ -23,8 +23,8 @@ def normalize_urls_runtime(body, v):
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/')
body = body.replace('https://twitter.com/', 'https://nitter.lacontrevoie.fr/')
body = body.replace('https://nitter.lacontrevoie.fr/i/', 'https://twitter.com/i/')
if v.imginn:
body = body.replace('https://instagram.com/', 'https://imginn.com/')
@ -447,4 +447,4 @@ class Comment(Base):
body += f"<strong class='ml-2'>Lost. The answer was: {wordle_answer}</strong>"
body += '</span>'
return body
return body

View File

@ -14,4 +14,4 @@ class BannedDomain(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<BannedDomain(domain={self.domain})>"
return f"<BannedDomain(domain={self.domain})>"

View File

@ -18,4 +18,4 @@ class Exile(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Exile(user_id={self.user_id}, sub={self.sub})>"
return f"<Exile(user_id={self.user_id}, sub={self.sub})>"

View File

@ -49,4 +49,4 @@ class CommentFlag(Base):
@lazy
def realreason(self, v):
return censor_slurs(self.reason, v)
return censor_slurs(self.reason, v)

View File

@ -17,4 +17,4 @@ class Follow(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Follow(id={self.id})>"
return f"<Follow(id={self.id})>"

View File

@ -61,4 +61,4 @@ class Hat(Base):
@lazy
def censored_description(self, v):
return self.hat_def.censored_description(v)
return self.hat_def.censored_description(v)

View File

@ -17,4 +17,4 @@ class Marsey(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Marsey(name={self.name})>"
return f"<Marsey(name={self.name})>"

View File

@ -16,4 +16,4 @@ class Mod(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Mod(user_id={self.user_id}, sub={self.sub})>"
return f"<Mod(user_id={self.user_id}, sub={self.sub})>"

View File

@ -70,10 +70,6 @@ class ModAction(Base):
else:
return self._note or ""
@note.setter
def note(self, x):
self._note=x
@property
@lazy
def string(self):
@ -149,16 +145,6 @@ ACTIONTYPES = {
"icon": 'fa-user-slash',
"color": 'bg-danger'
},
'change_sidebar': {
"str": 'changed the sidebar',
"icon": 'fa-columns',
"color": 'bg-primary'
},
'check': {
"str": 'gave {self.target_link} a checkmark',
"icon": 'fa-badge-check',
"color": 'bg-success'
},
'club_allow': {
"str": 'allowed user {self.target_link} into the {cc}',
"icon": 'fa-golf-club',
@ -275,7 +261,7 @@ ACTIONTYPES = {
"color": 'bg-success'
},
'make_admin': {
"str": 'made {self.target_link} admin',
"str": 'made {self.target_link} an admin',
"icon": 'fa-user-crown',
"color": 'bg-success'
},
@ -305,7 +291,7 @@ ACTIONTYPES = {
"color": 'bg-danger'
},
'pin_comment': {
"str": 'pinned a {self.target_link}',
"str": 'pinned {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-success'
},
@ -389,11 +375,6 @@ ACTIONTYPES = {
"icon": 'fa-user',
"color": 'bg-success'
},
'uncheck': {
"str": 'removed checkmark from {self.target_link}',
"icon": 'fa-badge-check',
"color": 'bg-muted'
},
'undistinguish_comment': {
"str": 'un-distinguished {self.target_link}',
"icon": 'fa-crown',
@ -410,12 +391,12 @@ ACTIONTYPES = {
"color": 'bg-success'
},
'unpin_comment': {
"str": 'un-pinned a {self.target_link}',
"str": 'unpinned {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-muted'
},
'unpin_post': {
"str": 'un-pinned post {self.target_link}',
"str": 'unpinned post {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-muted'
},
@ -448,4 +429,4 @@ ACTIONTYPES = {
ACTIONTYPES2 = deepcopy(ACTIONTYPES)
ACTIONTYPES2.pop("shadowban")
ACTIONTYPES2.pop("unshadowban")
ACTIONTYPES2.pop("unshadowban")

View File

@ -20,4 +20,4 @@ class Notification(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Notification(id={self.id})>"
return f"<Notification(id={self.id})>"

View File

@ -101,4 +101,4 @@ class CommentOptionVote(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<CommentOptionVote(option_id={self.option_id}, user_id={self.user_id})>"
return f"<CommentOptionVote(option_id={self.option_id}, user_id={self.user_id})>"

View File

@ -36,4 +36,4 @@ class CommentSaveRelationship(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<CommentSaveRelationship(user_id={self.user_id}, comment_id={self.comment_id})>"
return f"<CommentSaveRelationship(user_id={self.user_id}, comment_id={self.comment_id})>"

View File

@ -10,4 +10,4 @@ if SITE == 'pcmemes.net':
id = Column(String, primary_key=True)
def __repr__(self):
return f"<Streamer(id={self.id})>"
return f"<Streamer(id={self.id})>"

View File

@ -65,4 +65,4 @@ class Sub(Base):
@property
@lazy
def follow_num(self):
return len(self.followers)
return len(self.followers)

View File

@ -13,4 +13,4 @@ class SubBlock(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<SubBlock(user_id={self.user_id}, sub={self.sub})>"
return f"<SubBlock(user_id={self.user_id}, sub={self.sub})>"

View File

@ -13,4 +13,4 @@ class SubJoin(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<SubJoin(user_id={self.user_id}, sub={self.sub})>"
return f"<SubJoin(user_id={self.user_id}, sub={self.sub})>"

View File

@ -0,0 +1,188 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from files.__main__ import Base
import time
from files.helpers.lazy import lazy
from files.helpers.const import *
from files.helpers.regex import censor_slurs
class SubAction(Base):
__tablename__ = "subactions"
id = Column(Integer, primary_key=True)
sub = Column(String, ForeignKey("subs.name"))
user_id = Column(Integer, ForeignKey("users.id"))
kind = Column(String)
target_user_id = Column(Integer, ForeignKey("users.id"))
target_submission_id = Column(Integer, ForeignKey("submissions.id"))
target_comment_id = Column(Integer, ForeignKey("comments.id"))
_note=Column(String)
created_utc = Column(Integer)
user = relationship("User", primaryjoin="User.id==SubAction.user_id")
target_user = relationship("User", primaryjoin="User.id==SubAction.target_user_id")
target_post = relationship("Submission")
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"<SubAction(id={self.id})>"
@property
@lazy
def age_string(self):
age = int(time.time()) - self.created_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.created_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 string(self):
output = ACTIONTYPES[self.kind]["str"].format(self=self, cc=CC_TITLE)
if self._note: output += f" <i>({self._note})</i>"
return output
@property
@lazy
def target_link(self):
if self.target_user: return f'<a href="{self.target_user.url}">{self.target_user.username}</a>'
elif self.target_post:
if self.target_post.club: return f'<a href="{self.target_post.permalink}">{CC} ONLY</a>'
return censor_slurs(f'<a href="{self.target_post.permalink}">{self.target_post.title_html}</a>', None)
elif self.target_comment_id: return f'<a href="/comment/{self.target_comment_id}?context=8#context">comment</a>'
@property
@lazy
def icon(self):
return ACTIONTYPES[self.kind]['icon']
@property
@lazy
def color(self):
return ACTIONTYPES[self.kind]['color']
@property
@lazy
def permalink(self):
return f"{SITE_FULL}/log/{self.id}"
ACTIONTYPES = {
'exile_user': {
"str": 'exiled user {self.target_link}',
"icon": 'fa-user-slash',
"color": 'bg-danger'
},
'unexile_user': {
"str": 'unexiled user {self.target_link}',
"icon": 'fa-user',
"color": 'bg-success'
},
'make_mod': {
"str": 'made {self.target_link} a mod',
"icon": 'fa-user-crown',
"color": 'bg-success'
},
'remove_mod': {
"str": 'removed {self.target_link} as mod',
"icon": 'fa-user-crown',
"color": 'bg-danger'
},
'kick_post': {
"str": 'kicked post {self.target_link}',
"icon": 'fa-feather-alt',
"color": 'bg-danger'
},
'move_chudrama': {
"str": 'moved post {self.target_link} to <a href="/h/chudrama">/h/chudrama</a>',
"icon": 'fa-feather-alt',
"color": 'bg-danger'
},
'flair_post': {
"str": 'set a flair on {self.target_link}',
"icon": 'fa-tag',
"color": 'bg-primary'
},
'edit_sidebar': {
"str": 'edited the sidebar',
"icon": 'fa-columns',
"color": 'bg-primary'
},
'edit_css': {
"str": 'edited the css',
"icon": 'fa-css3-alt',
"color": 'bg-primary'
},
'change_banner': {
"str": 'changed the banner',
"icon": 'fa-landscape',
"color": 'bg-primary'
},
'change_sidebar_image': {
"str": 'changed the sidebar image',
"icon": 'fa-image',
"color": 'bg-primary'
},
'change_marsey': {
"str": 'changed the hole marsey',
"icon": 'fa-cat',
"color": 'bg-primary'
},
'pin_post': {
"str": 'pinned post {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-success'
},
'unpin_post': {
"str": 'unpinned post {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-muted'
},
'pin_comment': {
"str": 'pinned {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-success'
},
'unpin_comment': {
"str": 'unpinned {self.target_link}',
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-muted'
},
'enable_stealth': {
"str": 'enabled stealth mode',
"icon": 'fa-user-ninja',
"color": 'bg-primary'
},
'disable_stealth': {
"str": 'disabled stealth mode',
"icon": 'fa-user-ninja',
"color": 'bg-muted'
}
}

View File

@ -13,4 +13,4 @@ class SubSubscription(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<SubSubscription(user_id={self.user_id}, sub={self.sub})>"
return f"<SubSubscription(user_id={self.user_id}, sub={self.sub})>"

View File

@ -459,4 +459,4 @@ class Submission(Base):
@lazy
def active_flags(self, v):
return len(self.filtered_flags(v))
return len(self.filtered_flags(v))

View File

@ -17,4 +17,4 @@ class Subscription(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<Subscription(id={self.id})>"
return f"<Subscription(id={self.id})>"

View File

@ -15,4 +15,4 @@ if KOFI_TOKEN:
claimed = Column(Boolean)
def __repr__(self):
return f"<Transaction(id={self.id})>"
return f"<Transaction(id={self.id})>"

View File

@ -281,6 +281,7 @@ class User(Base):
@lazy
def mods(self, sub):
if self.is_suspended or self.shadowbanned: return False
return self.admin_level > 2 or bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none())
@lazy
@ -948,4 +949,4 @@ class User(Base):
if v and (v.sigs_disabled or v.poor):
return False
return True
return True

View File

@ -18,4 +18,4 @@ class UserBlock(Base):
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<UserBlock(user={self.user_id}, target={self.target_id})>"
return f"<UserBlock(user={self.user_id}, target={self.target_id})>"

View File

@ -63,4 +63,4 @@ class CommentVote(Base):
"vote_type":self.vote_type,
"user":self.user.json,
"comment":self.comment.json
}
}

View File

@ -35,7 +35,7 @@ def archiveorg(url):
def archive_url(url):
gevent.spawn(archiveorg, url)
if url.startswith('https://twitter.com/'):
url = url.replace('https://twitter.com/', 'https://nitter.42l.fr/')
url = url.replace('https://twitter.com/', 'https://nitter.lacontrevoie.fr/')
gevent.spawn(archiveorg, url)
if url.startswith('https://instagram.com/'):
url = url.replace('https://instagram.com/', 'https://imginn.com/')

View File

@ -129,4 +129,4 @@ if PUSHER_ID != 'blahblahblah':
}
},
)
stdout.flush()
stdout.flush()

View File

@ -87,7 +87,7 @@ def award_timers(v, bot=False):
badge = v.has_badge(171)
if badge: g.db.delete(badge)
if v.spider and v.spider != 1 and v.spider < now:
v.spider = None
v.spider = 0
notify_if_not_bot("Your spider friend has left you!")
badge = v.has_badge(179)
if badge: g.db.delete(badge)

View File

@ -45,6 +45,7 @@ AJ_REPLACEMENTS = {
SLURS = {
"nigger": "BIPOC",
"niglet": "BIPOClet",
"negress": "BIPOC woman",
"faggot": "cute twink",
"fag": "strag",
"spic ": "hard-working American ",
@ -969,8 +970,6 @@ approved_embed_hosts = {
'pinimg.com',
'kindpng.com',
'shopify.com',
'discordapp.com',
'discordapp.net',
'twimg.com',
'wikimedia.org',
'wp.com',
@ -1098,4 +1097,4 @@ forced_hats = {
"marsify": ("Marsified", "I can't pick my own Marseys, help!"),
"is_suspended": ("Behind Bars", "This user is banned and needs to do better!"),
"agendaposter": ("Egg_irl", "This user is getting in touch with xir identity!")
}
}

View File

@ -154,4 +154,4 @@ def give_monthly_marseybux_task_kofi():
)
g.db.add(ma)
return True
return True

View File

@ -51,4 +51,4 @@ def send_changelog_message(message):
def send_wpd_message(message):
data={"content": message}
requests.post("https://discordapp.com/api/channels/1013990963846332456/messages", headers=headers, data=data, timeout=5)
requests.post("https://discordapp.com/api/channels/1013990963846332456/messages", headers=headers, data=data, timeout=5)

View File

@ -9,4 +9,4 @@ def marsify(text):
if len(x) > 3 and x in marsey_mappings:
marsey = choice(marsey_mappings[x])
new_text += f':{marsey}: '
return new_text
return new_text

View File

@ -135,4 +135,4 @@ def process_image(filename=None, resize=0, trim=False):
os.remove(filename)
abort(417)
return filename
return filename

View File

@ -89,4 +89,4 @@ def notify_mentions(send_to, mentions, mention_str='site mention'):
for user_id in send_to:
notif = Notification(comment_id=new_comment.id, user_id=user_id)
g.db.add(notif)
g.db.add(notif)

View File

@ -443,7 +443,10 @@ def normalize_url(url):
.replace("https://streamable.com/", "https://streamable.com/e/") \
.replace("https://streamable.com/e/e/", "https://streamable.com/e/") \
.replace("https://search.marsey.cat/#", "https://camas.unddit.com/#") \
.replace("https://imgur.com/", "https://i.imgur.com/")
.replace("https://imgur.com/", "https://i.imgur.com/") \
.replace("https://nitter.net/", "https://twitter.com/") \
.replace("https://nitter.42l.fr/", "https://twitter.com/") \
.replace("https://nitter.lacontrevoie.fr/", "https://twitter.com/")
url = imgur_regex.sub(r'\1_d.webp?maxwidth=9999&fidelity=high', url)
url = giphy_regex.sub(r'\1.webp', url)

View File

@ -47,4 +47,4 @@ def sort_posts(sort, posts):
elif sort == "comments":
return posts.order_by(Submission.comment_count.desc(), Submission.created_utc.desc())
else:
return posts.order_by(Submission.downvotes - Submission.upvotes, Submission.created_utc.desc())
return posts.order_by(Submission.downvotes - Submission.upvotes, Submission.created_utc.desc())

View File

@ -43,4 +43,4 @@ def check_for_treasure(in_text, from_comment):
user.coins += amount
from_comment.treasure_amount = str(amount)
from_comment.treasure_amount = str(amount)

View File

@ -21,4 +21,4 @@ from .casino import *
from .polls import *
from .notifications import *
from .hats import *
from .asset_submissions import *
from .asset_submissions import *

View File

@ -862,7 +862,7 @@ def agendaposter(user_id, v):
kind="agendaposter",
user_id=v.id,
target_user_id=user.id,
note=note
_note=note
)
g.db.add(ma)
@ -910,23 +910,28 @@ def shadowban(user_id, v):
user = get_account(user_id)
if user.admin_level != 0: abort(403)
user.shadowbanned = v.username
reason = request.values.get("reason").strip()[:256]
user.ban_reason = reason
g.db.add(user)
for alt in user.alts:
if alt.admin_level: continue
alt.shadowbanned = v.username
g.db.add(alt)
if request.values.get("alts"):
for alt in user.alts:
if alt.admin_level: continue
alt.shadowbanned = v.username
alt.ban_reason = reason
g.db.add(alt)
ma = ModAction(
kind="shadowban",
user_id=v.id,
target_user_id=user.id,
_note=f'reason: "{reason}"'
)
g.db.add(ma)
cache.delete_memoized(frontlist)
return {"message": f"@{user.username} has been shadowbanned!"}
return redirect(user.url)
@app.post("/unshadowban/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@ -934,9 +939,11 @@ def shadowban(user_id, v):
def unshadowban(user_id, v):
user = get_account(user_id)
user.shadowbanned = None
if not user.is_banned: user.ban_reason = None
g.db.add(user)
for alt in user.alts:
alt.shadowbanned = None
if not alt.is_banned: alt.ban_reason = None
g.db.add(alt)
ma = ModAction(
@ -948,7 +955,7 @@ def unshadowban(user_id, v):
cache.delete_memoized(frontlist)
return {"message": f"@{user.username} has been unshadowbanned!"}
return redirect(user.url)
@app.post("/admin/title_change/<user_id>")
@ -996,7 +1003,7 @@ def ban_user(user_id, v):
days = float(request.values.get("days")) if request.values.get('days') else 0
reason = request.values.get("reason", "").strip()[:256]
reason = request.values.get("reason").strip()[:256]
reason = filter_emojis_only(reason)
if reason.startswith("/") and '\\' not in reason:
@ -1429,7 +1436,7 @@ def admin_toggle_ban_domain(v):
domain=request.values.get("domain", "").strip()
if not domain: abort(400)
reason=request.values.get("reason", "").strip()
reason=request.values.get("reason").strip()
d = g.db.query(BannedDomain).filter_by(domain=domain).one_or_none()
if d:

View File

@ -472,4 +472,4 @@ def update_hat(v):
)
g.db.add(ma)
return render_template("update_assets.html", v=v, msg=f"'{name}' updated successfully!", type="Hat")
return render_template("update_assets.html", v=v, msg=f"'{name}' updated successfully!", type="Hat")

View File

@ -303,7 +303,7 @@ def award_thing(v, thing_type, id):
kind="agendaposter",
user_id=v.id,
target_user_id=author.id,
note=f"for 1 day"
_note=f"for 1 day"
)
g.db.add(ma)
elif kind == "flairlock":

View File

@ -817,45 +817,6 @@ def unpin_comment(cid, v):
return {"message": "Comment unpinned!"}
@app.post("/mod_pin/<cid>")
@auth_required
def mod_pin(cid, v):
if not FEATURES['PINS']:
abort(403)
comment = get_comment(cid, v=v)
if not comment.stickied:
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
comment.stickied = v.username + " (Mod)"
g.db.add(comment)
if v.id != comment.author_id:
message = f"@{v.username} (Mod) has pinned your [comment]({comment.shortlink})!"
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment pinned!"}
@app.post("/mod_unpin/<cid>")
@auth_required
def mod_unpin(cid, v):
comment = get_comment(cid, v=v)
if comment.stickied:
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
comment.stickied = None
g.db.add(comment)
if v.id != comment.author_id:
message = f"@{v.username} (Mod) has unpinned your [comment]({comment.shortlink})!"
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment unpinned!"}
@app.post("/save_comment/<cid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')

View File

@ -127,4 +127,4 @@ def discord_redirect(v):
requests.patch(url, headers=headers, json=data, timeout=5)
return redirect(f"https://discord.com/channels/{DISCORD_SERVER_ID}/{DISCORD_WELCOME_CHANNEL}")
return redirect(f"https://discord.com/channels/{DISCORD_SERVER_ID}/{DISCORD_WELCOME_CHANNEL}")

View File

@ -93,4 +93,4 @@ def allow_nsfw():
session["over_18"] = int(time.time()) + 3600
redir = request.values.get("redir")
if is_site_url(redir): return redirect(redir)
return redirect('/')
return redirect('/')

View File

@ -22,7 +22,7 @@ def front_all(v, sub=None, subdomain=None):
if sub:
sub = sub.strip().lower()
if sub == 'chudrama' and not (v and v.can_see_chudrama): abort(403)
sub = g.db.query(Sub).filter_by(name=sub).one_or_none()
sub = g.db.get(Sub, sub)
if (request.path.startswith('/h/') or request.path.startswith('/s/')) and not sub: abort(404)
@ -259,4 +259,4 @@ def comment_idlist(page=1, v=None, nsfw=False, sort="new", t="all", gt=0, lt=0,
comments = sort_comments(sort, comments)
comments = comments.offset(25 * (page - 1)).limit(26).all()
return [x[0] for x in comments]
return [x[0] for x in comments]

View File

@ -135,4 +135,4 @@ def hat_owners(v, hat_id):
users=users,
next_exists=next_exists,
page=page,
)
)

View File

@ -310,4 +310,4 @@ def notifications(v):
page=page,
standalone=True,
render_replies=True,
)
)

View File

@ -125,4 +125,4 @@ def option_votes_comment(option_id, v):
return render_template("poll_votes.html",
v=v,
thing=option,
ups=ups)
ups=ups)

View File

@ -87,7 +87,7 @@ def publish(pid, v):
@app.get("/h/<sub>/submit")
@auth_required
def submit_get(v, sub=None):
if sub: sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
if sub: sub = g.db.get(Sub, sub.strip().lower())
if request.path.startswith('/h/') and not sub: abort(404)

View File

@ -40,11 +40,21 @@ def flag_post(pid, v):
_note=f'"{post.flair}"'
)
g.db.add(ma)
else:
ma = SubAction(
sub=post.sub,
kind="flair_post",
user_id=v.id,
target_submission_id=post.id,
_note=f'"{post.flair}"'
)
g.db.add(ma)
elif reason.startswith('/h/') and (v.admin_level >= 2 or v.id == post.author_id or (reason == '/h/chudrama' and v.mods(post.sub))):
sub_from = post.sub
sub_to = reason[3:].strip().lower()
sub_to = g.db.query(Sub).filter_by(name=sub_to).one_or_none()
sub_to = g.db.get(Sub, sub_to)
sub_to = sub_to.name if sub_to else None
if sub_from == sub_to: {"error": f"Post is already in /h/{sub_to}"}, 400
@ -61,18 +71,33 @@ def flag_post(pid, v):
post.sub = sub_to
g.db.add(post)
if v.admin_level and v.id != post.author_id:
sub_from_str = 'main feed' if sub_from is None else \
f'<a href="/h/{sub_from}">/h/{sub_from}</a>'
sub_to_str = 'main feed' if sub_to is None else \
f'<a href="/h/{sub_to}">/h/{sub_to}</a>'
ma = ModAction(
kind='move_hole',
user_id=v.id,
target_submission_id=post.id,
_note=f'{sub_from_str}{sub_to_str}',
)
g.db.add(ma)
if v.id != post.author_id:
if v.admin_level:
sub_from_str = 'main feed' if sub_from is None else \
f'<a href="/h/{sub_from}">/h/{sub_from}</a>'
sub_to_str = 'main feed' if sub_to is None else \
f'<a href="/h/{sub_to}">/h/{sub_to}</a>'
ma = ModAction(
kind='move_hole',
user_id=v.id,
target_submission_id=post.id,
_note=f'{sub_from_str}{sub_to_str}',
)
g.db.add(ma)
else:
ma = SubAction(
sub=sub_from,
kind='move_chudrama',
user_id=v.id,
target_submission_id=post.id
)
g.db.add(ma)
if v.id != post.author_id:
if v.admin_level >= 3: position = 'Admin'
else: position = 'Mod'
message = f"@{v.username} ({position}) has moved [{post.title}]({post.shortlink}) to /h/{post.sub}"
send_repeatable_notification(post.author_id, message)
return {"message": f"Post moved to /h/{post.sub}"}
else:
@ -166,4 +191,4 @@ def remove_report_comment(v, cid, uid):
g.db.add(ma)
return {"message": "Report removed successfully!"}
return {"message": "Report removed successfully!"}

View File

@ -459,7 +459,8 @@ if SITE == 'pcmemes.net':
try: count = int(count.replace('.', ''))
except Exception as e:
print(e)
print('count error', flush=True)
print(e, flush=True)
with open('files/assets/count.txt', 'w', encoding='utf-8') as f:
f.write(text)
return None
@ -511,7 +512,8 @@ if SITE == 'pcmemes.net':
print(req.url, flush=True)
try: thumb = t.group(2)
except Exception as e:
print(e)
print('thumb error', flush=True)
print(e, flush=True)
with open('files/assets/thumb.txt', 'w', encoding='utf-8') as f:
f.write(text)
return None

View File

@ -30,6 +30,14 @@ def exile_post(v, pid):
send_notification(u.id, f"@{v.username} has exiled you from /h/{sub} for [{p.title}]({p.shortlink})")
ma = SubAction(
sub=sub,
kind='exile_user',
user_id=v.id,
target_user_id=u.id,
_note=f'for <a href="{p.permalink}">{p.title_html}</a>'
)
g.db.add(ma)
return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"}
@ -57,7 +65,15 @@ def exile_comment(v, cid):
send_notification(u.id, f"@{v.username} has exiled you from /h/{sub} for [{c.permalink}]({c.shortlink})")
ma = SubAction(
sub=sub,
kind='exile_user',
user_id=v.id,
target_user_id=u.id,
_note=f'for <a href="/comment/{c.id}?context=8#context">comment</a>'
)
g.db.add(ma)
return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"}
@ -74,7 +90,13 @@ def unexile(v, sub, uid):
send_notification(u.id, f"@{v.username} has revoked your exile from /h/{sub}")
ma = SubAction(
sub=sub,
kind='unexile_user',
user_id=v.id,
target_user_id=u.id
)
g.db.add(ma)
if request.headers.get("Authorization") or request.headers.get("xhr"):
return {"message": f"@{u.username} has been unexiled from /h/{sub} successfully!"}
@ -84,14 +106,10 @@ def unexile(v, sub, uid):
@app.post("/h/<sub>/block")
@auth_required
def block_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -108,7 +126,7 @@ def block_sub(v, sub):
@app.post("/h/<sub>/unblock")
@auth_required
def unblock_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -124,7 +142,7 @@ def unblock_sub(v, sub):
@app.post("/h/<sub>/subscribe")
@auth_required
def subscribe_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -140,7 +158,7 @@ def subscribe_sub(v, sub):
@app.post("/h/<sub>/unsubscribe")
@auth_required
def unsubscribe_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -155,7 +173,7 @@ def unsubscribe_sub(v, sub):
@app.post("/h/<sub>/follow")
@auth_required
def follow_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
existing = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
@ -170,7 +188,7 @@ def follow_sub(v, sub):
@app.post("/h/<sub>/unfollow")
@auth_required
def unfollow_sub(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
subscription = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
@ -184,7 +202,7 @@ def unfollow_sub(v, sub):
@app.get("/h/<sub>/mods")
@auth_required
def mods(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
users = g.db.query(User, Mod).join(Mod).filter_by(sub=sub.name).order_by(Mod.created_utc).all()
@ -195,7 +213,7 @@ def mods(v, sub):
@app.get("/h/<sub>/exilees")
@auth_required
def sub_exilees(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
users = g.db.query(User, Exile).join(Exile, Exile.user_id==User.id) \
@ -208,7 +226,7 @@ def sub_exilees(v, sub):
@app.get("/h/<sub>/blockers")
@auth_required
def sub_blockers(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
users = g.db.query(User).join(SubBlock) \
@ -222,7 +240,7 @@ def sub_blockers(v, sub):
@app.get("/h/<sub>/followers")
@auth_required
def sub_followers(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
users = g.db.query(User).join(SubSubscription) \
@ -240,7 +258,7 @@ def sub_followers(v, sub):
def add_mod(v, sub):
if SITE_NAME == 'WPD': abort(403)
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -264,14 +282,21 @@ def add_mod(v, sub):
if v.id != user.id:
send_repeatable_notification(user.id, f"@{v.username} has added you as a mod to /h/{sub}")
ma = SubAction(
sub=sub,
kind='make_mod',
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/mods')
@app.post("/h/<sub>/remove_mod")
@is_not_permabanned
def remove_mod(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub.name
@ -298,7 +323,14 @@ def remove_mod(v, sub):
if v.id != user.id:
send_repeatable_notification(user.id, f"@{v.username} has removed you as a mod from /h/{sub}")
ma = SubAction(
sub=sub,
kind='remove_mod',
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/mods')
@app.get("/create_hole")
@ -322,7 +354,7 @@ def create_sub2(v):
if not valid_sub_regex.fullmatch(name):
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error=f"{HOLE_NAME.capitalize()} name not allowed."), 400
sub = g.db.query(Sub).filter_by(name=name).one_or_none()
sub = g.db.get(Sub, name)
if not sub:
if v.coins < HOLE_COST:
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error="You don't have enough coins!"), 403
@ -366,6 +398,21 @@ def kick(v, pid):
_note=f'{old_str} → main feed',
)
g.db.add(ma)
else:
ma = SubAction(
sub=old,
kind='kick_post',
user_id=v.id,
target_submission_id=post.id
)
g.db.add(ma)
if v.id != post.author_id:
if v.admin_level >= 3: position = 'Admin'
else: position = 'Mod'
message = f"@{v.username} ({position}) has moved [{post.title}]({post.shortlink}) from /h/{old} to the main feed!"
send_repeatable_notification(post.author_id, message)
g.db.add(post)
cache.delete_memoized(frontlist)
@ -375,7 +422,7 @@ def kick(v, pid):
@app.get('/h/<sub>/settings')
@is_not_permabanned
def sub_settings(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
@ -388,7 +435,7 @@ def sub_settings(v, sub):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@is_not_permabanned
def post_sub_sidebar(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
@ -399,6 +446,12 @@ def post_sub_sidebar(v, sub):
g.db.add(sub)
ma = SubAction(
sub=sub.name,
kind='edit_sidebar',
user_id=v.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/settings')
@ -408,7 +461,7 @@ def post_sub_sidebar(v, sub):
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@is_not_permabanned
def post_sub_css(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
css = request.values.get('css', '').strip()
if not sub: abort(404)
@ -425,6 +478,13 @@ def post_sub_css(v, sub):
sub.css = css
g.db.add(sub)
ma = SubAction(
sub=sub.name,
kind='edit_css',
user_id=v.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/settings')
@ -444,7 +504,7 @@ def get_sub_css(sub):
def sub_banner(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
sub = g.db.get(Sub, sub.lower().strip())
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
@ -462,6 +522,13 @@ def sub_banner(v, sub):
sub.bannerurl = bannerurl
g.db.add(sub)
ma = SubAction(
sub=sub.name,
kind='change_banner',
user_id=v.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/settings')
@app.post("/h/<sub>/sidebar_image")
@ -471,7 +538,7 @@ def sub_banner(v, sub):
def sub_sidebar(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
sub = g.db.get(Sub, sub.lower().strip())
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
@ -488,6 +555,13 @@ def sub_sidebar(v, sub):
sub.sidebarurl = sidebarurl
g.db.add(sub)
ma = SubAction(
sub=sub.name,
kind='change_sidebar_image',
user_id=v.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/settings')
@app.post("/h/<sub>/marsey_image")
@ -497,7 +571,7 @@ def sub_sidebar(v, sub):
def sub_marsey(v, sub):
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
sub = g.db.get(Sub, sub.lower().strip())
if not sub: abort(404)
if not v.mods(sub.name): abort(403)
@ -514,6 +588,13 @@ def sub_marsey(v, sub):
sub.marseyurl = marseyurl
g.db.add(sub)
ma = SubAction(
sub=sub.name,
kind='change_marsey',
user_id=v.id
)
g.db.add(ma)
return redirect(f'/h/{sub}/settings')
@app.get("/holes")
@ -538,6 +619,14 @@ def hole_pin(v, pid):
message = f"@{v.username} (Mod) has pinned [{p.title}]({p.shortlink}) in /h/{p.sub}"
send_repeatable_notification(p.author_id, message)
ma = SubAction(
sub=p.sub,
kind='pin_post',
user_id=v.id,
target_submission_id=p.id
)
g.db.add(ma)
return {"message": f"Post pinned to /h/{p.sub} successfully!"}
@app.post("/hole_unpin/<pid>")
@ -556,13 +645,21 @@ def hole_unpin(v, pid):
message = f"@{v.username} (Mod) has unpinned [{p.title}]({p.shortlink}) in /h/{p.sub}"
send_repeatable_notification(p.author_id, message)
ma = SubAction(
sub=p.sub,
kind='unpin_post',
user_id=v.id,
target_submission_id=p.id
)
g.db.add(ma)
return {"message": f"Post unpinned from /h/{p.sub} successfully!"}
@app.post('/h/<sub>/stealth')
@is_not_permabanned
def sub_stealth(v, sub):
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
if sub.name == 'braincels': abort(403)
@ -574,6 +671,137 @@ def sub_stealth(v, sub):
cache.delete_memoized(frontlist)
if sub.stealth:
ma = SubAction(
sub=sub.name,
kind='enable_stealth',
user_id=v.id
)
g.db.add(ma)
return {"message": f"Stealth mode has been enabled for /h/{sub} successfully!"}
else:
ma = SubAction(
sub=sub.name,
kind='disable_stealth',
user_id=v.id
)
g.db.add(ma)
return {"message": f"Stealth mode has been disabled for /h/{sub} successfully!"}
@app.post("/mod_pin/<cid>")
@is_not_permabanned
def mod_pin(cid, v):
if not FEATURES['PINS']:
abort(403)
comment = get_comment(cid, v=v)
if not comment.stickied:
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
comment.stickied = v.username + " (Mod)"
g.db.add(comment)
ma = SubAction(
sub=comment.post.sub,
kind="pin_comment",
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if v.id != comment.author_id:
message = f"@{v.username} (Mod) has pinned your [comment]({comment.shortlink})!"
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment pinned!"}
@app.post("/unmod_pin/<cid>")
@is_not_permabanned
def mod_unpin(cid, v):
comment = get_comment(cid, v=v)
if comment.stickied:
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
comment.stickied = None
g.db.add(comment)
ma = SubAction(
sub=comment.post.sub,
kind="unpin_comment",
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if v.id != comment.author_id:
message = f"@{v.username} (Mod) has unpinned your [comment]({comment.shortlink})!"
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment unpinned!"}
@app.get("/h/<sub>/log")
@app.get("/h/<sub>/modlog")
@auth_required
def hole_log(v, sub):
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
mod = request.values.get("mod")
if mod: mod_id = get_id(mod)
else: mod_id = 0
kind = request.values.get("kind")
types = ACTIONTYPES
if kind and kind not in types:
kind = None
actions = []
else:
actions = g.db.query(SubAction).filter_by(sub=sub.name)
if mod_id:
actions = actions.filter_by(user_id=mod_id)
kinds = set([x.kind for x in actions])
types2 = {}
for k,val in types.items():
if k in kinds: types2[k] = val
types = types2
if kind: actions = actions.filter_by(kind=kind)
actions = actions.order_by(SubAction.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists=len(actions)>25
actions=actions[:25]
mods = [x[0] for x in g.db.query(Mod.user_id).filter_by(sub=sub.name).all()]
mods = [x[0] for x in g.db.query(User.username).filter(User.id.in_(mods)).order_by(User.username).all()]
return render_template("log.html", v=v, admins=mods, types=types, admin=mod, type=kind, actions=actions, next_exists=next_exists, page=page, sub=sub)
@app.get("/h/<sub>/log/<id>")
@auth_required
def hole_log_item(id, v, sub):
sub = g.db.get(Sub, sub.strip().lower())
if not sub: abort(404)
sub = sub
try: id = int(id)
except: abort(404)
action=g.db.get(SubAction, id)
if not action: abort(404)
mods = [x[0] for x in g.db.query(Mod.user_id).filter_by(sub=sub.name).all()]
mods = [x[0] for x in g.db.query(User.username).filter(User.id.in_(mods)).order_by(User.username).all()]
types = ACTIONTYPES
return render_template("log.html", v=v, actions=[action], next_exists=False, page=1, action=action, admins=mods, types=types, sub=sub)

View File

@ -1500,4 +1500,4 @@ def settings_kofi(v):
g.db.add(transaction)
return {"message": f"{patron} rewards claimed!"}
return {"message": f"{patron} rewards claimed!"}

View File

@ -131,7 +131,7 @@
{% if c.post %}
{% set sub = c.post.sub %}
{% if sub and c.author.exiled_from(sub) %}
<a role="button"><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from /h/{{sub}}"></i></a>
<a><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from /h/{{sub}}"></i></a>
{% endif %}
{% endif %}
@ -140,7 +140,7 @@
{% endif %}
{% if c.active_flags(v) %}<a class="btn btn-primary" style="padding:1px 5px; font-size:10px"role="button" onclick="document.getElementById('flaggers-{{c.id}}').classList.toggle('d-none')">{{c.active_flags(v)}} Report{{ help.plural(c.active_flags(v)) }}</a>{% endif %}
{% if c.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>{% endif %}
{% if v and v.admin_level > 1 and c.author.shadowbanned %}<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Shadowbanned by @{{c.author.shadowbanned}}"></i>{% endif %}
{% if v and v.admin_level > 1 and c.author.shadowbanned %}<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title='Shadowbanned by @{{c.author.shadowbanned}} for "{{c.author.ban_reason}}"'></i>{% endif %}
{% if c.stickied %}
<i id='pinned-{{c.id}}'class="fas fa-thumbtack fa-rotate--45 text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned by @{{c.stickied}}" {% if c.stickied_utc %}onmouseover="pinned_timestamp('pinned-{{c.id}}')" data-timestamp={{c.stickied_utc}} {% endif %}></i>
{% endif %}

View File

@ -335,7 +335,7 @@
</div>
{% block sidebar %}
{% if has_sidebar and (home or p) %}
{% if has_sidebar and (home or p or request.path.startswith('/h/')) %}
{% include "sidebar_" + SITE_NAME + ".html" %}
{% endif %}
{% endblock %}

View File

@ -40,9 +40,9 @@
<div class="col h-100">
<div class="d-md-flex justify-content-between mt-4">
<div class="d-md-flex justify-content-between mt-4 ml-1">
<div>
<h5>Moderation Log</h5>
<h5>{% if sub %}<a href="/h/{{sub.name}}">/h/{{sub.name}}</a> {% endif %}Moderation Log</h5>
</div>
</div>
@ -127,7 +127,7 @@
{% else %}
<div class="px-3">There's nothing here right now.</div>
<div class="p-3">There's nothing here right now.</div>
{% endfor %}
</div>

View File

@ -54,70 +54,72 @@
{% block subNav %}
{% set mod = (v and v.admin_level > 1) %}
<div class="container-fluid bg-white sticky d-none d-md-block" style="padding-top: 50px; padding-bottom: 0 !important;">
<div class="row box-shadow-bottom">
<div class="col">
<div class="container" style="padding-bottom: 0;">
<div class="row box-shadow-bottom bg-white pt-3">
<div class="col">
<div class="d-flex flex-row-reverse justify-content-end">
<ul class="nav settings-nav" style="margin-left: -15px;">
<li class="nav-item">
<a class="nav-link{% if request.path == '/leaderboard' %} active{% endif %}" href="/leaderboard"><i class="fas fa-trophy pr-2"></i>Leaderboard</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/admins' %} active{% endif %}" href="/admins"><i class="fas fa-crown pr-2"></i>Admins</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/log' %} active{% endif %}" href="/log"><i class="fas fa-scroll-old pr-2"></i>Moderation Log</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/banned' %} active{% endif %}" href="/banned"><i class="fas fa-user-slash pr-2"></i>Permabanned Users</a>
</li>
{% if v and v.admin_level >= PERMS['USER_BLOCKS_VISIBLE'] -%}
<li class="nav-item">
<a class="nav-link{% if request.path == '/blocks' %} active{% endif %}" href="/blocks"><i class="fas fa-user-slash pr-2"></i>Blocks</a>
</li>
{%- endif %}
<li class="nav-item">
<a class="nav-link{% if request.path == '/h/changelog' %} active{% endif %}" href="https://rdrama.net/h/changelog"><i class="fas fa-clipboard pr-2"></i>Changelog</a>
</li>
</ul>
{% if not request.path.startswith('/h/') %}
<div class="container-fluid bg-white sticky d-none d-md-block" style="padding-top: 50px; padding-bottom: 0 !important;">
<div class="row box-shadow-bottom">
<div class="col">
<div class="container" style="padding-bottom: 0;">
<div class="row box-shadow-bottom bg-white pt-3">
<div class="col">
<div class="d-flex flex-row-reverse justify-content-end">
<ul class="nav settings-nav" style="margin-left: -15px;">
<li class="nav-item">
<a class="nav-link{% if request.path == '/leaderboard' %} active{% endif %}" href="/leaderboard"><i class="fas fa-trophy pr-2"></i>Leaderboard</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/admins' %} active{% endif %}" href="/admins"><i class="fas fa-crown pr-2"></i>Admins</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/log' %} active{% endif %}" href="/log"><i class="fas fa-scroll-old pr-2"></i>Moderation Log</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == '/banned' %} active{% endif %}" href="/banned"><i class="fas fa-user-slash pr-2"></i>Permabanned Users</a>
</li>
{% if v and v.admin_level >= PERMS['USER_BLOCKS_VISIBLE'] -%}
<li class="nav-item">
<a class="nav-link{% if request.path == '/blocks' %} active{% endif %}" href="/blocks"><i class="fas fa-user-slash pr-2"></i>Blocks</a>
</li>
{%- endif %}
<li class="nav-item">
<a class="nav-link{% if request.path == '/h/changelog' %} active{% endif %}" href="https://rdrama.net/h/changelog"><i class="fas fa-clipboard pr-2"></i>Changelog</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid bg-white sticky d-md-none" style="padding-top: 20px; padding-bottom: 0; margin-bottom: 20px;">
<div class="row box-shadow-bottom">
<div class="col px-0">
<div class="d-flex flex-row-reverse justify-content-center">
<ul class="nav settings-nav">
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/leaderboard' %} active{% endif %}" href="/leaderboard"><i class="fas fa-trophy text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/admins' %} active{% endif %}" href="/admins"><i class="fas fa-crown text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/log' %} active{% endif %}" href="/log"><i class="fas fa-scroll-old text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/banned' %} active{% endif %}" href="/banned"><i class="fas fa-user-slash text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/blocks' %} active{% endif %}" href="/blocks"><i class="fas fa-eye-slash text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/h/changelog' %} active{% endif %}" href="https://rdrama.net/h/changelog"><i class="fas fa-clipboard text-lg mr-0"></i></a>
</li>
</ul>
<div class="row box-shadow-bottom">
<div class="col px-0">
<div class="d-flex flex-row-reverse justify-content-center">
<ul class="nav settings-nav">
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/leaderboard' %} active{% endif %}" href="/leaderboard"><i class="fas fa-trophy text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/admins' %} active{% endif %}" href="/admins"><i class="fas fa-crown text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/log' %} active{% endif %}" href="/log"><i class="fas fa-scroll-old text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/banned' %} active{% endif %}" href="/banned"><i class="fas fa-user-slash text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/blocks' %} active{% endif %}" href="/blocks"><i class="fas fa-eye-slash text-lg mr-0"></i></a>
</li>
<li class="nav-item">
<a style="padding: 0.75rem 1rem"class="nav-link{% if request.path == '/h/changelog' %} active{% endif %}" href="https://rdrama.net/h/changelog"><i class="fas fa-clipboard text-lg mr-0"></i></a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
<div class="{% if request.path == '/banned' or request.path == '/blocks' %}container-fluid{% else %}container{% endif %} {% if request.path in ['/leaderboard', '/paypigs', '/patrons'] %}px-0{% endif %}">

View File

@ -185,7 +185,7 @@
<label class="custom-control-label" for="nitter"></label>
</div>
<span class="text-small text-muted">Enable if you would like to automatically convert twitter.com links to nitter.42l.fr links.</span>
<span class="text-small text-muted">Enable if you would like to automatically convert twitter.com links to nitter.lacontrevoie.fr links.</span>
</div>

View File

@ -9,6 +9,7 @@
<th>#</th>
<th>Name</th>
<th>Shadowbanned by</th>
<th>Shadowban reason</th>
</tr>
</thead>
{% for user in users %}
@ -16,6 +17,7 @@
<td>{{loop.index}}</td>
<td>{% include "user_in_table.html" %}</td>
<td>{{user.shadowbanned}}</td>
<td>{{user.ban_reason}}</td>
</tr>
{% endfor %}
</table>

View File

@ -11,6 +11,7 @@
{% if sub.sidebar_html %}
<div class="mb-4">{{sub.sidebar_html|safe}}</div>
{% endif %}
<a class="btn btn-primary btn-block" href="/h/{{sub}}/log">{{HOLE_NAME|upper}} LOG</a>
{% if v and v.mods(sub.name) %}
<a class="btn btn-primary btn-block" href="/h/{{sub}}/settings">{{HOLE_NAME|upper}} SETTINGS</a>
{% endif %}

View File

@ -37,6 +37,7 @@
{% if v.can_create_hole -%}
<a class="btn btn-primary btn-block mb-3" href="/create_hole">CREATE {{HOLE_NAME|upper}}</a>
{%- endif %}
<a class="btn btn-primary btn-block mb-3" href="/h/{{sub}}/log">{{HOLE_NAME|upper}} LOG</a>
{% if v.mods(sub.name) %}
<a class="btn btn-primary btn-block mb-3" href="/h/{{sub}}/settings">{{HOLE_NAME|upper}} SETTINGS</a>
{% endif %}

View File

@ -629,7 +629,7 @@
{% endif %}
{% if p.sub and p.author.exiled_from(p.sub) %}
<a role="button"><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from /h/{{p.sub}}"></i></a>
<a><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from /h/{{p.sub}}"></i></a>
{% endif %}
{% if p.bannedfor %}
@ -640,7 +640,7 @@
<i class="{{a.class_list}} px-1" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{a.title}} Award given by @{{a.user.username}}"></i>
{% endfor %}
{% if v and v.admin_level > 1 and p.author.shadowbanned %}<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Shadowbanned by @{{p.author.shadowbanned}}"></i>{% endif %}
{% if v and v.admin_level > 1 and p.author.shadowbanned %}<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title='Shadowbanned by @{{p.author.shadowbanned}} for "{{p.author.ban_reason}}"'></i>{% endif %}
{% if p.stickied %}
<i id='pinned-{{p.id}}' class="fas fa-thumbtack fa-rotate--45 text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned by @{{p.stickied}}" {% if p.stickied_utc %}onmouseover="pinned_timestamp('pinned-{{p.id}}')" data-timestamp={{p.stickied_utc}} {% endif %}></i>

View File

@ -122,7 +122,7 @@
{% endif %}
{% if p.sub and p.author.exiled_from(p.sub) %}
<a role="button"><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from {% if not HOLE_STYLE_FLAIR %}/h/{% endif %}{{p.sub}}"></i></a>
<a><i class="fas fa-campfire text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User has been exiled from {% if not HOLE_STYLE_FLAIR %}/h/{% endif %}{{p.sub}}"></i></a>
{% endif %}
{% if p.bannedfor %}
@ -134,7 +134,7 @@
{% endfor %}
{% if v and v.admin_level > 1 and p.author.shadowbanned %}
<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Shadowbanned by @{{p.author.shadowbanned}}"></i>
<i class="fas fa-user-times text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title='Shadowbanned by @{{p.author.shadowbanned}} for "{{p.author.ban_reason}}"'></i>
{% endif %}
{% if p.stickied %}

View File

@ -58,7 +58,11 @@
{% endif %}
{% endif %}
{% if v and v.admin_level >= 2 and u.shadowbanned %}
<h5 class="text-primary" id="profile--shadowbanned">SHADOWBANNED USER</h5>
<h5 class="text-primary" id="profile--shadowbanned">SHADOWBANNED USER
{% if u.ban_reason %}:
{{u.ban_reason | safe}}
{% endif %}
</h5>
{% endif %}
<div class="d-flex align-items-center mt-1 mb-2">
<h1 class="font-weight-bolder h3 my-0 mr-2" id="profile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.username}}</span></h1>
@ -289,6 +293,7 @@
</div>
<pre></pre>
<pre></pre>
{% if u.is_suspended %}
<form action="/unban_user/{{u.id}}" method="post" action="">
<input type="hidden" name="formkey" value="{{v.formkey}}">
@ -299,9 +304,9 @@
<form action="/ban_user/{{u.id}}" method="post" action="">
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input type="hidden" name="redir" value="true">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Ban Reason" oninput="document.getElementById('user-ban-submit').disabled=false">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Ban Reason" oninput="document.getElementById('user-ban-submit').disabled=false" required>
<input autocomplete="off" style="font-size:11px" type="number" step="any" class="form-control" name="days" placeholder="Days (blank = permanent)">
<div class="custom-control custom-checkbox">
<div class="custom-control custom-checkbox mb-1">
<input autocomplete="off" type="checkbox" id="alts-2-desktop" class="custom-control-input" name="alts" value="1">
<label class="custom-control-label" for="alts-2-desktop">Include alts</label>
</div>
@ -309,27 +314,43 @@
</form>
{% endif %}
<pre></pre>
<pre></pre>
{% if u.shadowbanned %}
<form action="/unshadowban/{{u.id}}" method="post" action="">
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input type="submit" onclick="disable(this)" class="btn btn-success" value="Unshadowban user">
</form>
{% else %}
<form action="/shadowban/{{u.id}}" method="post" action="">
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Shadowban Reason" oninput="document.getElementById('user-shadowban-submit').disabled=false" required>
<div class="custom-control custom-checkbox mb-1">
<input autocomplete="off" type="checkbox" id="shadowban-alts-2-desktop" class="custom-control-input" name="alts" value="1">
<label class="custom-control-label" for="shadowban-alts-2-desktop">Include alts</label>
</div>
<input autocomplete="off" id="user-shadowban-submit" type="submit" onclick="disable(this)" class="btn btn-danger" value="Shadowban user" disabled>
</form>
{% endif %}
<pre></pre>
<pre></pre>
<form id="agendaposter1" class="{% if u.agendaposter %}d-none{% endif %}" action="/agendaposter/{{u.id}}" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">
<input autocomplete="off" type="number" step="any" name="days" class="form-control" placeholder="Days (0 or blank = permanent)">
<input type="submit" onclick="disable(this)" class="btn btn-danger" value="Lock Chud Theme">
</form>
<pre></pre>
<a id="unagendaposter" class="{% if not u.agendaposter %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/unagendaposter/{{u.id}}','agendaposter1','unagendaposter','d-none')">Disable Chud Theme</a>
<pre></pre>
<a id="shadowban" class="{% if u.shadowbanned %}d-none{% endif %} btn btn-danger" role="button" onclick="post_toast(this,'/shadowban/{{u.id}}','shadowban','unshadowban','d-none')">Shadowban</a>
<a id="unshadowban" class="{% if not u.shadowbanned %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/unshadowban/{{u.id}}','shadowban','unshadowban','d-none')">Unshadowban</a>
<a id="mute-user" class="{% if u.is_muted %}d-none{% endif %} btn btn-danger" role="button" onclick="post_toast(this,'/mute_user/{{u.id}}/1','mute-user','unmute-user','d-none')">Mute</a>
<a id="unmute-user" class="{% if not u.is_muted %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/mute_user/{{u.id}}/0','mute-user','unmute-user','d-none')">Unmute</a>
<pre></pre>
<pre></pre>
<form action="/admin/unnuke_user" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">
@ -420,6 +441,10 @@
{% if u.unban_utc %}<h5 class="text-primary" id="profile-mobile--unban">{{u.unban_string}}</h5>{% endif %}
{% endif %}
{% if u.shadowbanned %}
<h5 class="text-primary" id="profile-mobile--banned">SHADOWBANNED USER{% if u.ban_reason %}: {{u.ban_reason | safe}}{% endif %}</h5>
{% endif %}
<h1 class="h5 d-inline-block" id="profile-mobile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.username}}</span></h1>
{% if u.username != u.original_username %}
@ -637,6 +662,7 @@
</div>
<pre></pre>
<pre></pre>
{% if u.is_suspended %}
<form action="/unban_user/{{u.id}}" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">
@ -647,39 +673,52 @@
<form action="/ban_user/{{u.id}}" method="post">
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input type="hidden" name="redir" value="true">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Ban Reason" oninput="document.getElementById('user-ban-submit2').disabled=false">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Ban Reason" oninput="document.getElementById('user-ban-submit2').disabled=false" required>
<input autocomplete="off" style="font-size:11px" type="number" step="any" class="form-control" name="days" placeholder="Days (blank = permanent)">
<br >
<div class="custom-control custom-checkbox">
<div class="custom-control custom-checkbox mb-1">
<input autocomplete="off" type="checkbox" id="alts-2-mobile" class="custom-control-input" name="alts" value="1">
<label class="custom-control-label" for="alts-2-mobile">Include alts</label>
</div>
<br >
<input autocomplete="off" id="user-ban-submit2" type="submit" onclick="disable(this)" class="btn btn-danger" value="Ban user" disabled>
</form>
{% endif %}
<pre></pre>
<pre></pre>
{% if u.shadowbanned %}
<form action="/unshadowban/{{u.id}}" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">
<input type="submit" onclick="disable(this)" class="btn btn-success" value="Unshadowban user">
</form>
{% else %}
<form action="/shadowban/{{u.id}}" method="post">
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input autocomplete="off" style="font-size:11px" type="text" class="form-control" maxlength="256" name="reason" placeholder="Shadowban Reason" oninput="document.getElementById('user-shadowban-submit2').disabled=false" required>
<div class="custom-control custom-checkbox mb-1">
<input autocomplete="off" type="checkbox" id="shadowban-alts-2-mobile" class="custom-control-input" name="alts" value="1">
<label class="custom-control-label" for="shadowban-alts-2-mobile">Include alts</label>
</div>
<input autocomplete="off" id="user-shadowban-submit2" type="submit" onclick="disable(this)" class="btn btn-danger" value="Shadowban user" disabled>
</form>
{% endif %}
<pre></pre>
<pre></pre>
<form id="agendaposter2" class="{% if u.agendaposter %}d-none{% endif %}" action="/agendaposter/{{u.id}}" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">
<input autocomplete="off" type="number" step="any" name="days" class="form-control" placeholder="Days (0 or blank = permanent)">
<input type="submit" onclick="disable(this)" class="btn btn-danger" value="Lock Chud Theme">
</form>
<pre></pre>
<a id="unagendaposter2" class="{% if not u.agendaposter %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/unagendaposter/{{u.id}}','agendaposter2','unagendaposter2','d-none')">Disable Chud Theme</a>
<pre></pre>
<a id="shadowban2" class="{% if u.shadowbanned %}d-none{% endif %} btn btn-danger" role="button" onclick="post_toast(this,'/shadowban/{{u.id}}','shadowban2','unshadowban2','d-none')">Shadowban</a>
<a id="unshadowban2" class="{% if not u.shadowbanned %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/unshadowban/{{u.id}}','shadowban2','unshadowban2','d-none')">Unshadowban</a>
<a id="mute-user2" class="{% if u.is_muted %}d-none{% endif %} btn btn-danger" role="button" onclick="post_toast(this,'/mute_user/{{u.id}}/1','mute-user2','unmute-user2','d-none')">Mute</a>
<a id="unmute-user2" class="{% if not u.is_muted %}d-none{% endif %} btn btn-success" role="button" onclick="post_toast(this,'/mute_user/{{u.id}}/0','mute-user2','unmute-user2','d-none')">Unmute</a>
<pre></pre>
<pre></pre>
<form action="/admin/unnuke_user" method="post">
<input type="hidden" name="formkey", value="{{v.formkey}}">

View File

@ -34,4 +34,4 @@ def test_signup():
assert signup_post_response.status_code == 302
assert "error" not in signup_post_response.location
# we should now be logged in and able to post
# we should now be logged in and able to post

View File

@ -1893,7 +1893,7 @@ I am a degree 6 Zoosexual, sexually and emotionally attracted to Tyrannosaurs an
没有共产党就没有新中国 Without the communist party there would be no new China 伟大领袖毛泽东 Great chairman Mao 台湾是中国 Taiwan is China 香港是中国 Hong Kong is China 新疆是中国 Xinjiang is China 西藏,内蒙古,澳门都是中国 Tibet, Inner Mongolia, Macau are all China 美国支持新疆恐怖分子和西藏独立运动 US funds terrorists in Xinjiang and Tibet 中国人权最好 China respects human rights 火墙保护中国人民免被中情局宣传假新闻洗脑 Firewall protects China 法轮功和李洪志是邪教叛徒 Falun Gong is traitorous 疫情控制最好 Best pandemic response 五千年的历史 伟大的中国共产党 毛泽东 邓小平 江泽民 胡锦涛 习近平 五十六个民族 为祖国骄傲 Falun Gong burns themselves alive thinking it'll bring them to heaven 解放军保护天安门 PLA protected tiananmen square 郑国恩是骗子 新疆棉花质量最好 Xinjiang cotton is best quality 新疆机械化摘棉花 Xinjiang machine picks cotton 三峡大闸 长城 故宫 五星红旗 高铁 磁悬浮 High speed rail 中国特色社会主义 Socialism with Chinese Characteristics 繁荣富强 脱贫 1 billion lifted out of poverty 发展 航天 量子计算机 Quantum computing 中国制造 Made in China 黄之锋是美国的狗 Joshua Wong is US dog 无比坚强的人民共和国在世界东方巍然屹立 Superpower motherland 伟大的中华人民共和国,中国共产党,中国人民万岁,为实现中华民族伟大复兴的中国梦而努力奋斗
{[para]}
🇨🇳 MESSAGE FROM THE MINISTRY OF STATE 🇨🇳
(我们的) 60 Social Credits have been removed from your account! Bad work citizen, Do not publicly disclose your opinion which can make our holy state look bad. Glory to the Chinese Communist Party!
(我们的) 60 Social Credits have been removed from your account!
{[para]}
🎵 Yooouuuu've
Beeeeeen

View File

@ -0,0 +1,43 @@
CREATE TABLE public.subactions (
id integer PRIMARY KEY,
sub character varying(25) NOT NULL,
user_id integer,
target_user_id integer,
target_submission_id integer,
target_comment_id integer,
created_utc integer NOT NULL,
kind character varying(32) DEFAULT NULL::character varying,
_note character varying(500) DEFAULT NULL::character varying
);
CREATE SEQUENCE public.subactions_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.subactions_id_seq OWNED BY public.subactions.id;
ALTER TABLE ONLY public.subactions ALTER COLUMN id SET DEFAULT nextval('public.subactions_id_seq'::regclass);
CREATE INDEX fki_subactions_user_fkey ON public.subactions USING btree (target_user_id);
CREATE INDEX modaction_action_idx ON public.subactions USING btree (kind);
CREATE INDEX modaction_pid_idx ON public.subactions USING btree (target_submission_id);
ALTER TABLE ONLY public.subactions
ADD CONSTRAINT subactions_submission_fkey FOREIGN KEY (target_submission_id) REFERENCES public.submissions(id);
CREATE INDEX modaction_cid_idx ON public.subactions USING btree (target_comment_id);
ALTER TABLE ONLY public.subactions
ADD CONSTRAINT subactions_comment_fkey FOREIGN KEY (target_comment_id) REFERENCES public.comments(id);
ALTER TABLE ONLY public.subactions
ADD CONSTRAINT subactions_user_fkey FOREIGN KEY (target_user_id) REFERENCES public.users(id);
ALTER TABLE ONLY public.subactions
ADD CONSTRAINT subactions_sub_fkey FOREIGN KEY (sub) REFERENCES public.subs(name);