diff --git a/.gitignore b/.gitignore
index 560f817d7..0f168e47f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,10 @@
-image.*
-video.mp4
-unsanitized.mp4
-cache/
-__pycache__/
-.idea/
-**/.pytest_cache/
+.*
venv/
-.vscode/
-.sass-cache/
-flask_session/
-.DS_Store
-site_settings.json
-/files/test.py
-tags
+__pycache__/
# Chat environment
chat/node_modules
chat/build
-chat/.env
# Chat artefacts
files/assets/css/chat_done.css
diff --git a/chat/src/features/chat/ChatMessage.css b/chat/src/features/chat/ChatMessage.css
index 13b1be018..d338ad70f 100644
--- a/chat/src/features/chat/ChatMessage.css
+++ b/chat/src/features/chat/ChatMessage.css
@@ -1,7 +1,6 @@
.ChatMessage {
position: relative;
padding-right: 1.5rem;
- overflow: hidden;
max-height: 300px;
}
@@ -39,6 +38,7 @@
align-items: center;
justify-content: space-between;
padding-left: 30px;
+ overflow: hidden;
}
.ChatMessage-content {
diff --git a/docker-compose.yml b/docker-compose.yml
index ea8351575..7d8da39ca 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,17 +15,26 @@ services:
depends_on:
- redis
- postgres
+ logging:
+ options:
+ max-size: "100k"
+ max-file: "1"
+
redis:
container_name: "redis"
image: redis
ports:
- "6379:6379"
+ logging:
+ options:
+ max-size: "100k"
+ max-file: "1"
postgres:
container_name: "postgres"
image: postgres
- command: ["postgres", "-c", "log_statement=all"]
+ #command: ["postgres", "-c", "log_statement=all"]
volumes:
- "./schema.sql:/docker-entrypoint-initdb.d/00-schema.sql"
- "./seed-db.sql:/docker-entrypoint-initdb.d/10-seed-db.sql"
@@ -33,9 +42,17 @@ services:
- POSTGRES_HOST_AUTH_METHOD=trust
ports:
- "5432:5432"
+ logging:
+ options:
+ max-size: "100k"
+ max-file: "1"
opera-proxy:
container_name: "opera-proxy"
image: yarmak/opera-proxy
ports:
- "18080:18080"
+ logging:
+ options:
+ max-size: "100k"
+ max-file: "1"
diff --git a/files/assets/css/main.css b/files/assets/css/main.css
index 7d02c39a6..954746a51 100644
--- a/files/assets/css/main.css
+++ b/files/assets/css/main.css
@@ -1853,7 +1853,7 @@ button.close {
color: #fff;
text-align: center;
background-color: #000;
- border-radius: 0.35rem;
+ border-radius: 0.2rem;
}
.popover {
position: absolute;
@@ -6384,3 +6384,18 @@ div.markdown {
object-fit: cover;
max-height: min(42vh,30vw) !important;
}
+
+.modlog-action {
+ display: flex;
+ flex-wrap: wrap;
+ background: var(--gray-600) !important;
+ position: relative;
+ align-items: center;
+ justify-content: space-between;
+ overflow-wrap: anywhere;
+ padding: 1rem 1rem 1rem 1rem;
+}
+
+.modlog-action:not(:first-of-type) {
+ border-top: 1px solid var(--gray-400) !important;
+}
diff --git a/files/assets/emojis.json b/files/assets/emojis.json
index b84daf8dc..160bc660d 100644
--- a/files/assets/emojis.json
+++ b/files/assets/emojis.json
@@ -664,6 +664,7 @@
{"name":"brainletchadmask","class":"Wojak"},
{"name":"bruce","class":"Wojak"},
{"name":"chadagent","class":"Wojak"},
+ {"name":"chadpostal","class":"Wojak"},
{"name":"chadblack2","class":"Wojak"},
{"name":"chadsnoo","class":"Wojak"},
{"name":"daddysgirl","class":"Wojak"},
@@ -694,6 +695,7 @@
{"name":"trumpjaktalking","class":"Wojak"},
{"name":"rdramajanny","tags": ["janny"],"class":"Wojak"},
{"name":"soyreddit","class":"Wojak"},
+ {"name":"soysnoo","class":"Wojak"},
{"name":"doomerboy","class":"Wojak"},
{"name":"npcsupport","class":"Wojak"},
{"name":"npcoppse","class":"Wojak"},
@@ -761,6 +763,7 @@
{"name":"chudgrug","class":"Wojak"},
{"name":"chudnazi","class":"Wojak"},
{"name":"chudsmug","class":"Wojak"},
+ {"name":"chudglassesglow","class":"Wojak"},
{"name":"soy4dchess","class":"Wojak"},
{"name":"soyjaktantrum","class":"Wojak"},
{"name":"psychojak","class":"Wojak"},
@@ -807,6 +810,7 @@
{"name":"chadyescapy", "tags":["capy", "aevann"],"class":"Misc"},
{"name":"chadnocapy", "tags":["capy", "aevann"],"class":"Misc"},
{"name":"capygitcommit", "tags":["capy", "aevann"],"class":"Misc"},
+ {"name":"capyantischizo", "class":"Misc"},
{"name":"capysneedboat", "tags":["capy", "aevann"],"class":"Misc"},
{"name":"chadbasedcapy", "tags":["capy", "aevann"],"class":"Misc"},
{"name":"chadcopecapy", "tags":["capy", "aevann"],"class":"Misc"},
@@ -842,6 +846,7 @@
{"name":"brookscringe","class":"Misc"},
{"name":"brooksjustright","class":"Misc"},
{"name":"brookskiss","class":"Misc"},
+ {"name":"brooksbailiffunamused","class":"Misc"},
{"name":"opperblink","tags":["sue", "opper", "darrell", "brooks", "prosecutor", "district", "attorney", "da", "annoyed", "irritated", "waukesha"],"class":"Misc"},
{"name":"gimp","class":"Misc"},
{"name":"taddance","class":"Misc","tags":["terry","davis","templeos","dance"]},
@@ -852,6 +857,8 @@
{"name":"yotsubafish","class":"Misc"},
{"name":"yotsubalol","class":"Misc"},
{"name":"sigmatalking","class":"Misc"},
+ {"name":"peoplewhoannoy","class":"Misc"},
+ {"name":"pepedrum", "tags":["football","soccer"], "class":"Misc"},
{"name":"zoroarkconfused","class":"Misc"},
{"name":"zoroarkhappy","class":"Misc"},
{"name":"zoroarkpout","class":"Misc"},
@@ -879,6 +886,7 @@
{"name":"gigachad","tags": ["chad"],"class":"Misc"},
{"name":"gigachad2", "tags": ["chad"], "class":"Misc"},
{"name":"gigachad3", "tags": ["chad"], "class":"Misc"},
+ {"name":"gigachad4", "tags": ["chad"], "class":"Misc"},
{"name":"pedobear", "class":"Misc"},
{"name":"kippy","class":"Misc"},
{"name":"onerat","class":"Misc"},
@@ -886,10 +894,17 @@
{"name":"duckdance", "class":"Misc"},
{"name":"stoning","class":"Misc"},
{"name":"stoningpills","class":"Misc"},
+ {"name":"stoningupvotes","class":"Misc"},
+ {"name":"stoningdownvotes","class":"Misc"},
+ {"name":"stoningbans","class":"Misc"},
+ {"name":"stoningunbans","class":"Misc"},
+ {"name":"stoningpins","class":"Misc"},
+ {"name":"stoningunpins","class":"Misc"},
{"name":"srdinepoppy","class":"Misc"},
{"name":"gunt","class":"Misc"},
{"name":"sneedbuddy","class":"Misc"},
{"name":"chuckbuddy","class":"Misc"},
+ {"name":"mallocbuddy","class":"Misc"},
{"name":"soren","class":"Misc"},
{"name":"upsoren","class":"Misc"},
{"name":"downdonger","class":"Misc"},
diff --git a/files/assets/images/backgrounds/glitter/1.webp b/files/assets/images/backgrounds/glitter/1.webp
new file mode 100644
index 000000000..6d0f22462
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/1.webp differ
diff --git a/files/assets/images/backgrounds/glitter/2.webp b/files/assets/images/backgrounds/glitter/2.webp
new file mode 100644
index 000000000..dd3f0cef2
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/2.webp differ
diff --git a/files/assets/images/backgrounds/glitter/3.webp b/files/assets/images/backgrounds/glitter/3.webp
new file mode 100644
index 000000000..84ec56ef0
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/3.webp differ
diff --git a/files/assets/images/backgrounds/glitter/4.webp b/files/assets/images/backgrounds/glitter/4.webp
new file mode 100644
index 000000000..b39005b0c
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/4.webp differ
diff --git a/files/assets/images/backgrounds/glitter/5.webp b/files/assets/images/backgrounds/glitter/5.webp
new file mode 100644
index 000000000..ea0443181
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/5.webp differ
diff --git a/files/assets/images/backgrounds/glitter/6.webp b/files/assets/images/backgrounds/glitter/6.webp
new file mode 100644
index 000000000..2c488fbcd
Binary files /dev/null and b/files/assets/images/backgrounds/glitter/6.webp differ
diff --git a/files/assets/images/emojis/brooksbailiffunamused.webp b/files/assets/images/emojis/brooksbailiffunamused.webp
new file mode 100644
index 000000000..dc8d5fb5f
Binary files /dev/null and b/files/assets/images/emojis/brooksbailiffunamused.webp differ
diff --git a/files/assets/images/emojis/brookscringe.webp b/files/assets/images/emojis/brookscringe.webp
index 47b5b481b..220acd55e 100644
Binary files a/files/assets/images/emojis/brookscringe.webp and b/files/assets/images/emojis/brookscringe.webp differ
diff --git a/files/assets/images/emojis/capyantischizo.webp b/files/assets/images/emojis/capyantischizo.webp
new file mode 100644
index 000000000..c2d99b220
Binary files /dev/null and b/files/assets/images/emojis/capyantischizo.webp differ
diff --git a/files/assets/images/emojis/chadpostal.webp b/files/assets/images/emojis/chadpostal.webp
new file mode 100644
index 000000000..e5e443eb9
Binary files /dev/null and b/files/assets/images/emojis/chadpostal.webp differ
diff --git a/files/assets/images/emojis/chudglassesglow.webp b/files/assets/images/emojis/chudglassesglow.webp
new file mode 100644
index 000000000..016dc3726
Binary files /dev/null and b/files/assets/images/emojis/chudglassesglow.webp differ
diff --git a/files/assets/images/emojis/gigachad4.webp b/files/assets/images/emojis/gigachad4.webp
new file mode 100644
index 000000000..473ba9121
Binary files /dev/null and b/files/assets/images/emojis/gigachad4.webp differ
diff --git a/files/assets/images/emojis/mallocbuddy.webp b/files/assets/images/emojis/mallocbuddy.webp
new file mode 100644
index 000000000..cb45781c3
Binary files /dev/null and b/files/assets/images/emojis/mallocbuddy.webp differ
diff --git a/files/assets/images/emojis/peoplewhoannoy.webp b/files/assets/images/emojis/peoplewhoannoy.webp
new file mode 100644
index 000000000..f7f0f8115
Binary files /dev/null and b/files/assets/images/emojis/peoplewhoannoy.webp differ
diff --git a/files/assets/images/emojis/pepedrum.webp b/files/assets/images/emojis/pepedrum.webp
new file mode 100644
index 000000000..a16429d28
Binary files /dev/null and b/files/assets/images/emojis/pepedrum.webp differ
diff --git a/files/assets/images/emojis/soysnoo.webp b/files/assets/images/emojis/soysnoo.webp
new file mode 100644
index 000000000..9258aeeaa
Binary files /dev/null and b/files/assets/images/emojis/soysnoo.webp differ
diff --git a/files/assets/images/emojis/stoningbans.webp b/files/assets/images/emojis/stoningbans.webp
new file mode 100644
index 000000000..68a0daa36
Binary files /dev/null and b/files/assets/images/emojis/stoningbans.webp differ
diff --git a/files/assets/images/emojis/stoningdownvotes.webp b/files/assets/images/emojis/stoningdownvotes.webp
new file mode 100644
index 000000000..2d5552540
Binary files /dev/null and b/files/assets/images/emojis/stoningdownvotes.webp differ
diff --git a/files/assets/images/emojis/stoningpins.webp b/files/assets/images/emojis/stoningpins.webp
new file mode 100644
index 000000000..7d2d0d1b4
Binary files /dev/null and b/files/assets/images/emojis/stoningpins.webp differ
diff --git a/files/assets/images/emojis/stoningunbans.webp b/files/assets/images/emojis/stoningunbans.webp
new file mode 100644
index 000000000..030474312
Binary files /dev/null and b/files/assets/images/emojis/stoningunbans.webp differ
diff --git a/files/assets/images/emojis/stoningunpins.webp b/files/assets/images/emojis/stoningunpins.webp
new file mode 100644
index 000000000..7e1c43a9d
Binary files /dev/null and b/files/assets/images/emojis/stoningunpins.webp differ
diff --git a/files/assets/images/emojis/stoningupvotes.webp b/files/assets/images/emojis/stoningupvotes.webp
new file mode 100644
index 000000000..4577f582b
Binary files /dev/null and b/files/assets/images/emojis/stoningupvotes.webp differ
diff --git a/files/assets/images/rDrama/sidebar/866.webp b/files/assets/images/rDrama/sidebar/866.webp
new file mode 100644
index 000000000..3fc64c898
Binary files /dev/null and b/files/assets/images/rDrama/sidebar/866.webp differ
diff --git a/files/assets/js/core.js b/files/assets/js/core.js
index 5f8d31533..c65401dcb 100644
--- a/files/assets/js/core.js
+++ b/files/assets/js/core.js
@@ -180,14 +180,18 @@ function smoothScrollTop()
}
// Click navbar to scroll back to top
-document.getElementsByTagName('nav')[0].addEventListener('click', (e) => {
- if (e.target.id === "navbar" ||
- e.target.classList.contains("container-fluid") ||
- e.target.id == "navbarResponsive" ||
- e.target.id == "logo-container" ||
- e.target.classList.contains("srd"))
- smoothScrollTop();
-}, false);
+const nav = document.getElementsByTagName('nav')
+
+if (nav) {
+ nav[0].addEventListener('click', (e) => {
+ if (e.target.id === "navbar" ||
+ e.target.classList.contains("container-fluid") ||
+ e.target.id == "navbarResponsive" ||
+ e.target.id == "logo-container" ||
+ e.target.classList.contains("srd"))
+ smoothScrollTop();
+ }, false);
+}
// Dynamic shadow when the user scrolls
document.addEventListener('scroll',function (event) {
@@ -403,9 +407,3 @@ function sendFormXHRSwitch(e) {
}
)
}
-
-if ("serviceWorker" in navigator) {
- navigator.serviceWorker.register("/service-worker.js?v=3")
- .then((registration) => registration.update())
- .catch((e) => console.log("Service worker update failed with error", e));
-}
diff --git a/files/assets/js/settings_profile.js b/files/assets/js/settings_profile.js
index bbc2d5ee8..3ff9126f7 100644
--- a/files/assets/js/settings_profile.js
+++ b/files/assets/js/settings_profile.js
@@ -11,6 +11,18 @@ function post(url) {
function updatebgselection(){
var bgselector = document.getElementById("backgroundSelector");
const backgrounds = [
+ {
+ folder: "glitter",
+ backgrounds:
+ [
+ "1.webp",
+ "2.webp",
+ "3.webp",
+ "4.webp",
+ "5.webp",
+ "6.webp",
+ ]
+ },
{
folder: "anime",
backgrounds:
diff --git a/files/assets/offline.html b/files/assets/offline.html
index d586c1cd0..e6067c4a4 100644
--- a/files/assets/offline.html
+++ b/files/assets/offline.html
@@ -1,71 +1,72 @@
No Internet Connection
+
-
+
No Internet
You need an internet connection to browse this site
diff --git a/files/classes/clients.py b/files/classes/clients.py
index 86f2e93ba..3e6a5ab2e 100644
--- a/files/classes/clients.py
+++ b/files/classes/clients.py
@@ -35,7 +35,7 @@ class OauthApp(Base):
@property
@lazy
def permalink(self):
- return f"{SITE_FULL}/admin/app/{self.id}/posts"
+ return f"{SITE_FULL}/admin/app/{self.id}"
@lazy
def idlist(self, db:scoped_session, page=1):
diff --git a/files/classes/comment.py b/files/classes/comment.py
index 5972622d3..b1cfa23bd 100644
--- a/files/classes/comment.py
+++ b/files/classes/comment.py
@@ -49,6 +49,7 @@ class Comment(Base):
is_bot = Column(Boolean, default=False)
stickied = Column(String)
stickied_utc = Column(Integer)
+ stickied_child_id = Column(Integer)
sentto = Column(Integer, ForeignKey("users.id"))
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
upvotes = Column(Integer, default=1)
@@ -139,7 +140,7 @@ class Comment(Base):
if self.replies2 != None:
return self.replies2
- replies = db.query(Comment).filter_by(parent_comment_id=self.id).order_by(Comment.stickied)
+ replies = db.query(Comment).filter_by(parent_comment_id=self.id).order_by(Comment.stickied, Comment.stickied_child_id)
if not self.parent_submission: sort='old'
return sort_objects(sort, replies, Comment,
include_shadowbanned=(v and v.can_see_shadowbanned)).all()
@@ -242,7 +243,7 @@ class Comment(Base):
@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] or (self.parent_comment and v.id == self.parent_comment.author_id))):
+ 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} or (self.parent_comment and v.id == self.parent_comment.author_id))):
return f"
{CC} ONLY
"
if self.deleted_utc != 0 and not (v and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.id == self.author.id)): return "[Deleted by user]"
if self.is_banned and not (v and v.admin_level >= PERMS['POST_COMMENT_MODERATION']) and not (v and v.id == self.author.id): return ""
@@ -293,11 +294,14 @@ class Comment(Base):
if not self.ghost and self.author.show_sig(v):
body += f"
{self.author.sig_html}"
+ if v:
+ body = body.replace("!YOU!", v.username)
+
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] or (self.parent_comment and v.id == self.parent_comment.author_id))):
+ 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} or (self.parent_comment and v.id == self.parent_comment.author_id))):
return f"{CC} ONLY"
if self.deleted_utc != 0 and not (v and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.id == self.author.id)): return "[Deleted by user]"
if self.is_banned and not (v and v.admin_level >= PERMS['POST_COMMENT_MODERATION']) and not (v and v.id == self.author.id): return ""
@@ -308,6 +312,9 @@ class Comment(Base):
body = censor_slurs(body, v).replace('
', ':marseytrain:')
+ if v:
+ body = body.replace("!YOU!", v.username)
+
return body
@lazy
diff --git a/files/classes/submission.py b/files/classes/submission.py
index 655ef47ee..526f8112f 100644
--- a/files/classes/submission.py
+++ b/files/classes/submission.py
@@ -324,6 +324,9 @@ class Submission(Base):
if not listing and not self.ghost and self.author.show_sig(v):
body += f"
{self.author.sig_html}"
+ if v:
+ body = body.replace("!YOU!", v.username)
+
return body
@lazy
@@ -337,6 +340,10 @@ class Submission(Base):
body = censor_slurs(body, v).replace('
', ':marseytrain:')
body = normalize_urls_runtime(body, v)
+
+ if v:
+ body = body.replace("!YOU!", v.username)
+
return body
@lazy
@@ -350,6 +357,9 @@ class Submission(Base):
title = censor_slurs(title, v)
+ if v:
+ title = title.replace("!YOU!", v.username)
+
return title
@lazy
@@ -361,6 +371,9 @@ class Submission(Base):
title = censor_slurs(title, v).replace('
', ':marseytrain:')
+ if v:
+ title = title.replace("!YOU!", v.username)
+
return title
@property
diff --git a/files/classes/user.py b/files/classes/user.py
index 4853538c5..468de4f69 100644
--- a/files/classes/user.py
+++ b/files/classes/user.py
@@ -141,7 +141,6 @@ class User(Base):
subscriptions = relationship("Subscription", back_populates="user")
following = relationship("Follow", primaryjoin="Follow.user_id==User.id", back_populates="user")
followers = relationship("Follow", primaryjoin="Follow.target_id==User.id", back_populates="target")
- viewers = relationship("ViewerRelationship", primaryjoin="User.id == ViewerRelationship.user_id")
blocking = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.user_id", back_populates="user")
blocked = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.target_id", back_populates="target")
authorizations = relationship("ClientAuth", back_populates="user")
@@ -248,7 +247,10 @@ class User(Base):
if self.marsify > 1:
user_forced_hats.append(val)
elif getattr(self, k):
- user_forced_hats.append(val)
+ if k == 'agendaposter':
+ user_forced_hats.append(random.choice(val))
+ else:
+ user_forced_hats.append(val)
if user_forced_hats: return random.choice(user_forced_hats)
else: return None
@@ -875,7 +877,7 @@ class User(Base):
def get_relationship_count(self, relationship_cls):
# TODO: deduplicate (see routes/users.py)
- if relationship_cls in [SaveRelationship, Subscription]:
+ if relationship_cls in {SaveRelationship, Subscription}:
query = relationship_cls.submission_id
join = relationship_cls.post
cls = Submission
@@ -942,17 +944,6 @@ class User(Base):
def can_create_hole(self):
return self.admin_level >= PERMS['HOLE_CREATE']
- @property
- @lazy
- def viewers_recorded(self):
- if SITE_NAME == 'WPD': # WPD gets profile views
- return True
- elif self.admin_level >= PERMS['VIEW_PROFILE_VIEWS']: # Admins get profile views
- return True
- elif self.patron: # Patrons get profile views as a perk
- return True
- return False
-
@property
@lazy
def patron_tooltip(self):
@@ -1000,6 +991,7 @@ class User(Base):
if not cls.can_see(user, other.author): return False
if user and user.id == other.author_id: return True
if isinstance(other, Submission):
+ if "!YOU!" in other.title and not user: return False
if other.sub and not cls.can_see(user, other.subr): return False
else:
if not other.parent_submission:
@@ -1015,7 +1007,6 @@ class User(Base):
return (user and user.id == other.id) or (user and user.can_see_shadowbanned) or not other.shadowbanned
return True
-
@property
@lazy
def can_see_chudrama(self):
@@ -1029,10 +1020,10 @@ class User(Base):
@property
@lazy
def can_post_in_ghost_threads(self):
- if not TRUESCORE_GHOST_LIMIT: return True
+ if not TRUESCORE_GHOST_MINIMUM: return True
if self.admin_level >= PERMS['POST_IN_GHOST_THREADS']: return True
if self.club_allowed: return True
- if self.truescore >= TRUESCORE_GHOST_LIMIT: return True
+ if self.truescore >= TRUESCORE_GHOST_MINIMUM: return True
if self.patron: return True
return False
diff --git a/files/helpers/actions.py b/files/helpers/actions.py
index 6bc781c2e..092fab7bf 100644
--- a/files/helpers/actions.py
+++ b/files/helpers/actions.py
@@ -457,18 +457,18 @@ def execute_lawlz_actions(v:User, p:Submission):
pin_time = 'for 1 day'
ma_1=ModAction(
kind="pin_post",
- user_id=v.id,
+ user_id=AUTOJANNY_ID,
target_submission_id=p.id,
_note=pin_time
)
ma_2=ModAction(
kind="distinguish_post",
- user_id=v.id,
+ user_id=AUTOJANNY_ID,
target_submission_id=p.id
)
ma_3=ModAction(
kind="flair_post",
- user_id=v.id,
+ user_id=AUTOJANNY_ID,
target_submission_id=p.id,
_note=f'"{p.flair}"'
)
diff --git a/files/helpers/const.py b/files/helpers/const.py
index f76149704..528366c52 100644
--- a/files/helpers/const.py
+++ b/files/helpers/const.py
@@ -98,6 +98,7 @@ AJ_REPLACEMENTS = {
SLURS = {
"nigger": "BIPOC",
+ "negroid": "BIPOC",
"niglet": 'BIPOClet',
"negress": "BIPOC woman",
'nigga': 'neighbor',
@@ -149,6 +150,7 @@ if SITE_NAME == 'rDrama':
"republican": 'republiKKKan',
"america": 'ameriKKKa',
"it's almost as if": "I'm a retard but",
+ "my brother in christ": "my brother in Allah",
}
SLURS.update(RDRAMA_SLURS)
@@ -197,6 +199,7 @@ WPD_CHANNEL_ID = 1013990963846332456
PIN_AWARD_TEXT = " (pin award)"
THEMES = ["4chan","classic","classic_dark","coffee","dark","dramblr","light","midnight","transparent","tron","win98"]
+BACKGROUND_CATEGORIES = ["glitter", "anime", "fantasy", "solarpunk", "pixelart"]
COMMENT_SORTS = ["hot", "new", "old", "top", "bottom", "controversial"]
SORTS = COMMENT_SORTS + ["bump", "comments"]
TIME_FILTERS = ["hour", "day", "week", "month", "year", "all"]
@@ -255,8 +258,6 @@ PERMS = { # Minimum admin_level to perform action.
'VIEW_CHUDRAMA': 1,
'VIEW_PRIVATE_PROFILES': 2,
'VIEW_ALTS': 2,
- 'VIEW_PROFILE_VIEWS': 2,
- 'VIEW_SORTED_ADMIN_LIST': 3,
'VIEW_ACTIVE_USERS': 2,
'VIEW_ALL_USERS': 2,
'VIEW_ALT_VOTES': 2,
@@ -390,10 +391,10 @@ COMMENT_MAX_DEPTH = 200
TRANSFER_MESSAGE_LENGTH_LIMIT = 200 # do not make larger than 10000 characters (comment limit) without altering the table
MIN_REPOST_CHECK_URL_LENGTH = 9 # also change the constant in checkRepost() of submit.js
CHAT_LENGTH_LIMIT = 1000
-TRUESCORE_DONATE_LIMIT = 100
COSMETIC_AWARD_COIN_AWARD_PCT = 0.10
-TRUESCORE_CHAT_LIMIT = 0
-TRUESCORE_GHOST_LIMIT = 0
+TRUESCORE_CHAT_MINIMUM = 0
+TRUESCORE_DONATE_MINIMUM = 100
+TRUESCORE_GHOST_MINIMUM = 0
LOGGEDIN_ACTIVE_TIME = 15 * 60
PFP_DEFAULT_MARSEY = True
@@ -461,7 +462,8 @@ MAX_VIDEO_SIZE_MB = 32
MAX_VIDEO_SIZE_MB_PATRON = 64
MAX_IMAGE_CONVERSION_TIMEOUT = 15 # seconds
-ANTISPAM_BYPASS_IDS = ()
+ANTISPAM_BYPASS_IDS = set()
+BOOSTED_USERS_EXCLUDED = set()
PAGE_SIZE = 25
LEADERBOARD_LIMIT = PAGE_SIZE
@@ -495,8 +497,8 @@ if SITE == 'rdrama.net':
NOTIFICATION_THREAD = 6489
CHAT_LENGTH_LIMIT = 200
- TRUESCORE_CHAT_LIMIT = 10
- TRUESCORE_GHOST_LIMIT = 10
+ TRUESCORE_CHAT_MINIMUM = 10
+ TRUESCORE_GHOST_MINIMUM = 10
NEW_USER_HAT_AGE = 7 * 86400
HOLE_COST = 50000
@@ -530,7 +532,34 @@ if SITE == 'rdrama.net':
GEESE_ID = 1710
BLACKJACKBTZ_ID = 12732
- ANTISPAM_BYPASS_IDS = (1703, 13427)
+ ANTISPAM_BYPASS_IDS = {1703, 13427}
+
+ BOOSTED_HOLES = {
+ 'furry',
+ 'femboy',
+ 'anime',
+ 'gaybros',
+ 'againsthateholes',
+ 'masterbaiters',
+ 'changelog',
+ }
+
+ BOOSTED_USERS = {
+ IMPASSIONATA_ID,
+ PIZZASHILL_ID,
+ SNAKES_ID,
+ JUSTCOOL_ID,
+ 2008, #TransGirlTradWife
+ 29, #QuadNarca
+ JOAN_ID,
+ }
+
+ BOOSTED_USERS_EXCLUDED = {
+ 8768,
+ 3402,
+ 5214,
+ 12719
+ }
GIFT_NOTIF_ID = CARP_ID
@@ -576,35 +605,40 @@ elif SITE == 'watchpeopledie.tv':
PERMS['HOLE_CREATE'] = 2
PERMS['POST_EDITING'] = 2
PERMS['ADMIN_ADD'] = 4
-
- ERROR_TITLES[400] = "Bad Request"
- ERROR_TITLES[401] = "Unauthorized"
- ERROR_TITLES[403] = "Forbidden"
- ERROR_TITLES[404] = "Not Found"
- ERROR_TITLES[405] = "Method Not Allowed"
- ERROR_TITLES[406] = "Too Many Pings"
- ERROR_TITLES[409] = "Mortal Conflict"
- ERROR_TITLES[410] = "Dead"
- ERROR_TITLES[413] = "Payload Too Large"
- ERROR_TITLES[415] = "Unsupported Media Type"
- ERROR_TITLES[500] = "Internal Server Error"
- ERROR_MSGS[400] = "That request is invalid"
- ERROR_MSGS[401] = "You need to login or sign up to do that"
- ERROR_MSGS[403] = "You're not allowed to do that"
- ERROR_MSGS[404] = "That wasn't found"
- ERROR_MSGS[405] = "You can't use this method here... if you keep getting this error tell us it's prolly something borked"
- ERROR_MSGS[409] = "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do."
- ERROR_MSGS[410] = "This link is dead. Request a new one to try again"
- ERROR_MSGS[413] = "You need to upload a smaller file please"
- ERROR_MSGS[429] = "Please wait a bit before doing that"
+
+ ERROR_TITLES.update({
+ 400: "Bad Request",
+ 401: "Unauthorized",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 405: "Method Not Allowed",
+ 406: "Too Many Pings",
+ 409: "Mortal Conflict",
+ 410: "Dead",
+ 413: "Payload Too Large",
+ 415: "Unsupported Media Type",
+ 500: "Internal Server Error",
+ })
+
+ ERROR_MSGS.update({
+ 400: "That request is invalid",
+ 401: "You need to login or sign up to do that",
+ 403: "You're not allowed to do that",
+ 404: "That wasn't found",
+ 405: "You can't use this method here... if you keep getting this error tell us it's prolly something borked",
+ 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do.",
+ 410: "This link is dead. Request a new one to try again",
+ 413: "You need to upload a smaller file please",
+ 429: "Please wait a bit before doing that",
+ })
POLL_THREAD = 13225
SIDEBAR_THREAD = 5403
BANNER_THREAD = 9869
- TRUESCORE_CHAT_LIMIT = 10
- TRUESCORE_GHOST_LIMIT = 10
+ TRUESCORE_CHAT_MINIMUM = 10
+ TRUESCORE_GHOST_MINIMUM = 10
HOLE_NAME = 'flair'
HOLE_STYLE_FLAIR = True
@@ -1419,9 +1453,7 @@ NOTIFIED_USERS = {
}
FORTUNE_REPLIES = ('
Your fortune: Allah Wills It','
Your fortune: Inshallah, Only Good Things Shall Come To Pass','
Your fortune: Allah Smiles At You This Day','
Your fortune: Your Bussy Is In For A Blasting','
Your fortune: You Will Be Propositioned By A High-Tier Twink','
Your fortune: Repent, You Have Displeased Allah And His Vengeance Is Nigh','
Your fortune: Reply Hazy, Try Again','
Your fortune: lmao you just lost 100 coins','
Your fortune: Yikes π¬','
Your fortune: You Will Be Blessed With Many Black Bulls','
Your fortune: NEETmax, The Day Is Lost If You Venture Outside','
Your fortune: A Taste Of Jannah Awaits You Today','
Your fortune: Watch Your Back','
Your fortune: Outlook good','
Your fortune: Godly Luck','
Your fortune: Good Luck','
Your fortune: Bad Luck','
Your fortune: Good news will come to you by mail','
Your fortune: Very Bad Luck','
Your fortune: ο½·οΎββββββ(οΎβοΎ)ββββββ !!!!','
Your fortune: Better not tell you now','
Your fortune: You will meet a dark handsome stranger','
Your fortune: οΌγΒ΄_γ`οΌοΎο½°οΎ','
Your fortune: Excellent Luck','
Your fortune: Average Luck')
-
FACTCHECK_REPLIES = ('
Factcheck: This claim has been confirmed as correct by experts. ','
Factcheck: This claim has been classified as misogynistic.','
Factcheck: This claim is currently being debunked.','
Factcheck: This claim is 100% true.','
Factcheck: This claim hurts trans lives.','
Factcheck: [REDACTED].','
Factcheck: This claim is both true and false.','
Factcheck: You really believe that shit? Lmao dumbass nigga π€£','
Factcheck: None of this is real.','
Factcheck: Yes.','
Factcheck: This claim has not been approved by experts.','
Factcheck: This claim is a gross exageration of reality.','
Factcheck: WARNING! THIS CLAIM HAS BEEN CLASSIFIED AS DANGEROUS. PLEASE REMAIN STILL, AN AGENT WILL COME TO MEET YOU SHORTLY.')
-
EIGHTBALL_REPLIES = ('
The 8-Ball Says: It is certain.', '
The 8-Ball Says: It is decidedly so.', '
The 8-Ball Says: Without a doubt.', '
The 8-Ball Says: Yes definitely.', '
The 8-Ball Says: You may rely on it.', '
The 8-Ball Says: As I see it, yes.', '
The 8-Ball Says: Most likely.', '
The 8-Ball Says: Outlook good.', '
The 8-Ball Says: Yes.', '
The 8-Ball Says: Signs point to yes.', '
The 8-Ball Says: Reply hazy, try again.', '
The 8-Ball Says: Ask again later.', '
The 8-Ball Says: Better not tell you now.', '
The 8-Ball Says: Cannot predict now.', '
The 8-Ball Says: Concentrate and ask again.', '
The 8-Ball Says: Don\'t count on it.', '
The 8-Ball Says: My reply is no.', '
The 8-Ball Says: My sources say no.', '
The 8-Ball Says: Outlook not so good.', '
The 8-Ball Says: Very doubtful.')
REDDIT_NOTIFS_SITE = set()
@@ -1588,7 +1620,12 @@ forced_hats = {
"earlylife": ("The Merchant", "SHUT IT DOWN, the goys know!"),
"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!")
+ "agendaposter": (("Egg_irl", "This user is getting in touch with xir identity!"),
+ ("Trans Flag", "Just in case you forgot, trans lives matter."),
+ ("Trans Flag II", "Your egg is cracked; wear it with pride!"),
+ ("Pride Flag", "Never forget that this is a primarily gay community. Dude bussy lmao."),
+ ("Pride Flag II", "This user is a proud supporter of LGBTQ+ rights."))
+
}
EMAIL_REGEX_PATTERN = '[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{2,63}\.[A-Za-z]{2,63}'
@@ -1662,6 +1699,7 @@ if SITE_NAME == 'rDrama':
'backloggd.com',
'tildes.net',
'blacktwitterapp.com',
+ 'dailystormer.in',
#fediverse
'rdrama.cc',
@@ -1676,26 +1714,6 @@ if SITE_NAME == 'rDrama':
'seal.cafe',
}
- BOOSTED_HOLES = {
- 'furry',
- 'femboy',
- 'anime',
- 'gaybros',
- 'againsthateholes',
- 'masterbaiters',
- 'changelog',
- }
-
- BOOSTED_USERS = {
- IMPASSIONATA_ID,
- PIZZASHILL_ID,
- SNAKES_ID,
- JUSTCOOL_ID,
- 2008, #TransGirlTradWife
- }
-
- BOOSTED_USERS_EXCLUDED = {8768, 5214, 12719, 3402}
-
IMAGE_FORMATS = ['png','gif','jpg','jpeg','webp']
VIDEO_FORMATS = ['mp4','webm','mov','avi','mkv','flv','m4v','3gp']
AUDIO_FORMATS = ['mp3','wav','ogg','aac','m4a','flac']
diff --git a/files/helpers/get.py b/files/helpers/get.py
index d04e480b6..85c4c2056 100644
--- a/files/helpers/get.py
+++ b/files/helpers/get.py
@@ -243,7 +243,7 @@ def add_block_props(target:Union[Submission, Comment, User], v:Optional[User]):
if not v: return target
id = None
- if any(isinstance(target, cls) for cls in [Submission, Comment]):
+ if any(isinstance(target, cls) for cls in {Submission, Comment}):
id = target.author_id
elif isinstance(target, User):
id = target.id
diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py
index 21b3315f5..6a156e486 100644
--- a/files/helpers/sanitize.py
+++ b/files/helpers/sanitize.py
@@ -52,7 +52,7 @@ def allowed_attributes(tag, name, value):
if name == 'style': return True
if tag == 'marquee':
- if name in ['direction', 'behavior', 'scrollamount']: return True
+ if name in {'direction', 'behavior', 'scrollamount'}: return True
if name in {'height', 'width'}:
try: value = int(value.replace('px', ''))
except: return False
@@ -67,11 +67,11 @@ def allowed_attributes(tag, name, value):
return False
if tag == 'img':
- if name in ['src','data-src']: return is_safe_url(value)
+ if name in {'src','data-src'}: return is_safe_url(value)
if name == 'loading' and value == 'lazy': return True
if name == 'data-bs-toggle' and value == 'tooltip': return True
- if name in ['g','b','glow'] and not value: return True
- if name in ['alt','title']: return True
+ if name in {'g','b','glow'} and not value: return True
+ if name in {'alt','title'}: return True
return False
if tag == 'lite-youtube':
@@ -426,8 +426,8 @@ def allowed_attributes_emojis(tag, name, value):
if name == 'src' and value.startswith('/') and '\\' not in value: return True
if name == 'loading' and value == 'lazy': return True
if name == 'data-bs-toggle' and value == 'tooltip': return True
- if name in ['g','glow'] and not value: return True
- if name in ['alt','title']: return True
+ if name in {'g','glow'} and not value: return True
+ if name in {'alt','title'}: return True
if tag == 'span':
if name == 'data-bs-toggle' and value == 'tooltip': return True
diff --git a/files/routes/admin.py b/files/routes/admin.py
index 047e10a65..6bd254d10 100644
--- a/files/routes/admin.py
+++ b/files/routes/admin.py
@@ -546,9 +546,11 @@ def badge_grant_post(v):
if desc: new_badge.description = desc
url = request.values.get("url")
- if '\\' in url: abort(400)
-
- if url: new_badge.url = url
+ if url:
+ if '\\' in url: abort(400)
+ if url.startswith(SITE_FULL):
+ url = url.split(SITE_FULL, 1)[1]
+ new_badge.url = url
g.db.add(new_badge)
g.db.flush()
@@ -829,7 +831,7 @@ def admin_removed_comments(v):
try: page = int(request.values.get("page", 1))
except: page = 1
- ids = g.db.query(Comment.id).join(Comment.author).filter(or_(Comment.is_banned==True, User.shadowbanned != None)).order_by(Comment.id.desc()).offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE).all()
+ ids = g.db.query(Comment.id).join(Comment.author).filter(or_(Comment.is_banned==True, User.shadowbanned != None)).order_by(Comment.id.desc()).offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE + 1).all()
ids=[x[0] for x in ids]
next_exists = len(ids) > PAGE_SIZE
ids = ids[:PAGE_SIZE]
@@ -1332,6 +1334,12 @@ def sticky_comment(cid, v):
message = f"@{v.username} (Admin) has pinned your [comment]({comment.shortlink})"
send_repeatable_notification(comment.author_id, message)
+ c = comment
+ while c.level > 2:
+ c = c.parent_comment
+ c.stickied_child_id = comment.id
+ g.db.add(c)
+
return {"message": "Comment pinned!"}
@@ -1357,6 +1365,11 @@ def unsticky_comment(cid, v):
message = f"@{v.username} (Admin) has unpinned your [comment]({comment.shortlink})"
send_repeatable_notification(comment.author_id, message)
+ cleanup = g.db.query(Comment).filter_by(stickied_child_id=comment.id).all()
+ for c in cleanup:
+ c.stickied_child_id = None
+ g.db.add(c)
+
return {"message": "Comment unpinned!"}
diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py
index 20605c855..fa339620c 100644
--- a/files/routes/asset_submissions.py
+++ b/files/routes/asset_submissions.py
@@ -28,7 +28,7 @@ def asset_submissions(path):
@app.get("/submit/marseys")
@auth_required
-def submit_marseys(v):
+def submit_marseys(v:User):
if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_MARSEYS']:
marseys = g.db.query(Marsey).filter(Marsey.submitter_id != None).all()
else:
@@ -43,7 +43,7 @@ def submit_marseys(v):
@app.post("/submit/marseys")
@auth_required
-def submit_marsey(v):
+def submit_marsey(v:User):
file = request.files["image"]
name = request.values.get('name', '').lower().strip()
tags = request.values.get('tags', '').lower().strip()
@@ -195,12 +195,12 @@ def remove_asset(cls, type_name:str, v:User, name:str) -> dict[str, str]:
@app.post("/remove/marsey/
")
@auth_required
-def remove_marsey(v, name):
+def remove_marsey(v:User, name):
return remove_asset(Marsey, "marsey", v, name)
@app.get("/submit/hats")
@auth_required
-def submit_hats(v):
+def submit_hats(v:User):
if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_HATS']: hats = g.db.query(HatDef).filter(HatDef.submitter_id != None).all()
else: hats = g.db.query(HatDef).filter(HatDef.submitter_id == v.id).all()
return render_template("submit_hats.html", v=v, hats=hats)
@@ -208,7 +208,7 @@ def submit_hats(v):
@app.post("/submit/hats")
@auth_required
-def submit_hat(v):
+def submit_hat(v:User):
name = request.values.get('name', '').strip()
description = request.values.get('description', '').strip()
username = request.values.get('author', '').strip()
@@ -328,7 +328,7 @@ def approve_hat(v, name):
@app.post("/remove/hat/")
@auth_required
-def remove_hat(v, name):
+def remove_hat(v:User, name):
return remove_asset(HatDef, 'hat', v, name)
@app.get("/admin/update/marseys")
diff --git a/files/routes/awards.py b/files/routes/awards.py
index 6b3a18d9f..ee584f8c5 100644
--- a/files/routes/awards.py
+++ b/files/routes/awards.py
@@ -22,7 +22,7 @@ from .front import frontlist
@app.get("/shop")
@app.get("/settings/shop")
@auth_required
-def shop(v):
+def shop(v:User):
AWARDS = deepcopy(AWARDS2)
if v.house:
@@ -46,7 +46,7 @@ def shop(v):
@app.post("/buy/")
@limiter.limit("100/minute;200/hour;1000/day")
@auth_required
-def buy(v, award):
+def buy(v:User, award):
if award == 'benefactor' and not request.values.get("mb"):
abort(403, "You can only buy the Benefactor award with marseybux.")
@@ -200,7 +200,7 @@ def award_thing(v, thing_type, id):
author = v
elif kind != 'spider':
awarded_coins = int(AWARDS[kind]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT) if AWARDS[kind]['cosmetic'] else 0
- if AWARDS[kind]['cosmetic']:
+ if AWARDS[kind]['cosmetic'] and kind != 'shit':
author.pay_account('coins', awarded_coins)
msg = f"@{v.username} has given your [{thing_type}]({thing.shortlink}) the {AWARDS[kind]['title']} Award"
diff --git a/files/routes/casino.py b/files/routes/casino.py
index 93e1a830e..a55f39874 100644
--- a/files/routes/casino.py
+++ b/files/routes/casino.py
@@ -14,7 +14,7 @@ from files.__main__ import app, limiter
@app.get("/casino")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def casino(v):
+def casino(v:User):
if v.rehab:
return render_template("casino/rehab.html", v=v), 403
@@ -24,7 +24,7 @@ def casino(v):
@app.get("/casino/")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def casino_game_page(v, game):
+def casino_game_page(v:User, game):
if v.rehab:
return render_template("casino/rehab.html", v=v), 403
elif game not in CASINO_GAME_KINDS:
@@ -53,7 +53,7 @@ def casino_game_page(v, game):
@app.get("/casino//feed")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def casino_game_feed(v, game):
+def casino_game_feed(v:User, game):
if v.rehab:
abort(403, "You are under Rehab award effect!")
elif game not in CASINO_GAME_KINDS:
@@ -67,7 +67,7 @@ def casino_game_feed(v, game):
@app.get("/lottershe")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def lottershe(v):
+def lottershe(v:User):
if v.rehab:
return render_template("casino/rehab.html", v=v)
@@ -78,7 +78,7 @@ def lottershe(v):
@app.post("/casino/slots")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def pull_slots(v):
+def pull_slots(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -109,7 +109,7 @@ def pull_slots(v):
@app.post("/casino/twentyone/deal")
@limiter.limit("1/second;100/minute;2000/hour;12000/day")
@auth_required
-def blackjack_deal_to_player(v):
+def blackjack_deal_to_player(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -128,7 +128,7 @@ def blackjack_deal_to_player(v):
@app.post("/casino/twentyone/hit")
@limiter.limit("1/second;100/minute;2000/hour;12000/day")
@auth_required
-def blackjack_player_hit(v):
+def blackjack_player_hit(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -143,7 +143,7 @@ def blackjack_player_hit(v):
@app.post("/casino/twentyone/stay")
@limiter.limit("1/second;100/minute;2000/hour;12000/day")
@auth_required
-def blackjack_player_stay(v):
+def blackjack_player_stay(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -158,7 +158,7 @@ def blackjack_player_stay(v):
@app.post("/casino/twentyone/double-down")
@limiter.limit("1/second;100/minute;2000/hour;12000/day")
@auth_required
-def blackjack_player_doubled_down(v):
+def blackjack_player_doubled_down(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -173,7 +173,7 @@ def blackjack_player_doubled_down(v):
@app.post("/casino/twentyone/buy-insurance")
@limiter.limit("1/second;100/minute;2000/hour;12000/day")
@auth_required
-def blackjack_player_bought_insurance(v):
+def blackjack_player_bought_insurance(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -188,7 +188,7 @@ def blackjack_player_bought_insurance(v):
@app.get("/casino/roulette/bets")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def roulette_get_bets(v):
+def roulette_get_bets(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
@@ -200,7 +200,7 @@ def roulette_get_bets(v):
@app.post("/casino/roulette/place-bet")
@limiter.limit("100/minute;2000/hour;12000/day")
@auth_required
-def roulette_player_placed_bet(v):
+def roulette_player_placed_bet(v:User):
if v.rehab:
abort(403, "You are under Rehab award effect!")
diff --git a/files/routes/chat.py b/files/routes/chat.py
index 1fe22bd89..0a0cc3fdc 100644
--- a/files/routes/chat.py
+++ b/files/routes/chat.py
@@ -39,8 +39,8 @@ user_ids_to_socket_ids = {}
@app.get("/chat")
@is_not_permabanned
def chat(v):
- if TRUESCORE_CHAT_LIMIT and v.truescore < TRUESCORE_CHAT_LIMIT and not v.club_allowed:
- abort(403, f"Need at least {TRUESCORE_CHAT_LIMIT} truescore for access to chat.")
+ if TRUESCORE_CHAT_MINIMUM and v.truescore < TRUESCORE_CHAT_MINIMUM and not v.club_allowed:
+ abort(403, f"Need at least {TRUESCORE_CHAT_MINIMUM} truescore for access to chat.")
return render_template("chat.html", v=v, messages=messages)
@@ -50,7 +50,7 @@ def chat(v):
@ratelimit_user("3/second;10/minute")
def speak(data, v):
if v.is_banned: return '', 403
- if TRUESCORE_CHAT_LIMIT and v.truescore < TRUESCORE_CHAT_LIMIT and not v.club_allowed: return '', 403
+ if TRUESCORE_CHAT_MINIMUM and v.truescore < TRUESCORE_CHAT_MINIMUM and not v.club_allowed: return '', 403
vname = v.username.lower()
if vname in muted and not v.admin_level >= PERMS['CHAT_BYPASS_MUTE']:
diff --git a/files/routes/comments.py b/files/routes/comments.py
index a7ced47b4..108fed507 100644
--- a/files/routes/comments.py
+++ b/files/routes/comments.py
@@ -79,10 +79,11 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None):
else: template = "submission.html"
return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True, sub=post.subr)
+#- API
@app.post("/comment")
@limiter.limit("1/second;20/minute;200/hour;1000/day")
-@ratelimit_user("1/second;20/minute;200/hour;1000/day")
@auth_required
+@ratelimit_user("1/second;20/minute;200/hour;1000/day")
def comment(v):
if v.is_suspended: abort(403, "You can't perform this action while banned.")
@@ -91,7 +92,7 @@ def comment(v):
id = parent_fullname[2:]
parent_comment_id = None
rts = False
-
+
if parent_fullname.startswith("p_"):
parent = get_post(id, v=v)
parent_post = parent
@@ -101,7 +102,7 @@ def comment(v):
parent_post = get_post(parent.parent_submission, v=v)
parent_comment_id = parent.id
if parent.author_id == v.id: rts = True
- if not v.can_post_in_ghost_threads and parent_post.ghost: abort(403, f"You need {TRUESCORE_GHOST_LIMIT} truescore to post in ghost threads")
+ if not v.can_post_in_ghost_threads and parent_post.ghost: abort(403, f"You need {TRUESCORE_GHOST_MINIMUM} truescore to post in ghost threads")
else: abort(400)
level = 1 if isinstance(parent, Submission) else parent.level + 1
@@ -359,6 +360,13 @@ def comment(v):
check_slots_command(v, v, c)
+ if c.level > 5:
+ n = g.db.query(Notification).filter_by(
+ comment_id=c.parent_comment.parent_comment.parent_comment.parent_comment_id,
+ user_id=c.parent_comment.author_id,
+ ).one_or_none()
+ if n: g.db.delete(n)
+
g.db.flush()
if v.client: return c.json(db=g.db)
@@ -368,8 +376,8 @@ def comment(v):
@app.post("/edit_comment/")
@limiter.limit("1/second;10/minute;100/hour;200/day")
-@ratelimit_user("1/second;10/minute;100/hour;200/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/minute;100/hour;200/day")
def edit_comment(cid, v):
c = get_comment(cid, v=v)
diff --git a/files/routes/front.py b/files/routes/front.py
index 2ed383775..11d8b7109 100644
--- a/files/routes/front.py
+++ b/files/routes/front.py
@@ -43,7 +43,7 @@ def front_all(v, sub=None, subdomain=None):
#### WPD TEMP #### end special front logic
if sub:
sub = get_sub_by_name(sub, graceful=True)
- if sub and not User.can_see(v, sub): abort(403)
+ if sub and not User.can_see(v, sub): abort(403, "You need 5000 truescore to be able to see /h/chudrama")
if (request.path.startswith('/h/') or request.path.startswith('/s/')) and not sub: abort(404)
@@ -179,7 +179,7 @@ def frontlist(v=None, sort="hot", page=1, t="all", ids_only=True, filter_words='
@app.get("/random_post")
@auth_required
-def random_post(v):
+def random_post(v:User):
p = g.db.query(Submission.id).filter(Submission.deleted_utc == 0, Submission.is_banned == False, Submission.private == False).order_by(func.random()).first()
@@ -191,7 +191,7 @@ def random_post(v):
@app.get("/random_user")
@auth_required
-def random_user(v):
+def random_user(v:User):
u = g.db.query(User.username).filter(User.song != None, User.shadowbanned == None).order_by(func.random()).first()
if u: u = u[0]
@@ -202,7 +202,7 @@ def random_user(v):
@app.get("/comments")
@auth_required
-def all_comments(v):
+def all_comments(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
diff --git a/files/routes/hats.py b/files/routes/hats.py
index 0641efb73..fe7a4b7f8 100644
--- a/files/routes/hats.py
+++ b/files/routes/hats.py
@@ -9,7 +9,7 @@ from files.__main__ import app, limiter
@app.get("/hats")
@auth_required
-def hats(v):
+def hats(v:User):
owned_hat_ids = [x.hat_id for x in v.owned_hats]
if request.values.get("sort") == 'author_asc':
@@ -34,7 +34,7 @@ def hats(v):
@app.post("/buy_hat/")
@limiter.limit('100/minute;1000/3 days')
@auth_required
-def buy_hat(v, hat_id):
+def buy_hat(v:User, hat_id):
try: hat_id = int(hat_id)
except: abort(404, "Hat not found!")
@@ -84,7 +84,7 @@ def buy_hat(v, hat_id):
@app.post("/equip_hat/")
@auth_required
-def equip_hat(v, hat_id):
+def equip_hat(v:User, hat_id):
try: hat_id = int(hat_id)
except: abort(404, "Hat not found!")
@@ -98,7 +98,7 @@ def equip_hat(v, hat_id):
@app.post("/unequip_hat/")
@auth_required
-def unequip_hat(v, hat_id):
+def unequip_hat(v:User, hat_id):
try: hat_id = int(hat_id)
except: abort(404, "Hat not found!")
@@ -112,7 +112,7 @@ def unequip_hat(v, hat_id):
@app.get("/hat_owners/")
@auth_required
-def hat_owners(v, hat_id):
+def hat_owners(v:User, hat_id):
try: hat_id = int(hat_id)
except: abort(404, "Hat not found!")
diff --git a/files/routes/jinja2.py b/files/routes/jinja2.py
index 5017cc3c4..47754f14d 100644
--- a/files/routes/jinja2.py
+++ b/files/routes/jinja2.py
@@ -61,10 +61,10 @@ def inject_constants():
"TELEGRAM_LINK":TELEGRAM_LINK, "EMAIL_REGEX_PATTERN":EMAIL_REGEX_PATTERN,
"CONTENT_SECURITY_POLICY_DEFAULT":CONTENT_SECURITY_POLICY_DEFAULT,
"CONTENT_SECURITY_POLICY_HOME":CONTENT_SECURITY_POLICY_HOME,
- "TRUESCORE_DONATE_LIMIT":TRUESCORE_DONATE_LIMIT,
+ "TRUESCORE_DONATE_MINIMUM":TRUESCORE_DONATE_MINIMUM,
"DONATE_LINK":DONATE_LINK, "DONATE_SERVICE":DONATE_SERVICE, "BAN_EVASION_DOMAIN":BAN_EVASION_DOMAIN,
"HOUSE_JOIN_COST":HOUSE_JOIN_COST, "HOUSE_SWITCH_COST":HOUSE_SWITCH_COST, "IMAGE_FORMATS":IMAGE_FORMATS,
"PAGE_SIZES":PAGE_SIZES, "THEMES":THEMES, "COMMENT_SORTS":COMMENT_SORTS, "SORTS":SORTS,
"TIME_FILTERS":TIME_FILTERS, "HOUSES":HOUSES, "TIERS_ID_TO_NAME":TIERS_ID_TO_NAME,
- "DEFAULT_CONFIG_VALUE":DEFAULT_CONFIG_VALUE, "IS_LOCALHOST":IS_LOCALHOST,
+ "DEFAULT_CONFIG_VALUE":DEFAULT_CONFIG_VALUE, "IS_LOCALHOST":IS_LOCALHOST, "BACKGROUND_CATEGORIES":BACKGROUND_CATEGORIES,
}
diff --git a/files/routes/login.py b/files/routes/login.py
index 90eee9ee7..47175ff44 100644
--- a/files/routes/login.py
+++ b/files/routes/login.py
@@ -21,7 +21,7 @@ NO_LOGIN_REDIRECT_URLS = ("/login", "/logout", "/signup", "/forgot", "/reset", "
@app.get("/login")
@auth_desired
-def login_get(v):
+def login_get(v:Optional[User]):
redir = request.values.get("redirect", "/").strip().rstrip('?').lower()
if redir:
if not is_site_url(redir) or redir in NO_LOGIN_REDIRECT_URLS:
@@ -106,7 +106,7 @@ def login_post():
redir = request.values.get("redirect", "").strip().rstrip('?').lower()
if redir:
- if is_site_url(redir) and redir in NO_LOGIN_REDIRECT_URLS:
+ if is_site_url(redir) and redir not in NO_LOGIN_REDIRECT_URLS:
return redirect(redir)
return redirect('/')
@@ -131,7 +131,7 @@ def on_login(account, redir=None):
@app.get("/me")
@app.get("/@me")
@auth_required
-def me(v):
+def me(v:User):
if v.client: return v.json
else: return redirect(v.url)
@@ -149,7 +149,7 @@ def logout(v):
@app.get("/signup")
@auth_desired
-def sign_up_get(v):
+def sign_up_get(v:Optional[User]):
if not get_setting('Signups'):
abort(403, "New account registration is currently closed. Please come back later.")
@@ -199,7 +199,7 @@ def sign_up_get(v):
@app.post("/signup")
@limiter.limit("1/second;10/day")
@auth_desired
-def sign_up_post(v):
+def sign_up_post(v:Optional[User]):
if not get_setting('Signups'):
abort(403, "New account registration is currently closed. Please come back later.")
@@ -235,6 +235,7 @@ def sign_up_post(v):
return signup_error("There was a problem. Please try again.")
if not hmac.compare_digest(correct_formkey, form_formkey):
+ if SITE == 'localhost': return signup_error("There was a problem. Please try again!")
return signup_error("There was a problem. Please try again.")
if not request.values.get(
@@ -339,7 +340,7 @@ def sign_up_post(v):
redir = request.values.get("redirect", "").strip().rstrip('?').lower()
if redir:
- if is_site_url(redir) or redir in NO_LOGIN_REDIRECT_URLS:
+ if is_site_url(redir) or redir not in NO_LOGIN_REDIRECT_URLS:
return redirect(redir)
return redirect('/')
@@ -416,7 +417,7 @@ def get_reset():
@app.post("/reset")
@limiter.limit(DEFAULT_RATELIMIT_SLOWER)
@auth_desired
-def post_reset(v):
+def post_reset(v:Optional[User]):
if v: return redirect('/')
user_id = request.values.get("user_id")
timestamp = 0
@@ -454,7 +455,7 @@ def post_reset(v):
@app.get("/lost_2fa")
@auth_desired
-def lost_2fa(v):
+def lost_2fa(v:Optional[User]):
if v and not v.mfa_secret: abort(400, "You don't have 2FA enabled")
return render_template("login/lost_2fa.html", v=v)
diff --git a/files/routes/lottery.py b/files/routes/lottery.py
index 4c7ee2953..707dd83af 100644
--- a/files/routes/lottery.py
+++ b/files/routes/lottery.py
@@ -23,7 +23,7 @@ def lottery_start(v):
@app.post("/lottery/buy")
@limiter.limit("3/second;100/minute;500/hour;1000/day")
@auth_required
-def lottery_buy(v):
+def lottery_buy(v:User):
try: quantity = int(request.values.get("quantity"))
except: abort(400, "Invalid ticket quantity.")
@@ -40,7 +40,7 @@ def lottery_buy(v):
@app.get("/lottery/active")
@limiter.limit("3/second;100/minute;500/hour;1000/day")
@auth_required
-def lottery_active(v):
+def lottery_active(v:User):
lottery, participants = get_active_lottery_stats()
return {"message": "", "stats": {"user": v.lottery_stats, "lottery": lottery, "participants": participants}}
diff --git a/files/routes/mail.py b/files/routes/mail.py
index 6bb5f7bcb..e49bf29a0 100644
--- a/files/routes/mail.py
+++ b/files/routes/mail.py
@@ -19,7 +19,7 @@ def verify_email(v):
@app.get("/activate")
@auth_required
-def activate(v):
+def activate(v:User):
email = request.values.get("email", "").strip().lower()
if not email_regex.fullmatch(email):
diff --git a/files/routes/notifications.py b/files/routes/notifications.py
index 0654abc7d..80be4d52c 100644
--- a/files/routes/notifications.py
+++ b/files/routes/notifications.py
@@ -68,7 +68,7 @@ def notifications_modmail(v):
@app.get("/notifications/messages")
@auth_required
-def notifications_messages(v):
+def notifications_messages(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
@@ -137,7 +137,7 @@ def notifications_messages(v):
@app.get("/notifications/posts")
@auth_required
-def notifications_posts(v):
+def notifications_posts(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
@@ -179,7 +179,7 @@ def notifications_posts(v):
@app.get("/notifications/modactions")
@auth_required
-def notifications_modactions(v):
+def notifications_modactions(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
@@ -218,7 +218,7 @@ def notifications_modactions(v):
@app.get("/notifications/reddit")
@auth_required
-def notifications_reddit(v):
+def notifications_reddit(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
@@ -263,7 +263,7 @@ def notifications_reddit(v):
@app.get("/notifications")
@auth_required
-def notifications(v):
+def notifications(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
diff --git a/files/routes/oauth.py b/files/routes/oauth.py
index 3e3f4e9fc..c095d4531 100644
--- a/files/routes/oauth.py
+++ b/files/routes/oauth.py
@@ -9,7 +9,7 @@ from files.__main__ import app, limiter
@app.get("/authorize")
@auth_required
-def authorize_prompt(v):
+def authorize_prompt(v:User):
client_id = request.values.get("client_id")
application = g.db.query(OauthApp).filter_by(client_id=client_id).one_or_none()
if not application: return {"oauth_error": "Invalid `client_id`"}, 401
diff --git a/files/routes/posts.py b/files/routes/posts.py
index 987eabdad..a70c92483 100644
--- a/files/routes/posts.py
+++ b/files/routes/posts.py
@@ -1,5 +1,6 @@
import os
import time
+import html
from io import BytesIO
from os import path
from shutil import copyfile
@@ -122,7 +123,7 @@ def publish(pid, v):
@app.get("/submit")
@app.get("/h//submit")
@auth_required
-def submit_get(v, sub=None):
+def submit_get(v:User, sub=None):
sub = get_sub_by_name(sub, graceful=True)
if request.path.startswith('/h/') and not sub: abort(404)
@@ -310,8 +311,8 @@ def morecomments(v, cid):
@app.post("/edit_post/")
@limiter.limit("1/second;10/minute;100/hour;200/day")
-@ratelimit_user("1/second;10/minute;100/hour;200/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/minute;100/hour;200/day")
def edit_post(pid, v):
p = get_post(pid)
if v.id != p.author_id and v.admin_level < PERMS['POST_EDITING']:
@@ -392,7 +393,7 @@ def edit_post(pid, v):
p.body = body
- for text in [p.body, p.title, p.url]:
+ for text in {p.body, p.title, p.url}:
if not execute_blackjack(v, p, text, 'submission'): break
if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT:
@@ -603,7 +604,7 @@ def is_repost():
@limiter.limit(POST_RATE_LIMIT)
@limiter.limit(POST_RATE_LIMIT, key_func=lambda:f'{SITE}-{session.get("lo_user")}')
@auth_required
-def submit_post(v, sub=None):
+def submit_post(v:User, sub=None):
url = request.values.get("url", "").strip()
@@ -810,7 +811,7 @@ def submit_post(v, sub=None):
g.db.add(post)
g.db.flush()
- for text in [post.body, post.title, post.url]:
+ for text in {post.body, post.title, post.url}:
if not execute_blackjack(v, post, text, 'submission'): break
if v and v.admin_level >= PERMS['POST_BETS']:
@@ -1072,8 +1073,8 @@ extensions = IMAGE_FORMATS + VIDEO_FORMATS + AUDIO_FORMATS
@app.get("/submit/title")
@limiter.limit("3/minute")
-@ratelimit_user("3/minute")
@auth_required
+@ratelimit_user("3/minute")
def get_post_title(v):
POST_TITLE_TIMEOUT = 5
url = request.values.get("url")
@@ -1098,4 +1099,6 @@ def get_post_title(v):
title = match.group(1)
else: abort(400)
+ title = html.unescape(title)
+
return {"url": url, "title": title}
diff --git a/files/routes/search.py b/files/routes/search.py
index 6b848b525..7515357aa 100644
--- a/files/routes/search.py
+++ b/files/routes/search.py
@@ -45,7 +45,7 @@ def searchparse(text):
@app.get("/search/posts")
@auth_required
-def searchposts(v):
+def searchposts(v:User):
query = request.values.get("q", '').strip()
@@ -181,7 +181,7 @@ def searchposts(v):
@app.get("/search/comments")
@auth_required
-def searchcomments(v):
+def searchcomments(v:User):
query = request.values.get("q", '').strip()
try: page = max(1, int(request.values.get("page", 1)))
@@ -276,7 +276,7 @@ def searchcomments(v):
@app.get("/search/users")
@auth_required
-def searchusers(v):
+def searchusers(v:User):
query = request.values.get("q", '').strip()
diff --git a/files/routes/settings.py b/files/routes/settings.py
index cfe3f02fd..e5bcfed5a 100644
--- a/files/routes/settings.py
+++ b/files/routes/settings.py
@@ -26,12 +26,12 @@ from files.__main__ import app, cache, limiter
@app.get("/settings")
@auth_required
-def settings(v):
+def settings(v:User):
return redirect("/settings/personal")
@app.get("/settings/personal")
@auth_required
-def settings_personal(v):
+def settings_personal(v:User):
return render_template("settings/personal.html", v=v)
@app.delete('/settings/background')
@@ -301,7 +301,7 @@ def settings_personal_post(v):
@app.post("/settings/filters")
@auth_required
-def filters(v):
+def filters(v:User):
filters=request.values.get("filters")[:1000].strip()
if filters == v.custom_filter_list:
@@ -540,7 +540,7 @@ def settings_images_banner(v):
@app.get("/settings/css")
@auth_required
-def settings_css_get(v):
+def settings_css_get(v:User):
return render_template("settings/css.html", v=v)
@app.post("/settings/css")
@@ -572,7 +572,7 @@ def settings_profilecss(v):
@app.get("/settings/security")
@auth_required
-def settings_security(v):
+def settings_security(v:User):
return render_template("settings/security.html",
v=v,
mfa_secret=pyotp.random_base32() if not v.mfa_secret else None,
@@ -581,8 +581,8 @@ def settings_security(v):
@app.post("/settings/block")
@limiter.limit("1/second;20/day")
-@ratelimit_user("1/second;20/day")
@auth_required
+@ratelimit_user("1/second;20/day")
def settings_block_user(v):
user = get_user(request.values.get("username"), graceful=True)
if not user: abort(404, "This user doesn't exist.")
@@ -622,12 +622,12 @@ def settings_unblock_user(v):
@app.get("/settings/apps")
@auth_required
-def settings_apps(v):
+def settings_apps(v:User):
return render_template("settings/apps.html", v=v)
@app.get("/settings/advanced")
@auth_required
-def settings_advanced_get(v):
+def settings_advanced_get(v:User):
return render_template("settings/advanced.html", v=v)
@app.post("/settings/name_change")
@@ -671,8 +671,8 @@ def settings_name_change(v):
@app.post("/settings/song_change_mp3")
@feature_required('USERS_PROFILE_SONG')
@limiter.limit("3/second;10/day")
-@ratelimit_user("3/second;10/day")
@auth_required
+@ratelimit_user("3/second;10/day")
def settings_song_change_mp3(v):
file = request.files['file']
if file.content_type != 'audio/mpeg':
@@ -699,8 +699,8 @@ def settings_song_change_mp3(v):
@app.post("/settings/song_change")
@feature_required('USERS_PROFILE_SONG')
@limiter.limit("3/second;10/day")
-@ratelimit_user("3/second;10/day")
@auth_required
+@ratelimit_user("3/second;10/day")
def settings_song_change(v):
song=request.values.get("song").strip()
diff --git a/files/routes/static.py b/files/routes/static.py
index 3de9c8137..d42c866fc 100644
--- a/files/routes/static.py
+++ b/files/routes/static.py
@@ -25,7 +25,7 @@ def rdrama(id, title):
@app.get("/marseys")
@auth_required
-def marseys(v):
+def marseys(v:User):
marseys = get_marseys(g.db)
authors = get_accounts_dict([m.author_id for m in marseys], graceful=True, include_shadowbanned=False)
original = os.listdir("/asset_submissions/marseys/original")
@@ -61,13 +61,13 @@ def get_emojis(db:scoped_session):
@app.get('/sidebar')
@auth_desired
-def sidebar(v):
+def sidebar(v:Optional[User]):
return render_template('sidebar.html', v=v)
@app.get("/stats")
@auth_required
-def participation_stats(v):
+def participation_stats(v:User):
if v.client: return stats_cached()
return render_template("stats.html", v=v, title="Content Statistics", data=stats_cached())
@@ -81,12 +81,12 @@ def chart():
@app.get("/weekly_chart")
@auth_required
-def weekly_chart(v):
+def weekly_chart(v:User):
return send_file(statshelper.chart_path(kind="weekly", site=SITE))
@app.get("/daily_chart")
@auth_required
-def daily_chart(v):
+def daily_chart(v:User):
return send_file(statshelper.chart_path(kind="daily", site=SITE))
@app.get("/patrons")
@@ -102,18 +102,15 @@ def patrons(v):
@app.get("/admins")
@app.get("/badmins")
@auth_required
-def admins(v):
- if v.admin_level >= PERMS['VIEW_SORTED_ADMIN_LIST']:
- admins = g.db.query(User).filter(User.admin_level>1).order_by(User.truescore.desc()).all()
- admins += g.db.query(User).filter(User.admin_level==1).order_by(User.truescore.desc()).all()
- else: admins = g.db.query(User).filter(User.admin_level>0).order_by(User.truescore.desc()).all()
+def admins(v:User):
+ admins = g.db.query(User).filter(User.admin_level >= PERMS['ADMIN_MOP_VISIBLE']).order_by(User.truescore.desc()).all()
return render_template("admins.html", v=v, admins=admins)
@app.get("/log")
@app.get("/modlog")
@auth_required
-def log(v):
+def log(v:User):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
@@ -176,7 +173,7 @@ def log_item(id, v):
@app.get("/directory")
@auth_required
-def static_megathread_index(v):
+def static_megathread_index(v:User):
return render_template("megathread_index.html", v=v)
@app.get("/api")
@@ -190,13 +187,13 @@ def api(v):
@app.get("/press")
@app.get("/media")
@auth_desired
-def contact(v):
+def contact(v:Optional[User]):
return render_template("contact.html", v=v)
@app.post("/send_admin")
@limiter.limit("1/second;1/2 minutes;10/day")
-@ratelimit_user("1/second;1/2 minutes;10/day")
@auth_required
+@ratelimit_user("1/second;1/2 minutes;10/day")
def submit_contact(v):
body = request.values.get("message")
if not body: abort(400)
@@ -317,7 +314,7 @@ def badge_list(site):
@app.get("/badges")
@feature_required('BADGES')
@auth_required
-def badges(v):
+def badges(v:User):
badges, counts = badge_list(SITE)
return render_template("badges.html", v=v, badges=badges, counts=counts)
@@ -338,7 +335,7 @@ def blocks(v):
@app.get("/banned")
@auth_required
-def banned(v):
+def banned(v:User):
users = g.db.query(User).filter(User.is_banned > 0, User.unban_utc == 0)
if not v.can_see_shadowbanned:
users = users.filter(User.shadowbanned == None)
@@ -347,12 +344,12 @@ def banned(v):
@app.get("/formatting")
@auth_required
-def formatting(v):
+def formatting(v:User):
return render_template("formatting.html", v=v)
@app.get("/app")
@auth_desired
-def mobile_app(v):
+def mobile_app(v:Optional[User]):
return render_template("app.html", v=v)
@app.get("/service-worker.js")
@@ -380,7 +377,7 @@ def transfers_id(id, v):
@app.get("/transfers")
@auth_required
-def transfers(v):
+def transfers(v:User):
comments = g.db.query(Comment).filter(Comment.author_id == AUTOJANNY_ID, Comment.parent_submission == None, Comment.body_html.like("% has transferred %")).order_by(Comment.id.desc())
diff --git a/files/routes/subs.py b/files/routes/subs.py
index 5f4a74e26..0e8a06426 100644
--- a/files/routes/subs.py
+++ b/files/routes/subs.py
@@ -107,7 +107,7 @@ def unexile(v, sub, uid):
@app.post("/h//block")
@auth_required
-def block_sub(v, sub):
+def block_sub(v:User, sub):
sub = get_sub_by_name(sub).name
existing = g.db.query(SubBlock).filter_by(user_id=v.id, sub=sub).one_or_none()
@@ -121,7 +121,7 @@ def block_sub(v, sub):
@app.post("/h//unblock")
@auth_required
-def unblock_sub(v, sub):
+def unblock_sub(v:User, sub):
sub = get_sub_by_name(sub).name
if sub == "chudrama" and not v.can_see_chudrama: abort(403)
block = g.db.query(SubBlock).filter_by(user_id=v.id, sub=sub).one_or_none()
@@ -135,7 +135,7 @@ def unblock_sub(v, sub):
@app.post("/h//subscribe")
@auth_required
-def subscribe_sub(v, sub):
+def subscribe_sub(v:User, sub):
sub = get_sub_by_name(sub).name
existing = g.db.query(SubJoin).filter_by(user_id=v.id, sub=sub).one_or_none()
@@ -148,7 +148,7 @@ def subscribe_sub(v, sub):
@app.post("/h//unsubscribe")
@auth_required
-def unsubscribe_sub(v, sub):
+def unsubscribe_sub(v:User, sub):
sub = get_sub_by_name(sub).name
subscribe = g.db.query(SubJoin).filter_by(user_id=v.id, sub=sub).one_or_none()
@@ -160,7 +160,7 @@ def unsubscribe_sub(v, sub):
@app.post("/h//follow")
@auth_required
-def follow_sub(v, sub):
+def follow_sub(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
existing = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
@@ -173,7 +173,7 @@ def follow_sub(v, sub):
@app.post("/h//unfollow")
@auth_required
-def unfollow_sub(v, sub):
+def unfollow_sub(v:User, sub):
sub = get_sub_by_name(sub)
subscription = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
if subscription:
@@ -184,7 +184,7 @@ def unfollow_sub(v, sub):
@app.get("/h//mods")
@auth_required
-def mods(v, sub):
+def mods(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
users = g.db.query(User, Mod).join(Mod).filter_by(sub=sub.name).order_by(Mod.created_utc).all()
@@ -194,7 +194,7 @@ def mods(v, sub):
@app.get("/h//exilees")
@auth_required
-def sub_exilees(v, sub):
+def sub_exilees(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
users = g.db.query(User, Exile).join(Exile, Exile.user_id==User.id) \
@@ -206,7 +206,7 @@ def sub_exilees(v, sub):
@app.get("/h//blockers")
@auth_required
-def sub_blockers(v, sub):
+def sub_blockers(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
users = g.db.query(User, SubBlock).join(SubBlock) \
@@ -219,7 +219,7 @@ def sub_blockers(v, sub):
@app.get("/h//followers")
@auth_required
-def sub_followers(v, sub):
+def sub_followers(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
users = g.db.query(User, SubSubscription).join(SubSubscription) \
@@ -232,8 +232,8 @@ def sub_followers(v, sub):
@app.post("/h//add_mod")
@limiter.limit("1/second;30/day")
-@ratelimit_user("1/second;30/day")
@is_not_permabanned
+@ratelimit_user("1/second;30/day")
def add_mod(v, sub):
if SITE_NAME == 'WPD': abort(403)
sub = get_sub_by_name(sub).name
@@ -457,8 +457,8 @@ def get_sub_css(sub):
@app.post("/h//banner")
@limiter.limit("1/second;10/day")
-@ratelimit_user("1/second;10/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/day")
def sub_banner(v, sub):
if g.is_tor: abort(403, "Image uploads are not allowed through TOR.")
@@ -490,8 +490,8 @@ def sub_banner(v, sub):
@app.post("/h//sidebar_image")
@limiter.limit("1/second;10/day")
-@ratelimit_user("1/second;10/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/day")
def sub_sidebar(v, sub):
if g.is_tor: abort(403, "Image uploads are not allowed through TOR.")
@@ -522,8 +522,8 @@ def sub_sidebar(v, sub):
@app.post("/h//marsey_image")
@limiter.limit("1/second;10/day")
-@ratelimit_user("1/second;10/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/day")
def sub_marsey(v, sub):
if g.is_tor: abort(403, "Image uploads are not allowed through TOR.")
@@ -554,7 +554,7 @@ def sub_marsey(v, sub):
@app.get("/holes")
@auth_required
-def subs(v):
+def subs(v:User):
subs = g.db.query(Sub, func.count(Submission.sub)).outerjoin(Submission, Sub.name == Submission.sub).group_by(Sub.name).order_by(func.count(Submission.sub).desc()).all()
total_users = g.db.query(User).count()
return render_template('sub/subs.html', v=v, subs=subs, total_users=total_users)
@@ -699,7 +699,7 @@ def mod_unpin(cid, v):
@app.get("/h//log")
@app.get("/h//modlog")
@auth_required
-def hole_log(v, sub):
+def hole_log(v:User, sub):
sub = get_sub_by_name(sub)
if sub.name == "chudrama" and not v.can_see_chudrama: abort(403)
try: page = max(int(request.values.get("page", 1)), 1)
diff --git a/files/routes/users.py b/files/routes/users.py
index 086ee9e25..e8d2a2a41 100644
--- a/files/routes/users.py
+++ b/files/routes/users.py
@@ -56,25 +56,25 @@ def upvoters_downvoters(v, username, uid, cls, vote_cls, vote_dir, template, sta
@app.get("/@/upvoters//posts")
@auth_required
-def upvoters_posts(v, username, uid):
+def upvoters_posts(v:User, username, uid):
return upvoters_downvoters(v, username, uid, Submission, Vote, 1, "userpage/voted_posts.html", None)
@app.get("/@/upvoters//comments")
@auth_required
-def upvoters_comments(v, username, uid):
+def upvoters_comments(v:User, username, uid):
return upvoters_downvoters(v, username, uid, Comment, CommentVote, 1, "userpage/voted_comments.html", True)
@app.get("/@/downvoters//posts")
@auth_required
-def downvoters_posts(v, username, uid):
+def downvoters_posts(v:User, username, uid):
return upvoters_downvoters(v, username, uid, Submission, Vote, -1, "userpage/voted_posts.html", None)
@app.get("/@/downvoters//comments")
@auth_required
-def downvoters_comments(v, username, uid):
+def downvoters_comments(v:User, username, uid):
return upvoters_downvoters(v, username, uid, Comment, CommentVote, -1, "userpage/voted_comments.html", True)
def upvoting_downvoting(v, username, uid, cls, vote_cls, vote_dir, template, standalone):
@@ -107,25 +107,25 @@ def upvoting_downvoting(v, username, uid, cls, vote_cls, vote_dir, template, sta
@app.get("/@/upvoting//posts")
@auth_required
-def upvoting_posts(v, username, uid):
+def upvoting_posts(v:User, username, uid):
return upvoting_downvoting(v, username, uid, Submission, Vote, 1, "userpage/voted_posts.html", None)
@app.get("/@/upvoting//comments")
@auth_required
-def upvoting_comments(v, username, uid):
+def upvoting_comments(v:User, username, uid):
return upvoting_downvoting(v, username, uid, Comment, CommentVote, 1, "userpage/voted_comments.html", True)
@app.get("/@/downvoting//posts")
@auth_required
-def downvoting_posts(v, username, uid):
+def downvoting_posts(v:User, username, uid):
return upvoting_downvoting(v, username, uid, Submission, Vote, -1, "userpage/voted_posts.html", None)
@app.get("/@/downvoting//comments")
@auth_required
-def downvoting_comments(v, username, uid):
+def downvoting_comments(v:User, username, uid):
return upvoting_downvoting(v, username, uid, Comment, CommentVote, -1, "userpage/voted_comments.html", True)
def user_voted(v, username, cls, vote_cls, template, standalone):
@@ -158,19 +158,19 @@ def user_voted(v, username, cls, vote_cls, template, standalone):
@app.get("/@/voted/posts")
@auth_required
-def user_voted_posts(v, username):
+def user_voted_posts(v:User, username):
return user_voted(v, username, Submission, Vote, "userpage/voted_posts.html", None)
@app.get("/@/voted/comments")
@auth_required
-def user_voted_comments(v, username):
+def user_voted_comments(v:User, username):
return user_voted(v, username, Comment, CommentVote, "userpage/voted_comments.html", True)
@app.get("/grassed")
@auth_required
-def grassed(v):
+def grassed(v:User):
users = g.db.query(User).filter(User.ban_reason.like('grass award used by @%'))
if not v.can_see_shadowbanned:
users = users.filter(User.shadowbanned == None)
@@ -179,7 +179,7 @@ def grassed(v):
@app.get("/chuds")
@auth_required
-def chuds(v):
+def chuds(v:User):
users = g.db.query(User).filter(User.agendaposter == 1)
if not v.can_see_shadowbanned:
users = users.filter(User.shadowbanned == None)
@@ -233,32 +233,30 @@ def all_upvoters_downvoters(v, username, vote_dir, is_who_simps_hates):
@app.get("/@/upvoters")
@auth_required
-def upvoters(v, username):
+def upvoters(v:User, username):
return all_upvoters_downvoters(v, username, 1, False)
@app.get("/@/downvoters")
@auth_required
-def downvoters(v, username):
+def downvoters(v:User, username):
return all_upvoters_downvoters(v, username, -1, False)
@app.get("/@/upvoting")
@auth_required
-def upvoting(v, username):
+def upvoting(v:User, username):
return all_upvoters_downvoters(v, username, 1, True)
@app.get("/@/downvoting")
@auth_required
-def downvoting(v, username):
+def downvoting(v:User, username):
return all_upvoters_downvoters(v, username, -1, True)
@app.post("/@/suicide")
@feature_required('USERS_SUICIDE')
@limiter.limit("1/second;5/day")
-@ratelimit_user("1/second;5/day")
@auth_required
+@ratelimit_user("1/second;5/day")
def suicide(v, username):
-
-
user = get_user(username)
suicide = f"Hi there,\n\nA [concerned user](/id/{v.id}) reached out to us about you.\n\nWhen you're in the middle of something painful, it may feel like you don't have a lot of options. But whatever you're going through, you deserve help and there are people who are here for you.\n\nThere are resources available in your area that are free, confidential, and available 24/7:\n\n- Call, Text, or Chat with Canada's [Crisis Services Canada](https://www.crisisservicescanada.ca/en/)\n- Call, Email, or Visit the UK's [Samaritans](https://www.samaritans.org/)\n- Text CHAT to America's [Crisis Text Line](https://www.crisistextline.org/) at 741741.\nIf you don't see a resource in your area above, the moderators keep a comprehensive list of resources and hotlines for people organized by location. Find Someone Now\n\nIf you think you may be depressed or struggling in another way, don't ignore it or brush it aside. Take yourself and your feelings seriously, and reach out to someone.\n\nIt may not feel like it, but you have options. There are people available to listen to you, and ways to move forward.\n\nYour fellow users care about you and there are people who want to help."
if not v.shadowbanned:
@@ -268,7 +266,7 @@ def suicide(v, username):
@app.get("/@/coins")
@auth_required
-def get_coins(v, username):
+def get_coins(v:User, username):
user = get_user(username, v=v, include_shadowbanned=False)
return {"coins": user.coins}
@@ -328,13 +326,13 @@ def transfer_bux(v, username):
@app.get("/leaderboard")
@auth_required
-def leaderboard(v):
+def leaderboard(v:User):
users = g.db.query(User)
if not v.can_see_shadowbanned:
users = users.filter(User.shadowbanned == None)
coins = Leaderboard("Coins", "coins", "coins", "Coins", None, Leaderboard.get_simple_lb, User.coins, v, lambda u:u.coins, g.db, users)
- subscribers = Leaderboard("Followers", "followers", "followers", "Followers", None, Leaderboard.get_simple_lb, User.stored_subscriber_count, v, lambda u:u.stored_subscriber_count, g.db, users)
+ subscribers = Leaderboard("Followers", "followers", "followers", "Followers", "followers", Leaderboard.get_simple_lb, User.stored_subscriber_count, v, lambda u:u.stored_subscriber_count, g.db, users)
posts = Leaderboard("Posts", "post count", "posts", "Posts", "", Leaderboard.get_simple_lb, User.post_count, v, lambda u:u.post_count, g.db, users)
comments = Leaderboard("Comments", "comment count", "comments", "Comments", "comments", Leaderboard.get_simple_lb, User.comment_count, v, lambda u:u.comment_count, g.db, users)
received_awards = Leaderboard("Awards", "received awards", "awards", "Awards", None, Leaderboard.get_simple_lb, User.received_award_count, v, lambda u:u.received_award_count, g.db, users)
@@ -342,14 +340,16 @@ def leaderboard(v):
truescore = Leaderboard("Truescore", "truescore", "truescore", "Truescore", None, Leaderboard.get_simple_lb, User.truescore, v, lambda u:u.truescore, g.db, users)
badges = Leaderboard("Badges", "badges", "badges", "Badges", None, Leaderboard.get_badge_marsey_lb, Badge.user_id, v, None, g.db, None)
- marseys = Leaderboard("Marseys", "Marseys made", "marseys", "Marseys", None, Leaderboard.get_badge_marsey_lb, Marsey.author_id, v, None, g.db, None) if SITE_NAME == 'rDrama' else None
blocks = Leaderboard("Blocked", "most blocked", "blocked", "Blocked By", "blockers", Leaderboard.get_blockers_lb, UserBlock.target_id, v, None, g.db, None)
owned_hats = Leaderboard("Owned hats", "owned hats", "owned-hats", "Owned Hats", None, Leaderboard.get_hat_lb, User.owned_hats, v, None, g.db, None)
- designed_hats = Leaderboard("Designed hats", "designed hats", "designed-hats", "Designed Hats", None, Leaderboard.get_hat_lb, User.designed_hats, v, None, g.db, None)
- leaderboards = [coins, coins_spent, truescore, subscribers, posts, comments, received_awards, badges, marseys, blocks, owned_hats, designed_hats]
+ leaderboards = [coins, coins_spent, truescore, subscribers, posts, comments, received_awards, badges, blocks, owned_hats]
+
+ if SITE == 'rdrama.net':
+ leaderboards.append(Leaderboard("Designed hats", "designed hats", "designed-hats", "Designed Hats", None, Leaderboard.get_hat_lb, User.designed_hats, v, None, g.db, None))
+ leaderboards.append(Leaderboard("Marseys", "Marseys made", "marseys", "Marseys", None, Leaderboard.get_badge_marsey_lb, Marsey.author_id, v, None, g.db, None))
return render_template("leaderboard.html", v=v, leaderboards=leaderboards)
@@ -414,8 +414,8 @@ def unsubscribe(v, post_id):
@app.post("/@/message")
@limiter.limit("1/second;10/minute;20/hour;50/day")
-@ratelimit_user("1/second;10/minute;20/hour;50/day")
@is_not_permabanned
+@ratelimit_user("1/second;10/minute;20/hour;50/day")
def message2(v, username):
user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False)
@@ -479,8 +479,8 @@ def message2(v, username):
@app.post("/reply")
@limiter.limit("1/second;6/minute;50/hour;200/day")
-@ratelimit_user("1/second;6/minute;50/hour;200/day")
@auth_required
+@ratelimit_user("1/second;6/minute;50/hour;200/day")
def messagereply(v):
body = sanitize_raw_body(request.values.get("body"), False)
if not body and not request.files.get("file"): abort(400, "Message is empty!")
@@ -624,25 +624,40 @@ def redditor_moment_redirect(username, v):
@auth_required
def followers(username, v):
u = get_user(username, v=v, include_shadowbanned=False)
- if u.id == CARP_ID and SITE == 'watchpeopledie.tv': abort(403)
if not (v.id == u.id or v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']):
abort(403)
+ try: page = int(request.values.get("page", 1))
+ except: page = 1
+
users = g.db.query(Follow, User).join(Follow, Follow.target_id == u.id) \
.filter(Follow.user_id == User.id) \
- .order_by(Follow.created_utc).all()
- return render_template("userpage/followers.html", v=v, u=u, users=users)
+ .order_by(Follow.created_utc.desc()) \
+ .offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE + 1).all()
+
+ next_exists = (len(users) > PAGE_SIZE)
+ users = users[:PAGE_SIZE]
+
+ return render_template("userpage/followers.html", v=v, u=u, users=users, page=page, next_exists=next_exists)
@app.get("/@/blockers")
@auth_required
def blockers(username, v):
u = get_user(username, v=v, include_shadowbanned=False)
+ try: page = int(request.values.get("page", 1))
+ except: page = 1
+
users = g.db.query(UserBlock, User).join(UserBlock, UserBlock.target_id == u.id) \
.filter(UserBlock.user_id == User.id) \
- .order_by(UserBlock.created_utc).all()
- return render_template("userpage/blockers.html", v=v, u=u, users=users)
+ .order_by(UserBlock.created_utc.desc()) \
+ .offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE + 1).all()
+
+ next_exists = (len(users) > PAGE_SIZE)
+ users = users[:PAGE_SIZE]
+
+ return render_template("userpage/blockers.html", v=v, u=u, users=users, page=page, next_exists=next_exists)
@app.get("/@/following")
@auth_required
@@ -651,18 +666,33 @@ def following(username, v):
if not (v.id == u.id or v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']):
abort(403)
+ try: page = int(request.values.get("page", 1))
+ except: page = 1
+
users = g.db.query(User).join(Follow, Follow.user_id == u.id) \
.filter(Follow.target_id == User.id) \
- .order_by(Follow.created_utc).all()
- return render_template("userpage/following.html", v=v, u=u, users=users)
+ .order_by(Follow.created_utc.desc()) \
+ .offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE + 1).all()
-@app.get("/views")
+ next_exists = (len(users) > PAGE_SIZE)
+ users = users[:PAGE_SIZE]
+
+ return render_template("userpage/following.html", v=v, u=u, users=users, page=page, next_exists=next_exists)
+
+@app.get("/@/views")
@auth_required
-def visitors(v):
- if not v.viewers_recorded:
- return render_template("errors/patron.html", v=v)
- viewers=sorted(v.viewers, key = lambda x: x.last_view_utc, reverse=True)
- return render_template("userpage/viewers.html", v=v, viewers=viewers)
+def visitors(username, v:User):
+ u = get_user(username, v=v, include_shadowbanned=False)
+
+ try: page = int(request.values.get("page", 1))
+ except: page = 1
+
+ views = g.db.query(ViewerRelationship).filter_by(user_id=u.id).order_by(ViewerRelationship.last_view_utc.desc()).offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE + 1).all()
+
+ next_exists = (len(views) > PAGE_SIZE)
+ views = views[:PAGE_SIZE]
+
+ return render_template("userpage/views.html", v=v, u=u, views=views, next_exists=next_exists, page=page)
@cache.memoize(timeout=86400)
def userpagelisting(user:User, site=None, v=None, page:int=1, sort="new", t="all"):
@@ -684,7 +714,7 @@ def u_username(username, v=None):
return redirect(SITE_FULL + request.full_path.replace(username, u.username))
is_following = v and u.has_follower(v)
- if v and v.id not in (u.id, DAD_ID) and u.viewers_recorded:
+ if v and v.id != u.id:
g.db.flush()
view = g.db.query(ViewerRelationship).filter_by(viewer_id=v.id, user_id=u.id).one_or_none()
@@ -935,7 +965,7 @@ def user_profile_name(username):
return redirect(x.profile_url)
def get_saves_and_subscribes(v, template, relationship_cls, page:int, standalone=False):
- if relationship_cls in [SaveRelationship, Subscription]:
+ if relationship_cls in {SaveRelationship, Subscription}:
query = relationship_cls.submission_id
join = relationship_cls.post
cls = Submission
@@ -965,7 +995,7 @@ def get_saves_and_subscribes(v, template, relationship_cls, page:int, standalone
@app.get("/@/saved/posts")
@auth_required
-def saved_posts(v, username):
+def saved_posts(v:User, username):
try: page = max(1, int(request.values.get("page", 1)))
except: abort(400, "Invalid page input!")
@@ -973,7 +1003,7 @@ def saved_posts(v, username):
@app.get("/@/saved/comments")
@auth_required
-def saved_comments(v, username):
+def saved_comments(v:User, username):
try: page = max(1, int(request.values.get("page", 1)))
except: abort(400, "Invalid page input!")
@@ -981,7 +1011,7 @@ def saved_comments(v, username):
@app.get("/@/subscribed/posts")
@auth_required
-def subscribed_posts(v, username):
+def subscribed_posts(v:User, username):
try: page = max(1, int(request.values.get("page", 1)))
except: abort(400, "Invalid page input!")
@@ -989,7 +1019,7 @@ def subscribed_posts(v, username):
@app.post("/fp/")
@auth_required
-def fp(v, fp):
+def fp(v:User, fp):
v.fp = fp
users = g.db.query(User).filter(User.fp == fp, User.id != v.id).all()
if users: print(f'{v.username}: fp', flush=True)
@@ -1035,7 +1065,7 @@ def toggle_holes():
@app.get("/badge_owners/")
@auth_required
-def bid_list(v, bid):
+def bid_list(v:User, bid):
try: bid = int(bid)
except: abort(400)
@@ -1097,7 +1127,7 @@ kofi_tiers={
@app.post("/settings/kofi")
@limiter.limit(DEFAULT_RATELIMIT_SLOWER)
@auth_required
-def settings_kofi(v):
+def settings_kofi(v:User):
if not KOFI_TOKEN or KOFI_TOKEN == DEFAULT_CONFIG_VALUE: abort(404)
if not (v.email and v.is_activated):
abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!")
diff --git a/files/routes/votes.py b/files/routes/votes.py
index 5d6d21e4e..ad6e5a282 100644
--- a/files/routes/votes.py
+++ b/files/routes/votes.py
@@ -42,7 +42,7 @@ def vote_info_get(v, link):
def vote_post_comment(target_id, new, v, cls, vote_cls):
if new == "-1" and DISABLE_DOWNVOTES: abort(403)
- if new not in ["-1", "0", "1"]: abort(400)
+ if new not in {"-1", "0", "1"}: abort(400)
if v.client and v.id not in PRIVILEGED_USER_BOTS: abort(403)
new = int(new)
target = None
diff --git a/files/routes/wrappers.py b/files/routes/wrappers.py
index 1341a7bcb..8fc202c00 100644
--- a/files/routes/wrappers.py
+++ b/files/routes/wrappers.py
@@ -21,7 +21,7 @@ def calc_users(v):
if g.is_api_or_xhr:
g.loggedin_counter = 0
g.loggedout_counter = 0
- return
+ return ''
loggedin = cache.get(f'{SITE}_loggedin') or {}
loggedout = cache.get(f'{SITE}_loggedout') or {}
timestamp = int(time.time())
diff --git a/files/templates/admin/app.html b/files/templates/admin/app.html
index 7a738ebbf..a8ea8b027 100644
--- a/files/templates/admin/app.html
+++ b/files/templates/admin/app.html
@@ -43,7 +43,7 @@
-
-
+
Posts
diff --git a/files/templates/admin/apps.html b/files/templates/admin/apps.html
index a6ded0fee..c04a1a0e5 100644
--- a/files/templates/admin/apps.html
+++ b/files/templates/admin/apps.html
@@ -8,7 +8,7 @@
diff --git a/files/templates/comments.html b/files/templates/comments.html
index bf3ad23e0..2f10f816b 100644
--- a/files/templates/comments.html
+++ b/files/templates/comments.html
@@ -438,6 +438,10 @@
{% endif %}
@@ -651,7 +652,7 @@
{% if c.author_id == v.id or (c.post.sub and v.mods(c.post.sub)) %}
-
+
{% endif %}
{% if v.admin_level < PERMS['POST_COMMENT_MODERATION'] %}
@@ -696,6 +697,10 @@
diff --git a/files/templates/donate_WPD.html b/files/templates/donate_WPD.html
index 219507850..09a9e04e9 100644
--- a/files/templates/donate_WPD.html
+++ b/files/templates/donate_WPD.html
@@ -5,7 +5,7 @@
- {% if v and v.truescore >= TRUESCORE_DONATE_LIMIT %}
+ {% if v and v.truescore >= TRUESCORE_DONATE_MINIMUM %}
Kofi |
{{KOFI_LINK}} |
diff --git a/files/templates/donate_rDrama.html b/files/templates/donate_rDrama.html
index 16c159a31..37e9c7e95 100644
--- a/files/templates/donate_rDrama.html
+++ b/files/templates/donate_rDrama.html
@@ -5,7 +5,7 @@
- {% if v and v.truescore >= TRUESCORE_DONATE_LIMIT %}
+ {% if v and v.truescore >= TRUESCORE_DONATE_MINIMUM %}
Gumroad |
{{GUMROAD_LINK}} |
diff --git a/files/templates/errors/error.html b/files/templates/errors/error.html
index d4f92a9dc..1e665add1 100644
--- a/files/templates/errors/error.html
+++ b/files/templates/errors/error.html
@@ -11,7 +11,7 @@
{{code}} {{title}}
{{msg|safe}}
{% if details -%}
- {{details|safe}}
+ {{details|safe}}
{%- endif %}
diff --git a/files/templates/errors/nsfw.html b/files/templates/errors/nsfw.html
index b14275e7d..9329fda68 100644
--- a/files/templates/errors/nsfw.html
+++ b/files/templates/errors/nsfw.html
@@ -4,10 +4,10 @@
{% block content %}
-
+
Are you over 18?
-
This post is rated +18 (Adult-Only). You must be 18 or older to continue. Are you sure you want to proceed?
+
This post is rated +18 (Adult-Only). You must be 18 or older to continue. Are you sure you want to proceed?