forked from rDrama/rDrama
1
0
Fork 0

Compare commits

...

50 Commits

Author SHA1 Message Date
TriHard 485ce8bc77 Fix sqlalchemy.exc.ArgumentError 2024-02-16 15:20:21 -07:00
TriHard ebe5f52b01 Use type aliases for common columns 2024-02-16 15:20:21 -07:00
TriHard 84f3025b1f Apply Optional to nullable columns 2024-02-16 15:20:21 -07:00
TriHard 74e025a6ed Add Mapped types to relationships 2024-02-16 15:20:21 -07:00
TriHard 2a5b2d4e03 Column -> mapped_column 2024-02-16 15:20:21 -07:00
TriHard 5902575ade declarative_base -> DeclarativeBase 2024-02-16 15:20:21 -07:00
TriHard 863088f9e8 Add type stub for Flask globals 2024-02-16 15:20:21 -07:00
Aevann 44eaad5e43 fix 500 error 2024-02-16 23:59:29 +02:00
Aevann 95ec09a6b3 make wpd report queue usable 2024-02-16 23:58:27 +02:00
Aevann aa67af527d its forced 8 now 2024-02-16 23:27:56 +02:00
Aevann e200a011c6 fix this https://rdrama.net/h/changelog/post/165657/changelog-megathread-marseynotes/5958269#context 2024-02-16 23:13:42 +02:00
Aevann 9009fa7d92 fix notifs 2024-02-16 23:05:06 +02:00
Aevann 0f9ed086ca move USER_BADGES to JL1 2024-02-16 23:03:34 +02:00
Aevann 31e263fe9d fix 500 error on devrama 2024-02-16 22:56:49 +02:00
Aevann 26b9feea2c fix cron error 2024-02-16 22:55:26 +02:00
Aevann 7624413b33 fix 500 error 2024-02-16 22:53:40 +02:00
Aevann 8707129a2e if not IS_LOCALHOST: 2024-02-16 22:50:32 +02:00
Aevann df2b5856fe add lemmy mention notifs 2024-02-16 22:42:42 +02:00
Aevann 61afbb5bf6 support .one 2024-02-16 21:34:21 +02:00
Aevann 1aae6f1cfb pin joan posts 2024-02-16 20:08:02 +02:00
Aevann d5621428db fix bio 2024-02-16 19:06:55 +02:00
Aevann eb39afc999 allow jannies to change author 2024-02-16 19:02:43 +02:00
Aevann 4201224595 remove useless name="" 2024-02-16 19:00:40 +02:00
Aevann a767ec2767 allow ??? in author field 2024-02-16 18:52:18 +02:00
Aevann e755205da8 fix this https://rdrama.net/h/singapore/post/246430/budget-2024-all-sporean-households-to/5955314#context 2024-02-16 17:37:34 +02:00
Aevann 667bdcc0bf allow "" - or 2024-02-16 17:20:17 +02:00
Aevann 638f4350be nsfw logic consistency 2024-02-16 16:50:03 +02:00
Aevann 01e6942e5c remove unnecessary exact parameter (quotation marks work) 2024-02-16 16:41:11 +02:00
SneedBot 19bc67ead7 sneed 2024-02-16 14:31:47 +00:00
Aevann c4351d97c0 capitalization consistency 2024-02-16 16:31:34 +02:00
SneedBot f6e0f81f2c sneed 2024-02-16 14:29:52 +00:00
Aevann f8614d79cd make top poster of the day get a badge 2024-02-16 16:27:28 +02:00
SneedBot 7ceaf62183 sneed 2024-02-16 14:22:13 +00:00
Aevann 6976619282 remove line 2024-02-16 16:21:17 +02:00
SneedBot 0ae67b83ab sneed 2024-02-16 14:16:23 +00:00
Aevann c8c4652300 do this https://rdrama.net/h/changelog/post/165657/changelog-megathread-marseynotes/5944237#context 2024-02-16 15:56:48 +02:00
Aevann 32ca7d1a69 add clearer error messages 2024-02-16 15:23:48 +02:00
Aevann c3d0b2c60a consistent capitalization 2024-02-16 15:08:05 +02:00
Aevann dc979659e9 fix this https://rdrama.net/h/changelog/post/165657/changelog-megathread-marseynotes/5954720#context 2024-02-16 15:06:49 +02:00
Aevann 91d7d16d9f hide ricardo for loggedout 2024-02-16 14:50:44 +02:00
Aevann 892137f23b do this https://rdrama.net/h/countryclub/post/79285/-/4426657#context 2024-02-16 14:49:26 +02:00
Aevann 00a2497118 shorter if conditions 2024-02-16 14:36:45 +02:00
Aevann 1e289e8941 pin autodrama and use PIZZASHILL_ID var 2024-02-16 14:22:11 +02:00
Aevann aa28bbb6f2 add more filters 2024-02-16 14:18:06 +02:00
Aevann ae0ed85301 fix 500 error 2024-02-16 14:12:37 +02:00
Aevann e6c2950666 rename distinguish_level to distinguished 2024-02-16 14:11:13 +02:00
SneedBot 5945ecc9b6 sneed 2024-02-16 12:00:15 +00:00
Aevann 821c606b8d move mods and exiles to hole_relationships.py 2024-02-16 13:44:38 +02:00
Aevann c2918d67f0 consistency 2024-02-16 13:44:37 +02:00
SneedBot 826ad536a1 sneed 2024-02-16 10:00:14 +00:00
87 changed files with 1129 additions and 865 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 KiB

View File

@ -5,6 +5,7 @@ function approve_emoji(t, name) {
"tags": document.getElementById(`${name}-tags`).value,
"name": document.getElementById(`${name}-name`).value,
"kind": document.getElementById(`${name}-kind`).value,
"author": document.getElementById(`${name}-author`).value,
"nsfw": document.getElementById(`${name}-nsfw`).checked,
},
() => {

View File

@ -1,6 +1,7 @@
# load sqlalchemy's declarative base...
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
# then load all of our classes :)
from .alts import *

View File

@ -1,17 +1,19 @@
import time
from typing import Optional
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import user_id_fk_pk
class Alt(Base):
__tablename__ = "alts"
user1 = Column(Integer, ForeignKey("users.id"), primary_key=True)
user2 = Column(Integer, ForeignKey("users.id"), primary_key=True)
is_manual = Column(Boolean, default=False)
created_utc = Column(Integer)
user1: Mapped[user_id_fk_pk]
user2: Mapped[user_id_fk_pk]
is_manual: Mapped[bool] = mapped_column(default=False)
created_utc: Mapped[Optional[int]]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,30 +1,34 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.config.awards import AWARDS, HOUSE_AWARDS
from files.helpers.lazy import lazy
from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk
if TYPE_CHECKING:
from files.classes import Comment, Post, User
class AwardRelationship(Base):
__tablename__ = "award_relationships"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
post_id = Column(Integer, ForeignKey("posts.id"))
comment_id = Column(Integer, ForeignKey("comments.id"))
kind = Column(String)
awarded_utc = Column(Integer)
created_utc = Column(Integer)
price_paid = Column(Integer, default = 0)
note = Column(String)
id: Mapped[int_pk]
user_id: Mapped[user_id_fk]
post_id: Mapped[Optional[post_id_fk]]
comment_id: Mapped[Optional[comment_id_fk]]
kind: Mapped[str]
awarded_utc: Mapped[Optional[int]]
created_utc: Mapped[Optional[int]]
price_paid: Mapped[int] = mapped_column(default = 0)
note: Mapped[Optional[str]]
user = relationship("User", primaryjoin="AwardRelationship.user_id==User.id", back_populates="awards")
post = relationship("Post", primaryjoin="AwardRelationship.post_id==Post.id", back_populates="awards")
comment = relationship("Comment", primaryjoin="AwardRelationship.comment_id==Comment.id", back_populates="awards")
user: Mapped["User"] = relationship(primaryjoin="AwardRelationship.user_id==User.id", back_populates="awards")
post: Mapped["Post"] = relationship(primaryjoin="AwardRelationship.post_id==Post.id", back_populates="awards")
comment: Mapped["Comment"] = relationship(primaryjoin="AwardRelationship.comment_id==Comment.id", back_populates="awards")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,20 +1,26 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.config.const import *
from files.helpers.lazy import lazy
from files.helpers.types import int_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class BadgeDef(Base):
__tablename__ = "badge_defs"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
description = Column(String)
created_utc = Column(Integer)
id: Mapped[int_pk] = mapped_column(autoincrement=True)
name: Mapped[str]
description: Mapped[Optional[str]]
created_utc: Mapped[int]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -32,14 +38,14 @@ class Badge(Base):
__tablename__ = "badges"
user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
badge_id = Column(Integer, ForeignKey('badge_defs.id'), primary_key=True)
description = Column(String)
url = Column(String)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
badge_id: Mapped[int] = mapped_column(ForeignKey('badge_defs.id'), primary_key=True)
description: Mapped[Optional[str]]
url: Mapped[Optional[str]]
created_utc: Mapped[int]
user = relationship("User", back_populates="badges")
badge = relationship("BadgeDef", primaryjoin="Badge.badge_id == BadgeDef.id", lazy="joined", innerjoin=True)
user: Mapped["User"] = relationship(back_populates="badges")
badge: Mapped["BadgeDef"] = relationship(primaryjoin="Badge.badge_id == BadgeDef.id", lazy="joined", innerjoin=True)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:

View File

@ -1,29 +1,33 @@
import json
import time
from typing import TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.types import int_pk, user_id_fk
if TYPE_CHECKING:
from files.classes import User
CASINO_GAME_KINDS = ['blackjack', 'slots', 'roulette']
class CasinoGame(Base):
__tablename__ = "casino_games"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
active = Column(Boolean, default=True)
currency = Column(String)
wager = Column(Integer)
winnings = Column(Integer)
kind = Column(String)
game_state = Column(JSON)
id: Mapped[int_pk]
user_id: Mapped[user_id_fk]
created_utc: Mapped[int]
active: Mapped[bool] = mapped_column(default=True)
currency: Mapped[str]
wager: Mapped[int]
winnings: Mapped[int]
kind: Mapped[str]
game_state: Mapped[str] = mapped_column(JSON)
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:

View File

@ -1,29 +1,35 @@
import time
from typing import Annotated, Optional, TYPE_CHECKING
from flask import g
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship, load_only
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, load_only, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.config.const import *
from files.helpers.lazy import lazy
from files.helpers.types import int_pk, user_id_fk, user_id_fk_pk
from .comment import Comment
from .post import Post
if TYPE_CHECKING:
from files.classes import User
class OauthApp(Base):
__tablename__ = "oauth_apps"
id = Column(Integer, primary_key=True)
client_id = Column(String)
app_name = Column(String)
redirect_uri = Column(String)
description = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
id: Mapped[int_pk]
client_id: Mapped[Optional[Annotated[str, 64]]]
app_name: Mapped[str]
redirect_uri: Mapped[str]
description: Mapped[str]
author_id: Mapped[user_id_fk]
created_utc: Mapped[Optional[int]]
author = relationship("User", back_populates="apps")
author: Mapped["User"] = relationship(back_populates="apps")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -51,13 +57,13 @@ class OauthApp(Base):
class ClientAuth(Base):
__tablename__ = "client_auths"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
oauth_client = Column(Integer, ForeignKey("oauth_apps.id"), primary_key=True)
access_token = Column(String)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
oauth_client: Mapped[int] = mapped_column(ForeignKey("oauth_apps.id"), primary_key=True)
access_token: Mapped[str]
created_utc: Mapped[Optional[int]]
user = relationship("User")
application = relationship("OauthApp")
user: Mapped["User"] = relationship()
application: Mapped["OauthApp"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,12 +1,13 @@
import time
from math import floor
from random import randint
from typing import Optional, TYPE_CHECKING
from urllib.parse import parse_qs, urlencode, urlparse
from flask import g
from sqlalchemy import Column, ForeignKey
from sqlalchemy import ForeignKey
from sqlalchemy.dialects.postgresql import TSVECTOR
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.schema import FetchedValue
from sqlalchemy.sql.sqltypes import *
@ -17,10 +18,14 @@ from files.helpers.slurs_and_profanities import *
from files.helpers.lazy import lazy
from files.helpers.regex import *
from files.helpers.sorting_and_time import *
from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk
from files.helpers.bleach_body import *
from .saves import CommentSaveRelationship
if TYPE_CHECKING:
from files.classes import AwardRelationship, CasinoGame, CommentOption, CommentReport, OauthApp, Post, User
def get_emoji_awards_emojis(obj, v, kind, NSFW_EMOJIS):
if g.show_nsfw:
emojis = [x.note for x in obj.awards if x.kind == kind]
@ -172,60 +177,60 @@ def add_options(self, body, v):
class Comment(Base):
__tablename__ = "comments"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"))
parent_post = Column(Integer, ForeignKey("posts.id"))
wall_user_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
edited_utc = Column(Integer, default=0)
is_banned = Column(Boolean, default=False)
ghost = Column(Boolean, default=False)
bannedfor = Column(String)
chuddedfor = Column(String)
distinguish_level = Column(Integer, default=0)
deleted_utc = Column(Integer, default=0)
is_approved = Column(Integer, ForeignKey("users.id"))
level = Column(Integer, default=1)
parent_comment_id = Column(Integer, ForeignKey("comments.id"))
top_comment_id = Column(Integer)
is_bot = Column(Boolean, default=False)
stickied = Column(String)
stickied_utc = Column(Integer)
num_of_pinned_children = Column(Integer, default=0)
sentto = Column(Integer, ForeignKey("users.id"))
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
upvotes = Column(Integer, default=1)
downvotes = Column(Integer, default=0)
realupvotes = Column(Integer, default=1)
body = Column(String)
body_html = Column(String)
body_ts = Column(TSVECTOR(), server_default=FetchedValue())
ban_reason = Column(String)
treasure_amount = Column(String)
slots_result = Column(String)
ping_cost = Column(Integer, default=0)
blackjack_result = Column(String)
casino_game_id = Column(Integer, ForeignKey("casino_games.id"))
chudded = Column(Boolean, default=False)
rainbowed = Column(Boolean, default=False)
queened = Column(Boolean, default=False)
sharpened = Column(Boolean, default=False)
id: Mapped[int_pk]
author_id: Mapped[user_id_fk]
parent_post: Mapped[Optional[post_id_fk]]
wall_user_id: Mapped[Optional[user_id_fk]]
created_utc: Mapped[int]
edited_utc: Mapped[int] = mapped_column(default=0)
is_banned: Mapped[bool] = mapped_column(default=False)
ghost: Mapped[bool] = mapped_column(default=False)
bannedfor: Mapped[Optional[str]]
chuddedfor: Mapped[Optional[str]]
distinguished: Mapped[bool] = mapped_column(default=False)
deleted_utc: Mapped[int] = mapped_column(default=0)
is_approved: Mapped[Optional[user_id_fk]]
level: Mapped[int] = mapped_column(default=1)
parent_comment_id: Mapped[Optional[comment_id_fk]]
top_comment_id: Mapped[Optional[int]]
is_bot: Mapped[bool] = mapped_column(default=False)
stickied: Mapped[Optional[str]]
stickied_utc: Mapped[Optional[int]]
num_of_pinned_children: Mapped[int] = mapped_column(default=0)
sentto: Mapped[Optional[user_id_fk]]
app_id: Mapped[Optional[int]] = mapped_column(ForeignKey("oauth_apps.id"))
upvotes: Mapped[int] = mapped_column(default=1)
downvotes: Mapped[int] = mapped_column(default=0)
realupvotes: Mapped[int] = mapped_column(default=1)
body: Mapped[Optional[str]]
body_html: Mapped[Optional[str]]
body_ts: Mapped[str] = mapped_column(TSVECTOR(), server_default=FetchedValue())
ban_reason: Mapped[Optional[str]]
treasure_amount: Mapped[Optional[str]]
slots_result: Mapped[Optional[str]]
ping_cost: Mapped[int] = mapped_column(default=0)
blackjack_result: Mapped[Optional[str]]
casino_game_id: Mapped[Optional[int]] = mapped_column(ForeignKey("casino_games.id"))
chudded: Mapped[bool] = mapped_column(default=False)
rainbowed: Mapped[bool] = mapped_column(default=False)
queened: Mapped[bool] = mapped_column(default=False)
sharpened: Mapped[bool] = mapped_column(default=False)
if FEATURES['NSFW_MARKING']:
nsfw = Column(Boolean, default=False)
nsfw: Mapped[bool] = mapped_column(default=False)
else:
nsfw = False
oauth_app = relationship("OauthApp")
post = relationship("Post", back_populates="comments")
author = relationship("User", primaryjoin="User.id==Comment.author_id")
senttouser = relationship("User", primaryjoin="User.id==Comment.sentto")
parent_comment = relationship("Comment", remote_side=[id])
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment")
reports = relationship("CommentReport", order_by="CommentReport.created_utc")
options = relationship("CommentOption", order_by="CommentOption.id")
casino_game = relationship("CasinoGame")
wall_user = relationship("User", primaryjoin="User.id==Comment.wall_user_id")
oauth_app: Mapped["OauthApp"] = relationship()
post: Mapped["Post"] = relationship(back_populates="comments")
author: Mapped["User"] = relationship(primaryjoin="User.id==Comment.author_id")
senttouser: Mapped["User"] = relationship(primaryjoin="User.id==Comment.sentto")
parent_comment: Mapped["Comment"] = relationship(remote_side="Comment.id")
awards: Mapped[list["AwardRelationship"]] = relationship(order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment")
reports: Mapped[list["CommentReport"]] = relationship(order_by="CommentReport.created_utc")
options: Mapped[list["CommentOption"]] = relationship(order_by="CommentOption.id")
casino_game: Mapped["CasinoGame"] = relationship()
wall_user: Mapped["User"] = relationship(primaryjoin="User.id==Comment.wall_user_id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:
@ -383,7 +388,7 @@ class Comment(Base):
'is_nsfw': self.nsfw,
'permalink': f'/comment/{self.id}#context',
'stickied': self.stickied,
'distinguish_level': self.distinguish_level,
'distinguished': self.distinguished,
'post_id': self.post.id if self.post else 0,
'score': self.score,
'upvotes': self.upvotes,

View File

@ -1,15 +1,17 @@
import time
from typing import Optional
from sqlalchemy import Column
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk
class BannedDomain(Base):
__tablename__ = "banneddomains"
domain = Column(String, primary_key=True)
reason = Column(String)
created_utc = Column(Integer)
domain: Mapped[str_pk]
reason: Mapped[str]
created_utc: Mapped[Optional[int]]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,21 +1,23 @@
import time
from typing import Optional
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk, user_id_fk
class Emoji(Base):
__tablename__ = "emojis"
name = Column(String, primary_key=True)
kind = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
tags = Column(String)
count = Column(Integer, default=0)
submitter_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
nsfw = Column(Boolean, default=False)
name: Mapped[str_pk]
kind: Mapped[str]
author_id: Mapped[user_id_fk]
tags: Mapped[str]
count: Mapped[int] = mapped_column(default=0)
submitter_id: Mapped[Optional[user_id_fk]]
created_utc: Mapped[int]
nsfw: Mapped[bool] = mapped_column(default=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,23 +0,0 @@
import time
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
class Exile(Base):
__tablename__ = "exiles"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
hole = Column(String, ForeignKey("holes.name"), primary_key=True)
exiler_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
exiler = relationship("User", primaryjoin="User.id==Exile.exiler_id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<{self.__class__.__name__}(user_id={self.user_id}, hole={self.hole})>"

View File

@ -1,19 +1,24 @@
import time
from typing import TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class Follow(Base):
__tablename__ = "follows"
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
target_id: Mapped[user_id_fk_pk]
user_id: Mapped[user_id_fk_pk]
created_utc: Mapped[int]
user = relationship("User", uselist=False, primaryjoin="User.id==Follow.user_id", back_populates="following")
target = relationship("User", uselist=False, primaryjoin="User.id==Follow.target_id", back_populates="followers")
user: Mapped["User"] = relationship(uselist=False, primaryjoin="User.id==Follow.user_id", back_populates="following")
target: Mapped["User"] = relationship(uselist=False, primaryjoin="User.id==Follow.target_id", back_populates="followers")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,25 +1,29 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column
from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer
from sqlalchemy.orm import Mapped, relationship
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.config.const import *
from files.helpers.types import str_pk, user_id_fk
from .group_membership import *
if TYPE_CHECKING:
from files.classes import User
class Group(Base):
__tablename__ = "groups"
name = Column(String, primary_key=True)
created_utc = Column(Integer)
owner_id = Column(Integer, ForeignKey("users.id"))
description = Column(String)
description_html = Column(String)
name: Mapped[str_pk]
created_utc: Mapped[int]
owner_id: Mapped[Optional[user_id_fk]]
description: Mapped[Optional[str]]
description_html: Mapped[Optional[str]]
memberships = relationship("GroupMembership", primaryjoin="GroupMembership.group_name==Group.name", order_by="GroupMembership.approved_utc")
owner = relationship("User", primaryjoin="Group.owner_id==User.id")
memberships: Mapped[list["GroupMembership"]] = relationship(primaryjoin="GroupMembership.group_name==Group.name", order_by="GroupMembership.approved_utc")
owner: Mapped["User"] = relationship(primaryjoin="Group.owner_id==User.id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,20 +1,25 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer, String, Boolean
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes import Base
from files.helpers.types import user_id_fk_pk
if TYPE_CHECKING:
from files.classes.user import User
class GroupMembership(Base):
__tablename__ = "group_memberships"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
group_name = Column(String, ForeignKey("groups.name"), primary_key=True)
created_utc = Column(Integer)
approved_utc = Column(Integer)
is_mod = Column(Boolean, default=False)
user_id: Mapped[user_id_fk_pk]
group_name: Mapped[str] = mapped_column(ForeignKey("groups.name"), primary_key=True)
created_utc: Mapped[int]
approved_utc: Mapped[Optional[int]]
is_mod: Mapped[bool] = mapped_column(default=False)
user = relationship("User", uselist=False)
user: Mapped["User"] = relationship(uselist=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,27 +1,33 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from flask import g
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.slurs_and_profanities import censor_slurs_profanities
from files.helpers.types import int_pk, user_id_fk
if TYPE_CHECKING:
from files.classes import User
class HatDef(Base):
__tablename__ = "hat_defs"
id = Column(Integer, primary_key=True)
name = Column(String)
description = Column(String)
author_id = Column(Integer, ForeignKey('users.id'))
price = Column(Integer)
submitter_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
id: Mapped[int_pk]
name: Mapped[str]
description: Mapped[str]
author_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
price: Mapped[int]
submitter_id: Mapped[Optional[user_id_fk]]
created_utc: Mapped[int]
author = relationship("User", primaryjoin="HatDef.author_id == User.id", back_populates="designed_hats")
submitter = relationship("User", primaryjoin="HatDef.submitter_id == User.id")
author: Mapped["User"] = relationship(primaryjoin="HatDef.author_id == User.id", back_populates="designed_hats")
submitter: Mapped["User"] = relationship(primaryjoin="HatDef.submitter_id == User.id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -48,13 +54,13 @@ class HatDef(Base):
class Hat(Base):
__tablename__ = "hats"
user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
hat_id = Column(Integer, ForeignKey('hat_defs.id'), primary_key=True)
equipped = Column(Boolean, default=False)
created_utc = Column(Integer)
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True)
hat_id: Mapped[int] = mapped_column(ForeignKey('hat_defs.id'), primary_key=True)
equipped: Mapped[Optional[bool]] = mapped_column(default=False)
created_utc: Mapped[Optional[int]]
hat_def = relationship("HatDef")
owners = relationship("User", back_populates="owned_hats")
hat_def: Mapped["HatDef"] = relationship()
owners: Mapped[list["User"]] = relationship(back_populates="owned_hats")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,10 +1,9 @@
import random
import time
from typing import Annotated, Optional
from sqlalchemy import Column
from sqlalchemy.ext.mutable import MutableList
from sqlalchemy.orm import relationship, deferred
from sqlalchemy.types import VARCHAR, Boolean, Integer
from sqlalchemy.orm import DynamicMapped, Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import ARRAY
from files.classes import Base
@ -15,24 +14,24 @@ from .hole_relationship import *
class Hole(Base):
__tablename__ = "holes"
name = Column(VARCHAR(HOLE_NAME_COLUMN_LENGTH), primary_key=True)
sidebar = Column(VARCHAR(HOLE_SIDEBAR_COLUMN_LENGTH))
sidebar_html = Column(VARCHAR(HOLE_SIDEBAR_HTML_COLUMN_LENGTH))
sidebarurls = Column(MutableList.as_mutable(ARRAY(VARCHAR(HOLE_BANNER_URL_COLUMN_LENGTH))), default=MutableList([]))
bannerurls = Column(MutableList.as_mutable(ARRAY(VARCHAR(HOLE_BANNER_URL_COLUMN_LENGTH))), default=MutableList([]))
marseyurl = Column(VARCHAR(HOLE_MARSEY_URL_LENGTH))
css = deferred(Column(VARCHAR(CSS_LENGTH_LIMIT)))
stealth = Column(Boolean, default=False)
public_use = Column(Boolean, default=False)
created_utc = Column(Integer)
name: Mapped[str] = mapped_column(primary_key=True)
sidebar: Mapped[Optional[str]]
sidebar_html: Mapped[Optional[str]]
sidebarurls: Mapped[list[str]] = mapped_column(MutableList.as_mutable(ARRAY(String)), default=MutableList([]))
bannerurls: Mapped[list[str]] = mapped_column(MutableList.as_mutable(ARRAY(String)), default=MutableList([]))
marseyurl: Mapped[Optional[str]]
css: Mapped[Optional[str]] = mapped_column(deferred=True)
stealth: Mapped[Optional[bool]] = mapped_column(default=False)
public_use: Mapped[bool] = mapped_column(default=False)
created_utc: Mapped[Optional[int]]
if SITE_NAME == 'WPD':
snappy_quotes = None
else:
snappy_quotes = deferred(Column(VARCHAR(HOLE_SNAPPY_QUOTES_LENGTH)))
snappy_quotes: Mapped[Optional[Annotated[str, HOLE_SNAPPY_QUOTES_LENGTH]]] = mapped_column(deferred=True)
blocks = relationship("HoleBlock", primaryjoin="HoleBlock.hole==Hole.name")
followers = relationship("HoleFollow", primaryjoin="HoleFollow.hole==Hole.name")
stealth_hole_unblocks = relationship("StealthHoleUnblock", lazy="dynamic", primaryjoin="StealthHoleUnblock.hole==Hole.name")
blocks: Mapped[list["HoleBlock"]] = relationship(primaryjoin="HoleBlock.hole==Hole.name")
followers: Mapped[list["HoleFollow"]] = relationship(primaryjoin="HoleFollow.hole==Hole.name")
stealth_hole_unblocks: DynamicMapped["StealthHoleUnblock"] = relationship(primaryjoin="StealthHoleUnblock.hole==Hole.name")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,7 +1,8 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from flask import g
@ -10,23 +11,28 @@ from files.helpers.config.const import *
from files.helpers.lazy import lazy
from files.helpers.slurs_and_profanities import censor_slurs_profanities
from files.helpers.sorting_and_time import make_age_string
from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk
if TYPE_CHECKING:
from files.classes import Comment, Post, User
class HoleAction(Base):
__tablename__ = "hole_actions"
id = Column(Integer, primary_key=True)
hole = Column(String, ForeignKey("holes.name"))
user_id = Column(Integer, ForeignKey("users.id"))
kind = Column(String)
target_user_id = Column(Integer, ForeignKey("users.id"))
target_post_id = Column(Integer, ForeignKey("posts.id"))
target_comment_id = Column(Integer, ForeignKey("comments.id"))
_note = Column(String)
created_utc = Column(Integer)
id: Mapped[int_pk]
hole: Mapped[str] = mapped_column(ForeignKey("holes.name"))
user_id: Mapped[Optional[user_id_fk]]
kind: Mapped[Optional[str]]
target_user_id: Mapped[Optional[user_id_fk]]
target_post_id: Mapped[Optional[post_id_fk]]
target_comment_id: Mapped[Optional[comment_id_fk]]
_note: Mapped[Optional[str]]
created_utc: Mapped[int]
user = relationship("User", primaryjoin="User.id==HoleAction.user_id")
target_user = relationship("User", primaryjoin="User.id==HoleAction.target_user_id")
target_post = relationship("Post")
target_comment = relationship("Comment")
user: Mapped["User"] = relationship(primaryjoin="User.id==HoleAction.user_id")
target_user: Mapped["User"] = relationship(primaryjoin="User.id==HoleAction.target_user_id")
target_post: Mapped["Post"] = relationship()
target_comment: Mapped["Comment"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,26 +1,23 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import declared_attr
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, relationship, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
if TYPE_CHECKING:
from files.classes import User
class HoleRelationship(Base):
__tablename__ = NotImplemented
__abstract__ = True
@declared_attr
def user_id(self):
return Column(Integer, ForeignKey("users.id"), primary_key=True)
@declared_attr
def hole(self):
return Column(String(20), ForeignKey("holes.name"), primary_key=True)
@declared_attr
def created_utc(self):
return Column(Integer)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
hole: Mapped[str] = mapped_column(ForeignKey("holes.name"), primary_key=True)
created_utc: Mapped[Optional[int]]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -37,3 +34,11 @@ class HoleBlock(HoleRelationship):
class HoleFollow(HoleRelationship):
__tablename__ = "hole_follows"
class Mod(HoleRelationship):
__tablename__ = "mods"
class Exile(HoleRelationship):
__tablename__ = "exiles"
exiler_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
exiler: Mapped["User"] = relationship(primaryjoin="User.id==Exile.exiler_id")

View File

@ -1,19 +1,24 @@
import time
from typing import TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class IPLog(Base):
__tablename__ = "ip_logs"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
ip = Column(String, primary_key=True)
created_utc = Column(Integer)
last_used = Column(Integer)
user_id: Mapped[user_id_fk_pk]
ip: Mapped[str_pk]
created_utc: Mapped[int]
last_used: Mapped[int]
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:

View File

@ -1,22 +1,24 @@
import time
from typing import Optional
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.config.const import *
from files.helpers.lazy import lazy
from files.helpers.types import int_pk, user_id_fk
class Lottery(Base):
__tablename__ = "lotteries"
id = Column(Integer, primary_key=True)
is_active = Column(Boolean, default=False)
ends_at = Column(Integer)
prize = Column(Integer, default=0)
tickets_sold = Column(Integer, default=0)
winner_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
id: Mapped[int_pk]
is_active: Mapped[bool] = mapped_column(default=False)
ends_at: Mapped[int]
prize: Mapped[int] = mapped_column(default=0)
tickets_sold: Mapped[int] = mapped_column(default=0)
winner_id: Mapped[Optional[user_id_fk]]
created_utc: Mapped[Optional[int]]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,16 +1,17 @@
import time
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import Mapped
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk, user_id_fk
class Media(Base):
__tablename__ = "media"
kind = Column(String, primary_key=True)
filename = Column(String, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
size = Column(Integer)
kind: Mapped[str_pk]
filename: Mapped[str_pk]
user_id: Mapped[user_id_fk]
created_utc: Mapped[int]
size: Mapped[int]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,20 +0,0 @@
import time
from sqlalchemy import Column, ForeignKey
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import *
class Mod(Base):
__tablename__ = "mods"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
hole = Column(String, ForeignKey("holes.name"), primary_key=True)
created_utc = Column(Integer)
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"<{self.__class__.__name__}(user_id={self.user_id}, hole={self.hole})>"

View File

@ -1,7 +1,7 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from flask import g
@ -10,22 +10,27 @@ from files.helpers.config.const import *
from files.helpers.lazy import lazy
from files.helpers.slurs_and_profanities import censor_slurs_profanities
from files.helpers.sorting_and_time import make_age_string
from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk
if TYPE_CHECKING:
from files.classes import Comment, Post, User
class ModAction(Base):
__tablename__ = "modactions"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
kind = Column(String)
target_user_id = Column(Integer, ForeignKey("users.id"))
target_post_id = Column(Integer, ForeignKey("posts.id"))
target_comment_id = Column(Integer, ForeignKey("comments.id"))
_note = Column(String)
created_utc = Column(Integer)
id: Mapped[int_pk]
user_id: Mapped[Optional[user_id_fk]]
kind: Mapped[Optional[str]]
target_user_id: Mapped[Optional[user_id_fk]]
target_post_id: Mapped[Optional[post_id_fk]]
target_comment_id: Mapped[Optional[comment_id_fk]]
_note: Mapped[Optional[str]]
created_utc: Mapped[int]
user = relationship("User", primaryjoin="User.id==ModAction.user_id")
target_user = relationship("User", primaryjoin="User.id==ModAction.target_user_id")
target_post = relationship("Post")
target_comment = relationship("Comment")
user: Mapped["User"] = relationship(primaryjoin="User.id==ModAction.user_id")
target_user: Mapped["User"] = relationship(primaryjoin="User.id==ModAction.target_user_id")
target_post: Mapped["Post"] = relationship()
target_comment: Mapped["Comment"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,21 +1,26 @@
import time
from typing import TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import comment_id_fk_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import Comment, User
class Notification(Base):
__tablename__ = "notifications"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True)
read = Column(Boolean, default=False)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
comment_id: Mapped[comment_id_fk_pk]
read: Mapped[bool] = mapped_column(default=False)
created_utc: Mapped[int]
comment = relationship("Comment")
user = relationship("User")
comment: Mapped["Comment"] = relationship()
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,11 +1,13 @@
import time
from flask import g, abort
import requests
from typing import Optional
from sqlalchemy import Column, or_
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import int_pk
from files.helpers.lazy import lazy
from files.helpers.config.const import *
@ -13,13 +15,13 @@ from files.helpers.config.const import *
class Orgy(Base):
__tablename__ = "orgies"
created_utc = Column(Integer, primary_key=True)
type = Column(String)
data = Column(String)
title = Column(String)
start_utc = Column(Integer)
end_utc = Column(Integer)
started = Column(Boolean, default=False)
created_utc: Mapped[int_pk]
type: Mapped[str]
data: Mapped[str]
title: Mapped[str]
start_utc: Mapped[int]
end_utc: Mapped[Optional[int]]
started: Mapped[bool] = mapped_column(default=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,23 +1,29 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import Comment, Post, User
class PostOption(Base):
__tablename__ = "post_options"
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey("posts.id"))
body_html = Column(Text)
exclusive = Column(Integer)
created_utc = Column(Integer)
id: Mapped[int_pk]
parent_id: Mapped[post_id_fk]
body_html: Mapped[str] = mapped_column(Text)
exclusive: Mapped[int]
created_utc: Mapped[Optional[int]]
votes = relationship("PostOptionVote")
parent = relationship("Post", back_populates="options")
votes: Mapped[list["PostOptionVote"]] = relationship()
parent: Mapped["Post"] = relationship(back_populates="options")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -40,12 +46,12 @@ class PostOptionVote(Base):
__tablename__ = "post_option_votes"
option_id = Column(Integer, ForeignKey("post_options.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
post_id = Column(Integer, ForeignKey("posts.id"))
option_id: Mapped[int] = mapped_column(ForeignKey("post_options.id"), primary_key=True)
user_id: Mapped[user_id_fk_pk]
created_utc: Mapped[int]
post_id: Mapped[Optional[post_id_fk]]
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -59,14 +65,14 @@ class CommentOption(Base):
__tablename__ = "comment_options"
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey("comments.id"))
body_html = Column(Text)
exclusive = Column(Integer)
created_utc = Column(Integer)
id: Mapped[int_pk]
parent_id: Mapped[comment_id_fk]
body_html: Mapped[str] = mapped_column(Text)
exclusive: Mapped[int]
created_utc: Mapped[Optional[int]]
votes = relationship("CommentOptionVote")
parent = relationship("Comment", back_populates="options")
votes: Mapped[list["CommentOptionVote"]] = relationship()
parent: Mapped["Comment"] = relationship(back_populates="options")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -89,12 +95,12 @@ class CommentOptionVote(Base):
__tablename__ = "comment_option_votes"
option_id = Column(Integer, ForeignKey("comment_options.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
comment_id = Column(Integer, ForeignKey("comments.id"))
option_id: Mapped[int] = mapped_column(ForeignKey("comment_options.id"), primary_key=True)
user_id: Mapped[user_id_fk_pk]
created_utc: Mapped[int]
comment_id: Mapped[Optional[comment_id_fk]]
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,10 +1,11 @@
import random
import time
from typing import Optional, TYPE_CHECKING
from urllib.parse import urlparse
from flask import g
from sqlalchemy import Column, FetchedValue, ForeignKey
from sqlalchemy.orm import deferred, relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
@ -14,6 +15,7 @@ from files.helpers.slurs_and_profanities import *
from files.helpers.lazy import lazy
from files.helpers.regex import *
from files.helpers.sorting_and_time import make_age_string
from files.helpers.types import int_pk, user_id_fk
from files.helpers.bleach_body import *
from .comment import *
@ -22,69 +24,73 @@ from .hole import *
from .subscriptions import *
from .saves import SaveRelationship
if TYPE_CHECKING:
from files.classes import OauthApp, Report
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"))
edited_utc = Column(Integer, default=0)
created_utc = Column(Integer)
thumburl = Column(String)
posterurl = Column(String)
is_banned = Column(Boolean, default=False)
bannedfor = Column(String)
chuddedfor = Column(String)
ghost = Column(Boolean, default=False)
effortpost = Column(Boolean, default=False)
views = Column(Integer, default=0)
deleted_utc = Column(Integer, default=0)
distinguish_level = Column(Integer, default=0)
stickied = Column(String)
stickied_utc = Column(Integer)
hole_pinned = Column(String)
hole = Column(String, ForeignKey("holes.name"))
is_pinned = Column(Boolean, default=False)
private = Column(Boolean, default=False)
comment_count = Column(Integer, default=0)
is_approved = Column(Integer, ForeignKey("users.id"))
is_bot = Column(Boolean, default=False)
upvotes = Column(Integer, default=1)
downvotes = Column(Integer, default=0)
realupvotes = Column(Integer, default=1)
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
title = Column(String)
title_html = Column(String)
url = Column(String)
body = Column(String)
body_html = Column(String)
flair = Column(String)
ban_reason = Column(String)
embed = Column(String)
new = Column(Boolean)
notify = Column(Boolean)
chudded = Column(Boolean, default=False)
rainbowed = Column(Boolean, default=False)
queened = Column(Boolean, default=False)
sharpened = Column(Boolean, default=False)
ping_cost = Column(Integer, default=0)
bump_utc = Column(Integer)
id: Mapped[int_pk]
author_id: Mapped[user_id_fk]
edited_utc: Mapped[int] = mapped_column(default=0)
created_utc: Mapped[int]
thumburl: Mapped[Optional[str]]
posterurl: Mapped[Optional[str]]
is_banned: Mapped[bool] = mapped_column(default=False)
bannedfor: Mapped[Optional[str]]
chuddedfor: Mapped[Optional[str]]
ghost: Mapped[bool] = mapped_column(default=False)
effortpost: Mapped[bool] = mapped_column(default=False)
views: Mapped[int] = mapped_column(default=0)
deleted_utc: Mapped[int] = mapped_column(default=0)
distinguished: Mapped[bool] = mapped_column(default=False)
stickied: Mapped[Optional[str]]
stickied_utc: Mapped[Optional[int]]
hole_pinned: Mapped[Optional[str]]
hole: Mapped[Optional[str]] = mapped_column(ForeignKey("holes.name"))
is_pinned: Mapped[bool] = mapped_column(default=False)
private: Mapped[bool] = mapped_column(default=False)
comment_count: Mapped[int] = mapped_column(default=0)
is_approved: Mapped[Optional[user_id_fk]]
is_bot: Mapped[bool] = mapped_column(default=False)
upvotes: Mapped[int] = mapped_column(default=1)
downvotes: Mapped[int] = mapped_column(default=0)
realupvotes: Mapped[Optional[int]] = mapped_column(default=1)
app_id: Mapped[Optional[int]] = mapped_column(ForeignKey("oauth_apps.id"))
title: Mapped[str]
title_html: Mapped[str]
url: Mapped[Optional[str]]
body: Mapped[Optional[str]]
body_html: Mapped[Optional[str]]
flair: Mapped[Optional[str]]
ban_reason: Mapped[Optional[str]]
embed: Mapped[Optional[str]]
new: Mapped[Optional[bool]]
notify: Mapped[bool]
chudded: Mapped[bool] = mapped_column(default=False)
rainbowed: Mapped[bool] = mapped_column(default=False)
queened: Mapped[bool] = mapped_column(default=False)
sharpened: Mapped[bool] = mapped_column(default=False)
ping_cost: Mapped[int] = mapped_column(default=0)
bump_utc: Mapped[int]
if FEATURES['NSFW_MARKING']:
nsfw = Column(Boolean, default=False)
nsfw: Mapped[bool] = mapped_column(default=False)
else:
nsfw = False
if SITE_NAME == 'WPD':
cw = Column(Boolean, default=False)
cw: Mapped[bool] = mapped_column(default=False)
author = relationship("User", primaryjoin="Post.author_id==User.id")
oauth_app = relationship("OauthApp")
approved_by = relationship("User", uselist=False, primaryjoin="Post.is_approved==User.id")
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="post")
reports = relationship("Report", order_by="Report.created_utc")
comments = relationship("Comment", primaryjoin="Comment.parent_post==Post.id", back_populates="post")
hole_obj = relationship("Hole", primaryjoin="foreign(Post.hole)==remote(Hole.name)")
options = relationship("PostOption", order_by="PostOption.id")
author: Mapped["User"] = relationship(primaryjoin="Post.author_id==User.id")
oauth_app: Mapped["OauthApp"] = relationship()
approved_by: Mapped["User"] = relationship(uselist=False, primaryjoin="Post.is_approved==User.id")
awards: Mapped[list["AwardRelationship"]] = relationship(order_by="AwardRelationship.awarded_utc.desc()", back_populates="post")
reports: Mapped[list["Report"]] = relationship(order_by="Report.created_utc")
comments: Mapped[list["Comment"]] = relationship(primaryjoin="Comment.parent_post==Post.id", back_populates="post")
hole_obj: Mapped["Hole"] = relationship(primaryjoin="foreign(Post.hole)==remote(Hole.name)")
options: Mapped[list["PostOption"]] = relationship(order_by="PostOption.id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:
@ -254,7 +260,7 @@ class Post(Base):
'downvotes': self.downvotes,
'stickied': self.stickied,
'private' : self.private,
'distinguish_level': self.distinguish_level,
'distinguished': self.distinguished,
'voted': self.voted if hasattr(self, 'voted') else 0,
'reports': reports,
}
@ -269,7 +275,10 @@ class Post(Base):
if v and v.poor:
return 0
if self.distinguish_level and SITE_NAME == 'WPD':
if self.distinguished and SITE_NAME == 'WPD':
return 0
if not v and kind == "ricardo":
return 0
num = len([x for x in self.awards if x.kind == kind])
@ -420,4 +429,4 @@ class Post(Base):
if self.hole == 'chudrama':
return v.admin_level >= PERMS['POST_COMMENT_MODERATION']
else:
return v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (self.hole and v.mods_hole(self.hole)) or self.author_id == v.id
return v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (v.mods_hole(self.hole)) or self.author_id == v.id

View File

@ -1,15 +1,16 @@
import time
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import Mapped
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk, user_id_fk_pk
class PushSubscription(Base):
__tablename__ = "push_subscriptions"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
subscription_json = Column(String, primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
subscription_json: Mapped[str_pk]
created_utc: Mapped[int]
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,22 +1,27 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.slurs_and_profanities import censor_slurs_profanities
from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class Report(Base):
__tablename__ = "reports"
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
reason = Column(String)
created_utc = Column(Integer)
post_id: Mapped[post_id_fk_pk]
user_id: Mapped[user_id_fk_pk]
reason: Mapped[Optional[str]]
created_utc: Mapped[int]
user = relationship("User", primaryjoin = "Report.user_id == User.id", uselist = False)
user: Mapped["User"] = relationship(primaryjoin = "Report.user_id == User.id", uselist = False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -39,12 +44,12 @@ class Report(Base):
class CommentReport(Base):
__tablename__ = "commentreports"
comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
reason = Column(String)
created_utc = Column(Integer)
comment_id: Mapped[comment_id_fk_pk]
user_id: Mapped[user_id_fk_pk]
reason: Mapped[Optional[str]]
created_utc: Mapped[int]
user = relationship("User", primaryjoin = "CommentReport.user_id == User.id", uselist = False)
user: Mapped["User"] = relationship(primaryjoin = "CommentReport.user_id == User.id", uselist = False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,19 +1,25 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes.comment import Comment
from files.classes.post import Post
class SaveRelationship(Base):
__tablename__ = "save_relationship"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
post_id: Mapped[post_id_fk_pk]
created_utc: Mapped[Optional[int]]
post = relationship("Post", uselist=False)
post: Mapped["Post"] = relationship(uselist=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -27,11 +33,11 @@ class CommentSaveRelationship(Base):
__tablename__ = "comment_save_relationship"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
comment_id: Mapped[comment_id_fk_pk]
created_utc: Mapped[Optional[int]]
comment = relationship("Comment", uselist=False)
comment: Mapped["Comment"] = relationship(uselist=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,19 +1,24 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import post_id_fk_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import Post, User
class Subscription(Base):
__tablename__ = "subscriptions"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
post_id: Mapped[post_id_fk_pk]
created_utc: Mapped[Optional[int]]
user = relationship("User", uselist=False)
post = relationship("Post", uselist=False)
user: Mapped["User"] = relationship(uselist=False)
post: Mapped["Post"] = relationship(uselist=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,16 +1,19 @@
from sqlalchemy import Column
from typing import Optional
from sqlalchemy.orm import Mapped
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import str_pk
class Transaction(Base):
__tablename__ = "transactions"
id = Column(String, primary_key=True)
created_utc = Column(Integer)
type = Column(String)
amount = Column(Integer)
email = Column(String)
claimed = Column(Boolean)
id: Mapped[str_pk]
created_utc: Mapped[int]
type: Mapped[str]
amount: Mapped[int]
email: Mapped[str]
claimed: Mapped[Optional[bool]]
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id})>"

View File

@ -1,9 +1,9 @@
import random
from operator import *
from typing import Optional
import pyotp
from sqlalchemy import Column, ForeignKey, FetchedValue
from sqlalchemy.orm import aliased, deferred, Query
from sqlalchemy.orm import DynamicMapped, Mapped, aliased, mapped_column
from sqlalchemy.sql import case, func, literal
from sqlalchemy.sql.expression import not_, and_, or_
from sqlalchemy.sql.sqltypes import *
@ -20,16 +20,15 @@ from files.helpers.config.awards import AWARDS_ENABLED, HOUSE_AWARDS
from files.helpers.media import *
from files.helpers.security import *
from files.helpers.sorting_and_time import *
from files.helpers.types import int_pk, user_id_fk
from files.helpers.can_see import *
from .alts import Alt
from .award import AwardRelationship
from .badges import *
from .clients import *
from .exiles import *
from .follows import *
from .hats import *
from .mod import *
from .mod_logs import *
from .notifications import Notification
from .saves import *
@ -51,106 +50,106 @@ else:
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String)
namecolor = Column(String, default=DEFAULT_COLOR)
background = Column(String)
profile_background = Column(String)
flair = deferred(Column(String))
flair_html = Column(String)
flaircolor = Column(String, default=DEFAULT_COLOR)
theme = Column(String, default=DEFAULT_THEME)
themecolor = Column(String, default=DEFAULT_COLOR)
song = Column(String)
highres = Column(String)
profileurl = Column(String)
bannerurl = Column(String)
house = Column(String, default='')
old_house = Column(String, default='')
patron = Column(Integer, default=0)
patron_utc = Column(Integer, default=0)
verified = Column(String)
verifiedcolor = Column(String)
hieroglyphs = Column(Integer, default=0)
rehab = Column(Integer, default=0)
longpost = Column(Integer, default=0)
bird = Column(Integer, default=0)
email = deferred(Column(String))
css = Column(String)
profilecss = deferred(Column(String))
passhash = deferred(Column(String))
post_count = Column(Integer, default=0)
comment_count = Column(Integer, default=0)
received_award_count = Column(Integer, default=0)
created_utc = Column(Integer)
admin_level = Column(Integer, default=DEFAULT_ADMIN_LEVEL)
last_active = Column(Integer)
coins_spent = Column(Integer, default=0)
coins_spent_on_hats = Column(Integer, default=0)
lootboxes_bought = Column(Integer, default=0)
chud = Column(Integer, default=0)
queen = Column(Integer, default=0)
chud_phrase = Column(String)
email_verified = Column(Boolean, default=False)
shadowbanned = Column(Integer, ForeignKey("users.id"))
chudded_by = Column(Integer, ForeignKey("users.id"))
slurreplacer = Column(Integer, default=1)
profanityreplacer = Column(Integer, default=1)
flairchanged = Column(Integer, default=0)
namechanged = Column(Integer, default=0)
newtab = Column(Boolean, default=False)
newtabexternal = Column(Boolean, default=True)
frontsize = Column(Integer, default=25)
bio = deferred(Column(String))
bio_html = Column(String)
sig = deferred(Column(String))
sig_html = Column(String)
show_sigs = Column(Boolean, default=True)
progressivestack = Column(Integer, default=0)
deflector = Column(Integer, default=0)
friends = deferred(Column(String))
friends_html = deferred(Column(String))
enemies = deferred(Column(String))
enemies_html = deferred(Column(String))
is_banned = Column(Integer, ForeignKey("users.id"))
unban_utc = Column(Integer)
ban_reason = deferred(Column(String))
shadowban_reason = deferred(Column(String))
is_muted = Column(Boolean, default=False)
login_nonce = Column(Integer, default=0)
coins = Column(Integer, default=DEFAULT_COINS)
truescore = Column(Integer, default=0)
marseybux = Column(Integer, default=DEFAULT_MARSEYBUX)
mfa_secret = deferred(Column(String))
is_private = Column(Boolean, default=False)
stored_subscriber_count = Column(Integer, default=0)
defaultsortingcomments = Column(String, default="hot")
defaultsorting = Column(String, default="hot")
defaulttime = Column(String, default=DEFAULT_TIME_FILTER)
custom_filter_list = Column(String)
original_username = Column(String)
extra_username = Column(String)
prelock_username = Column(String)
referred_by = Column(Integer, ForeignKey("users.id"))
currently_held_lottery_tickets = Column(Integer, default=0)
total_held_lottery_tickets = Column(Integer, default=0)
total_lottery_winnings = Column(Integer, default=0)
last_viewed_modmail_notifs = Column(Integer, default=0)
last_viewed_post_notifs = Column(Integer, default=0)
last_viewed_log_notifs = Column(Integer, default=0)
last_viewed_reddit_notifs = Column(Integer, default=0)
bite = Column(Integer, default=0)
owoify = Column(Integer, default=0)
sharpen = Column(Integer, default=0)
marsify = Column(Integer, default=0)
rainbow = Column(Integer, default=0)
spider = Column(Integer, default=0)
lifetimedonated = Column(Integer, default=0)
lifetimedonated_visible = Column(Boolean, default=False)
blacklisted_by = Column(Integer, ForeignKey("users.id"))
grinch = Column(Boolean, default=SITE_NAME != 'rDrama') #don't put in an if condition, it will cause an error bc it has a not-null constraint
group_creation_notifs = Column(Boolean, default=False)
effortpost_notifs = Column(Boolean, default=False)
id: Mapped[int_pk]
username: Mapped[str]
namecolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR)
background: Mapped[Optional[str]]
profile_background: Mapped[Optional[str]]
flair: Mapped[Optional[str]] = mapped_column(deferred=True)
flair_html: Mapped[Optional[str]]
flaircolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR)
theme: Mapped[str] = mapped_column(default=DEFAULT_THEME)
themecolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR)
song: Mapped[Optional[str]]
highres: Mapped[Optional[str]]
profileurl: Mapped[Optional[str]]
bannerurl: Mapped[Optional[str]]
house: Mapped[Optional[str]] = mapped_column(default='')
old_house: Mapped[Optional[str]] = mapped_column(default='')
patron: Mapped[int] = mapped_column(default=0)
patron_utc: Mapped[int] = mapped_column(default=0)
verified: Mapped[Optional[str]]
verifiedcolor: Mapped[Optional[str]]
hieroglyphs: Mapped[Optional[int]] = mapped_column(default=0)
rehab: Mapped[Optional[int]] = mapped_column(default=0)
longpost: Mapped[Optional[int]] = mapped_column(default=0)
bird: Mapped[Optional[int]] = mapped_column(default=0)
email: Mapped[Optional[str]] = mapped_column(deferred=True)
css: Mapped[Optional[str]]
profilecss: Mapped[Optional[str]] = mapped_column(deferred=True)
passhash: Mapped[str] = mapped_column(deferred=True)
post_count: Mapped[int] = mapped_column(default=0)
comment_count: Mapped[int] = mapped_column(default=0)
received_award_count: Mapped[int] = mapped_column(default=0)
created_utc: Mapped[int]
admin_level: Mapped[int] = mapped_column(default=DEFAULT_ADMIN_LEVEL)
last_active: Mapped[int]
coins_spent: Mapped[int] = mapped_column(default=0)
coins_spent_on_hats: Mapped[int] = mapped_column(default=0)
lootboxes_bought: Mapped[int] = mapped_column(default=0)
chud: Mapped[int] = mapped_column(default=0)
queen: Mapped[Optional[int]] = mapped_column(default=0)
chud_phrase: Mapped[Optional[str]]
email_verified: Mapped[bool] = mapped_column(default=False)
shadowbanned: Mapped[Optional[user_id_fk]]
chudded_by: Mapped[Optional[user_id_fk]]
slurreplacer: Mapped[int] = mapped_column(default=1)
profanityreplacer: Mapped[int] = mapped_column(default=1)
flairchanged: Mapped[Optional[int]] = mapped_column(default=0)
namechanged: Mapped[Optional[int]] = mapped_column(default=0)
newtab: Mapped[bool] = mapped_column(default=False)
newtabexternal: Mapped[bool] = mapped_column(default=True)
frontsize: Mapped[int] = mapped_column(default=25)
bio: Mapped[Optional[str]] = mapped_column(deferred=True)
bio_html: Mapped[Optional[str]]
sig: Mapped[Optional[str]] = mapped_column(deferred=True)
sig_html: Mapped[Optional[str]]
show_sigs: Mapped[bool] = mapped_column(default=True)
progressivestack: Mapped[Optional[int]] = mapped_column(default=0)
deflector: Mapped[Optional[int]] = mapped_column(default=0)
friends: Mapped[Optional[str]] = mapped_column(deferred=True)
friends_html: Mapped[Optional[str]] = mapped_column(deferred=True)
enemies: Mapped[Optional[str]] = mapped_column(deferred=True)
enemies_html: Mapped[Optional[str]] = mapped_column(deferred=True)
is_banned: Mapped[Optional[user_id_fk]]
unban_utc: Mapped[Optional[int]]
ban_reason: Mapped[Optional[str]] = mapped_column(deferred=True)
shadowban_reason: Mapped[Optional[str]] = mapped_column(deferred=True)
is_muted: Mapped[bool] = mapped_column(default=False)
login_nonce: Mapped[int] = mapped_column(default=0)
coins: Mapped[int] = mapped_column(default=DEFAULT_COINS)
truescore: Mapped[int] = mapped_column(default=0)
marseybux: Mapped[int] = mapped_column(default=DEFAULT_MARSEYBUX)
mfa_secret: Mapped[Optional[str]] = mapped_column(deferred=True)
is_private: Mapped[bool] = mapped_column(default=False)
stored_subscriber_count: Mapped[int] = mapped_column(default=0)
defaultsortingcomments: Mapped[str] = mapped_column(default="hot")
defaultsorting: Mapped[str] = mapped_column(default="hot")
defaulttime: Mapped[str] = mapped_column(default=DEFAULT_TIME_FILTER)
custom_filter_list: Mapped[Optional[str]]
original_username: Mapped[Optional[str]]
extra_username: Mapped[Optional[str]]
prelock_username: Mapped[Optional[str]]
referred_by: Mapped[Optional[user_id_fk]]
currently_held_lottery_tickets: Mapped[int] = mapped_column(default=0)
total_held_lottery_tickets: Mapped[int] = mapped_column(default=0)
total_lottery_winnings: Mapped[int] = mapped_column(default=0)
last_viewed_modmail_notifs: Mapped[int] = mapped_column(default=0)
last_viewed_post_notifs: Mapped[int] = mapped_column(default=0)
last_viewed_log_notifs: Mapped[int] = mapped_column(default=0)
last_viewed_offsite_notifs: Mapped[int] = mapped_column(default=0)
bite: Mapped[Optional[int]] = mapped_column(default=0)
owoify: Mapped[Optional[int]] = mapped_column(default=0)
sharpen: Mapped[Optional[int]] = mapped_column(default=0)
marsify: Mapped[Optional[int]] = mapped_column(default=0)
rainbow: Mapped[Optional[int]] = mapped_column(default=0)
spider: Mapped[Optional[int]] = mapped_column(default=0)
lifetimedonated: Mapped[int] = mapped_column(default=0)
lifetimedonated_visible: Mapped[bool] = mapped_column(default=False)
blacklisted_by: Mapped[Optional[user_id_fk]]
grinch: Mapped[bool] = mapped_column(default=SITE_NAME != 'rDrama') #don't put in an if condition, it will cause an error bc it has a not-null constraint
group_creation_notifs: Mapped[bool] = mapped_column(default=False)
effortpost_notifs: Mapped[bool] = mapped_column(default=False)
if SITE_NAME == 'WPD':
nitter = False
@ -160,37 +159,37 @@ class User(Base):
pronouns = 'they/them'
earlylife = 0
hole_creation_notifs = False
hidevotedon = Column(Boolean, default=False)
hide_cw = Column(Boolean, default=False)
hidevotedon: Mapped[bool] = mapped_column(default=False)
hide_cw: Mapped[bool] = mapped_column(default=False)
else:
nitter = Column(Boolean, default=False)
imgsed = Column(Boolean, default=False)
controversial = Column(Boolean, default=False)
reddit = Column(String, default='old.reddit.com')
pronouns = Column(String, default='they/them')
earlylife = Column(Integer, default=0)
hole_creation_notifs = Column(Boolean, default=True)
nitter: Mapped[bool] = mapped_column(default=False)
imgsed: Mapped[bool] = mapped_column(default=False)
controversial: Mapped[bool] = mapped_column(default=False)
reddit: Mapped[str] = mapped_column(default='old.reddit.com')
pronouns: Mapped[str] = mapped_column(default='they/them')
earlylife: Mapped[Optional[int]] = mapped_column(default=0)
hole_creation_notifs: Mapped[bool] = mapped_column(default=True)
hidevotedon = False
hide_cw = False
if IS_HOMOWEEN():
zombie = Column(Integer, default=0) # > 0 vaxxed; < 0 zombie
jumpscare = Column(Integer, default=0)
zombie: Mapped[int] = mapped_column(default=0) # > 0 vaxxed; < 0 zombie
jumpscare: Mapped[int] = mapped_column(default=0)
badges = relationship("Badge", order_by="Badge.created_utc", back_populates="user")
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")
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")
apps = relationship("OauthApp", back_populates="author")
awards = relationship("AwardRelationship", primaryjoin="User.id==AwardRelationship.user_id", back_populates="user")
referrals = relationship("User", primaryjoin="User.id==User.referred_by", order_by="User.created_utc")
designed_hats = relationship("HatDef", primaryjoin="User.id==HatDef.author_id", back_populates="author")
owned_hats = relationship("Hat", back_populates="owners")
hats_equipped = relationship("Hat", lazy="raise", viewonly=True)
hole_mods = relationship("Mod", primaryjoin="User.id == Mod.user_id", lazy="raise")
badges: Mapped[list["Badge"]] = relationship(order_by="Badge.created_utc", back_populates="user")
subscriptions: Mapped[list["Subscription"]] = relationship(back_populates="user")
following: Mapped[list["Follow"]] = relationship(primaryjoin="Follow.user_id==User.id", back_populates="user")
followers: Mapped[list["Follow"]] = relationship(primaryjoin="Follow.target_id==User.id", back_populates="target")
blocking: DynamicMapped["UserBlock"] = relationship(primaryjoin="User.id==UserBlock.user_id", back_populates="user")
blocked: DynamicMapped["UserBlock"] = relationship(primaryjoin="User.id==UserBlock.target_id", back_populates="target")
authorizations: Mapped[list["ClientAuth"]] = relationship(back_populates="user")
apps: Mapped[list["OauthApp"]] = relationship(back_populates="author")
awards: Mapped[list["AwardRelationship"]] = relationship(primaryjoin="User.id==AwardRelationship.user_id", back_populates="user")
referrals: Mapped[list["User"]] = relationship(primaryjoin="User.id==User.referred_by", order_by="User.created_utc")
designed_hats: Mapped[list["HatDef"]] = relationship(primaryjoin="User.id==HatDef.author_id", back_populates="author")
owned_hats: Mapped[list["Hat"]] = relationship(back_populates="owners")
hats_equipped: Mapped[list["Hat"]] = relationship(lazy="raise", viewonly=True)
hole_mods: Mapped[list["Mod"]] = relationship(primaryjoin="User.id == Mod.user_id", lazy="raise")
def __init__(self, **kwargs):
@ -204,7 +203,7 @@ class User(Base):
kwargs["last_viewed_modmail_notifs"] = kwargs["created_utc"]
kwargs["last_viewed_post_notifs"] = kwargs["created_utc"]
kwargs["last_viewed_log_notifs"] = kwargs["created_utc"]
kwargs["last_viewed_reddit_notifs"] = kwargs["created_utc"]
kwargs["last_viewed_offsite_notifs"] = kwargs["created_utc"]
super().__init__(**kwargs)
@ -431,6 +430,7 @@ class User(Base):
@lazy
def mods_hole(self, hole):
if not hole: return False
if self.is_permabanned or self.shadowbanned: return False
if hole == 'test'and self.truescore >= TRUESCORE_MINIMUM: return True
if self.admin_level >= PERMS['MODS_EVERY_HOLE']: return True
@ -557,8 +557,8 @@ class User(Base):
@property
@lazy
def can_view_offsitementions(self):
return self.offsitementions or self.admin_level >= PERMS['NOTIFICATIONS_REDDIT']
def can_view_offsite_mentions(self):
return self.has_badge(140) or self.admin_level >= PERMS['NOTIFICATIONS_OFFSITE']
@lazy
def can_edit(self, target):
@ -787,7 +787,7 @@ class User(Base):
Comment.deleted_utc == 0,
)
return notifs.count() + self.modmail_notifications_count + self.post_notifications_count + self.modaction_notifications_count + self.reddit_notifications_count
return notifs.count() + self.modmail_notifications_count + self.post_notifications_count + self.modaction_notifications_count + self.offsite_notifications_count
@property
@lazy
@ -797,7 +797,7 @@ class User(Base):
- self.modmail_notifications_count \
- self.post_notifications_count \
- self.modaction_notifications_count \
- self.reddit_notifications_count
- self.offsite_notifications_count
@property
@lazy
@ -886,13 +886,13 @@ class User(Base):
@property
@lazy
def reddit_notifications_count(self):
if not self.can_view_offsitementions or (SITE == "watchpeopledie.tv" and self.id == AEVANN_ID):
def offsite_notifications_count(self):
if not self.can_view_offsite_mentions or (SITE == "watchpeopledie.tv" and self.id == AEVANN_ID):
return 0
return g.db.query(Comment).filter(
Comment.created_utc > self.last_viewed_reddit_notifs,
Comment.created_utc > self.last_viewed_offsite_notifs,
Comment.is_banned == False, Comment.deleted_utc == 0,
Comment.body_html.like('%<p>New site mention%<a href="https://old.reddit.com/r/%'),
Comment.body_html.like('%<p>New site mention by <a href=%'),
Comment.parent_post == None, Comment.author_id == AUTOJANNY_ID).count()
@property
@ -909,8 +909,8 @@ class User(Base):
return 'posts'
elif self.modaction_notifications_count > 0:
return 'modactions'
elif self.reddit_notifications_count > 0:
return 'reddit'
elif self.offsite_notifications_count > 0:
return 'offsite'
return ''
@property
@ -922,7 +922,7 @@ class User(Base):
'modmail': '#f15387',
'posts': '#0000ff',
'modactions': '#1ad80d',
'reddit': '#805ad5',
'offsite': '#805ad5',
}
return colors[self.notifications_do] if self.notifications_do \
else colors['']
@ -1288,11 +1288,6 @@ class User(Base):
def unblockable(self):
return self.has_badge(87)
@property
@lazy
def offsitementions(self):
return self.has_badge(140)
@lazy
def pride_username(self, v):
return not (v and v.poor) and self.has_badge(303)

View File

@ -1,19 +1,24 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class UserBlock(Base):
__tablename__ = "userblocks"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
target_id: Mapped[user_id_fk_pk]
created_utc: Mapped[Optional[int]]
user = relationship("User", primaryjoin="User.id==UserBlock.user_id", back_populates="blocking")
target = relationship("User", primaryjoin="User.id==UserBlock.target_id", back_populates="blocked")
user: Mapped["User"] = relationship(primaryjoin="User.id==UserBlock.user_id", back_populates="blocking")
target: Mapped["User"] = relationship(primaryjoin="User.id==UserBlock.target_id", back_populates="blocked")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,19 +1,24 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.types import user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class UserMute(Base):
__tablename__ = "usermutes"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
target_id: Mapped[user_id_fk_pk]
created_utc: Mapped[Optional[int]]
user = relationship("User", primaryjoin="User.id==UserMute.user_id")
target = relationship("User", primaryjoin="User.id==UserMute.target_id")
user: Mapped["User"] = relationship(primaryjoin="User.id==UserMute.user_id")
target: Mapped["User"] = relationship(primaryjoin="User.id==UserMute.target_id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,22 +1,27 @@
import time
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import *
from files.helpers.sorting_and_time import make_age_string
from files.helpers.types import user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class ViewerRelationship(Base):
__tablename__ = "viewers"
user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
viewer_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
last_view_utc = Column(Integer)
created_utc = Column(Integer)
user_id: Mapped[user_id_fk_pk]
viewer_id: Mapped[user_id_fk_pk]
last_view_utc: Mapped[int]
created_utc: Mapped[Optional[int]]
viewer = relationship("User", primaryjoin="ViewerRelationship.viewer_id == User.id")
viewer: Mapped["User"] = relationship(primaryjoin="ViewerRelationship.viewer_id == User.id")
def __init__(self, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -1,23 +1,28 @@
import time
from typing import TYPE_CHECKING
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
from files.helpers.lazy import lazy
from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk
if TYPE_CHECKING:
from files.classes import User
class Vote(Base):
__tablename__ = "votes"
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
vote_type = Column(Integer)
real = Column(Boolean, default=True)
coins = Column(Integer, default=1)
created_utc = Column(Integer)
post_id: Mapped[post_id_fk_pk]
user_id: Mapped[user_id_fk_pk]
vote_type: Mapped[int]
real: Mapped[bool] = mapped_column(default=True)
coins: Mapped[int] = mapped_column(default=1)
created_utc: Mapped[int]
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -40,14 +45,14 @@ class CommentVote(Base):
__tablename__ = "commentvotes"
comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
vote_type = Column(Integer)
real = Column(Boolean, default=True)
coins = Column(Integer, default=1)
created_utc = Column(Integer)
comment_id: Mapped[comment_id_fk_pk]
user_id: Mapped[user_id_fk_pk]
vote_type: Mapped[int]
real: Mapped[bool] = mapped_column(default=True)
coins: Mapped[int] = mapped_column(default=1)
created_utc: Mapped[int]
user = relationship("User")
user: Mapped["User"] = relationship()
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -12,7 +12,7 @@ from files.classes.mod_logs import ModAction
from files.classes.notifications import Notification
from files.classes.polls import CommentOption, PostOption
from files.classes.award import AwardRelationship
from files.classes.exiles import Exile
from files.classes.hole_relationship import Exile
from files.helpers.alerts import send_repeatable_notification, push_notif
from files.helpers.config.const import *
@ -241,7 +241,7 @@ def execute_snappy(post, v):
if len(body_html) < COMMENT_BODY_HTML_LENGTH_LIMIT:
c = Comment(author_id=SNAPPY_ID,
distinguish_level=6,
distinguished=True,
parent_post=post.id,
level=1,
nsfw=False,
@ -314,7 +314,7 @@ def execute_zozbot(c, level, post, v):
body_html='<p class="zozbot">zoz</p>',
top_comment_id=c.top_comment_id,
ghost=c.ghost,
distinguish_level=6
distinguished=True
)
g.db.add(c2)
@ -332,7 +332,7 @@ def execute_zozbot(c, level, post, v):
body_html='<p class="zozbot">zle</p>',
top_comment_id=c.top_comment_id,
ghost=c.ghost,
distinguish_level=6
distinguished=True
)
g.db.add(c3)
@ -349,7 +349,7 @@ def execute_zozbot(c, level, post, v):
body_html='<p class="zozbot">zozzle</p>',
top_comment_id=c.top_comment_id,
ghost=c.ghost,
distinguish_level=6
distinguished=True
)
g.db.add(c4)

View File

@ -19,7 +19,7 @@ def create_comment(text_html):
new_comment = Comment(author_id=AUTOJANNY_ID,
parent_post=None,
body_html=text_html,
distinguish_level=6,
distinguished=True,
is_bot=True)
g.db.add(new_comment)
g.db.flush()
@ -310,7 +310,7 @@ def alert_admins(body):
level=1,
body_html=body_html,
sentto=MODMAIL_ID,
distinguish_level=6,
distinguished=True,
is_bot=True
)
g.db.add(new_comment)

View File

@ -4,7 +4,7 @@ from files.classes.comment import Comment
from files.classes.hole import Hole
from flask import request
words_to_hide = ('israel', 'palestin', 'muslim', 'islam', 'hamas', 'jew', 'gaza', 'rafah', 'isis', 'terror', 'iraq')
words_to_hide = ('israel', 'isreal', 'palestin', 'muslim', 'islam', 'hamas', 'jew', 'zion', 'gaza', 'rafah', 'isis', 'terror', 'iraq', 'allah', 'mohammad', 'muhammad', 'mohammed', 'muhammed', 'mohamad', 'muhamad', 'mohamed', 'muhamed')
def can_see(user, other):
if isinstance(other, (Post, Comment)):

View File

@ -132,14 +132,14 @@ GIRL_PHRASES = [
patron = "Patron"
REDDIT_NOTIFS_SITE = set()
OFFSITE_NOTIF_QUERIES = set()
REDDIT_NOTIFS_USERS = {}
if len(SITE_NAME) > 5:
REDDIT_NOTIFS_SITE.add(SITE_NAME.lower())
OFFSITE_NOTIF_QUERIES.add(SITE_NAME.lower())
if not IS_LOCALHOST:
REDDIT_NOTIFS_SITE.add(SITE)
OFFSITE_NOTIF_QUERIES.add(SITE)
TAGLINES = ()
@ -154,7 +154,7 @@ PERMS = { # Minimum admin_level to perform action.
'BYPASS_CHAT_TRUESCORE_REQUIREMENT': 1,
'BYPASS_ANTISPAM_CHECKS': 1,
'WARN_ON_FAILED_LOGIN': 1,
'NOTIFICATIONS_REDDIT': 1,
'NOTIFICATIONS_OFFSITE': 1,
'NOTIFICATIONS_SPECIFIC_WPD_COMMENTS': 1,
'MESSAGE_BLOCKED_USERS': 1,
'ADMIN_MOP_VISIBLE': 1,
@ -175,9 +175,9 @@ PERMS = { # Minimum admin_level to perform action.
'ENABLE_VOTE_BUTTONS_ON_USER_PAGE': 1,
'NOTIFICATIONS_MODERATOR_ACTIONS': 1,
'EXEMPT_FROM_IP_LOGGING': 1,
'USER_BADGES': 1,
'IS_PERMA_PROGSTACKED': 2,
'USER_BADGES': 2,
'USER_LINK': 2,
'USER_CHANGE_FLAIR': 2,
'LOTTERY_VIEW_PARTICIPANTS': 2,
@ -316,10 +316,10 @@ if SITE_NAME == 'rDrama':
'atheism',
}
REDDIT_NOTIFS_SITE.update({'marsey', 'r/drama', 'justice4darrell', 'cringetopia.org'})
OFFSITE_NOTIF_QUERIES.update({'marsey', 'r/drama', 'justice4darrell', 'cringetopia.org'})
elif SITE_NAME == 'WPD':
REDDIT_NOTIFS_SITE.update({'marsey', 'watchpeopledie', 'makemycoffin'})
OFFSITE_NOTIF_QUERIES.update({'marsey', 'watchpeopledie', 'makemycoffin'})
LONGPOSTBOT_REPLIES = (
@ -693,7 +693,9 @@ if SITE in {'rdrama.net', 'staging.rdrama.net'}:
IMMUNE_TO_NEGATIVE_AWARDS = {PIZZASHILL_ID, CARP_ID, 23629}
PINNED_POSTS_IDS = {
2424: 1, #pizzashill
PIZZASHILL_ID: 1,
28: 1, #Joan_Wayne_Gacy
10953: 1, #autodrama
864: 1, #RitalinRx
1096: 1, #xa15428
1357: 1, #_______________

View File

@ -14,7 +14,6 @@ import requests
import ffmpeg
import files.helpers.offsitementions as offsitementions
import files.helpers.stats as stats
import files.routes.static as route_static
from files.routes.front import frontlist
@ -27,6 +26,9 @@ from files.helpers.lottery import check_if_end_lottery_task
from files.helpers.roulette import spin_roulette_wheel
from files.helpers.sanitize import filter_emojis_only, sanitize
from files.helpers.useractions import *
from files.helpers.reddit_mentions import *
from files.helpers.lemmy_mentions import *
from files.cli import app, db_session, g
CRON_CACHE_TIMEOUT = 172800
@ -58,7 +60,10 @@ def cron_fn(every_5m, every_1d, every_1mo):
g.db.commit()
if not IS_LOCALHOST:
offsitementions.offsite_mentions_task(cache)
reddit_mentions_task()
g.db.commit()
lemmy_mentions_task()
g.db.commit()
if every_1d or (not cache.get('stats') and not IS_LOCALHOST):

View File

@ -326,15 +326,15 @@ def get_comments_v_properties(v, should_keep_func=None, *criterion):
def get_hole(hole, v=None, graceful=False):
if not hole:
if graceful: return None
else: abort(404)
else: abort(404, f"/h/{hole} was not found.")
hole = hole.replace('/h/', '').replace('h/', '').strip().lower()
if not hole:
if graceful: return None
else: abort(404)
else: abort(404, f"/h/{hole} was not found.")
hole = g.db.get(Hole, hole)
if not hole:
if graceful: return None
else: abort(404)
else: abort(404, f"/h/{hole} was not found.")
return hole
@cache.memoize(timeout=3600)

View File

@ -0,0 +1,56 @@
import requests
from flask import g
from files.helpers.config.const import *
from files.classes.comment import Comment
from files.helpers.sanitize import *
from files.helpers.alerts import push_notif
from files.classes.notifications import Notification
def lemmy_mentions_task():
for q in OFFSITE_NOTIF_QUERIES:
url = f'https://lemm.ee/api/v3/search?q={q}'
data = requests.get(url, headers=HEADERS, timeout=5).json()
for kind in ("post", "comment"):
for thing in data[f'{kind}s']:
creator = thing['creator']
author_string = f"[/u/{creator['name']}]({creator['actor_id']})"
thing = thing[kind]
if kind == 'comment':
body = thing["content"]
text = f'<blockquote><p>{body}</p></blockquote>'
else:
title = thing["name"]
text = f'<blockquote><p>{title}</p></blockquote>'
if thing["body"]:
selftext = thing["body"][:5000]
text += f'<br><blockquote><p>{selftext}</p></blockquote>'
if 'erdrama' in text: continue
permalink = thing['ap_id']
text = f'New site mention by {author_string}\n\n{permalink}\n\n{text}'
text = sanitize(text, blackjack="lemmy mention", golden=False)
existing_comment = g.db.query(Comment.id).filter_by(
author_id=AUTOJANNY_ID,
parent_post=None,
body_html=text).one_or_none()
if existing_comment: break
try: created_utc = int(time.mktime(time.strptime(thing['published'].split('.')[0], "%Y-%m-%dT%H:%M:%S")))
except: created_utc = int(time.mktime(time.strptime(thing['published'].split('.')[0], "%Y-%m-%dT%H:%M:%SZ")))
new_comment = Comment(
author_id=AUTOJANNY_ID,
parent_post=None,
body_html=text,
distinguished=True,
created_utc=created_utc,
)
g.db.add(new_comment)
g.db.flush()
new_comment.top_comment_id = new_comment.id

View File

@ -92,7 +92,7 @@ def process_audio(file, v, old=None):
extension = guess_extension(file.content_type)
if not extension:
os.remove(old)
abort(400)
abort(400, "Unsupported audio format.")
new = old + extension
try:
@ -101,7 +101,7 @@ def process_audio(file, v, old=None):
os.remove(old)
if os.path.isfile(new):
os.remove(new)
abort(400)
abort(400, "Something went wrong processing your audio on our end. Please try uploading it to https://pomf2.lain.la and post the link instead.")
os.remove(old)
@ -162,7 +162,7 @@ def process_video(file, v):
bitrate = int(video_info.get('bit_rate', 3000000))
except:
os.remove(old)
abort(400, "Something went wrong processing your video and it might be on our end. Please try uploading it to https://pomf2.lain.la and post the link instead.")
abort(400, "Something went wrong processing your video on our end. Please try uploading it to https://pomf2.lain.la and post the link instead.")
if codec != 'h264':
copyfile(old, new)
@ -177,7 +177,7 @@ def process_video(file, v):
os.remove(old)
if os.path.isfile(new):
os.remove(new)
abort(400)
abort(400, "Something went wrong processing your video on our end. Please try uploading it to https://pomf2.lain.la and post the link instead.")
os.remove(old)
@ -227,7 +227,7 @@ def process_image(filename, v, resize=0, trim=False, uploader_id=None):
except:
os.remove(filename)
if has_request and not filename.startswith('/chat_images/'):
abort(415)
abort(400, "Something went wrong processing your image on our end. Please try uploading it to https://pomf2.lain.la and post the link instead.")
return None
params.append(filename)

View File

@ -1,36 +1,23 @@
import time
import itertools
import requests
from flask_caching import Cache
from flask import g
from sqlalchemy import or_
import files.helpers.config.const as const
from files.classes.badges import Badge
from files.helpers.config.const import *
from files.classes.comment import Comment
from files.classes.user import User
from files.helpers.sanitize import *
from files.helpers.alerts import push_notif
from files.classes.notifications import Notification
# Note: while https://api.pushshift.io/meta provides the key
# server_ratelimit_per_minute, in practice Cloudflare puts stricter,
# unofficially documented limits at around 60/minute. We get nowhere near this
# with current keyword quantities. If this ever changes, consider reading the
# value from /meta (or just guessing) and doing a random selection of keywords.
def offsite_mentions_task(cache):
site_mentions = get_mentions(cache, const.REDDIT_NOTIFS_SITE)
def reddit_mentions_task():
site_mentions = get_mentions(OFFSITE_NOTIF_QUERIES)
notify_mentions(site_mentions)
if const.REDDIT_NOTIFS_USERS:
for query, send_user in const.REDDIT_NOTIFS_USERS.items():
user_mentions = get_mentions(cache, [query], reddit_notifs_users=True)
if REDDIT_NOTIFS_USERS:
for query, send_user in REDDIT_NOTIFS_USERS.items():
user_mentions = get_mentions([query], reddit_notifs_users=True)
if user_mentions:
notify_mentions(user_mentions, send_to=send_user, mention_str='mention of you')
def get_mentions(cache, queries, reddit_notifs_users=False):
def get_mentions(queries, reddit_notifs_users=False):
mentions = []
for kind in ('submission', 'comment'):
q = " or ".join(queries)
@ -73,33 +60,32 @@ def notify_mentions(mentions, send_to=None, mention_str='site mention'):
for m in mentions:
author = m['author']
permalink = m['permalink']
text = sanitize(m['text'], blackjack="reddit mention", golden=False)
notif_text = (
text = (
f'<p>New {mention_str} by <a href="https://old.reddit.com/user/{author}" '
f'rel="nofollow noopener" target="_blank">/u/{author}</a></p>'
f'<p><a href="https://old.reddit.com{permalink}?context=89" '
f'<p><a href="https://old.reddit.com{permalink}?context=8" '
'rel="nofollow noopener" target="_blank">'
f'https://old.reddit.com{permalink}?context=89</a></p>'
f'{text}'
f'https://old.reddit.com{permalink}?context=8</a></p>'
f'{m["text"]}'
)
text = sanitize(text, blackjack="reddit mention", golden=False)
g.db.flush()
try:
existing_comment = g.db.query(Comment.id).filter_by(
author_id=const.AUTOJANNY_ID,
author_id=AUTOJANNY_ID,
parent_post=None,
body_html=notif_text).one_or_none()
body_html=text).one_or_none()
if existing_comment: break
# todo: handle this exception by removing one of the existing
# means that multiple rows were found, happens on new install for some reason
except:
pass
new_comment = Comment(
author_id=const.AUTOJANNY_ID,
author_id=AUTOJANNY_ID,
parent_post=None,
body_html=notif_text,
distinguish_level=6,
body_html=text,
distinguished=True,
created_utc=int(m['created_utc']),
)
g.db.add(new_comment)

View File

@ -211,7 +211,7 @@ tlds = ( # Original gTLDs and ccTLDs
'vu','wf','ws','xn','xxx','ye','yt','yu','za','zm','zw',
# New gTLDs
'app','cleaning','club','dev','farm','florist','fun','gay','lgbt','life','lol',
'moe','mom','monster','new','news','online','pics','press','pub','site','blog',
'moe','mom','monster','new','news','one','online','pics','press','pub','site','blog',
'vip','win','world','wtf','xyz','video','host','art','media','wiki','tech',
'cooking','network','party','goog','markets','today','beauty','camp','top',
'red','city','quest','works','soy','zone',

View File

@ -34,7 +34,7 @@ def create_comment_duplicated(text_html):
new_comment = Comment(author_id=AUTOJANNY_ID,
parent_post=None,
body_html=text_html,
distinguish_level=6,
distinguished=True,
is_bot=True)
g.db.add(new_comment)
g.db.flush()
@ -44,7 +44,6 @@ def create_comment_duplicated(text_html):
return new_comment.id
def send_repeatable_notification_duplicated(uid, text):
if uid in BOT_IDs: return
text_html = sanitize(text)
@ -742,8 +741,7 @@ def normalize_url(url):
if not url.startswith('/') and not url.startswith('https://rdrama.net') and not url.startswith('https://watchpeopledie.tv'):
try: parsed_url = urlparse(url)
except:
print(url, flush=True)
abort(500)
abort(400, f"Something is wrong with the url you submitted ({url}) and it couldn't be parsed.")
netloc = parsed_url.netloc
path = parsed_url.path

View File

@ -0,0 +1,14 @@
from typing import Annotated
from sqlalchemy.sql.schema import ForeignKey
from sqlalchemy.orm import mapped_column
int_pk = Annotated[int, mapped_column(primary_key=True)]
str_pk = Annotated[str, mapped_column(primary_key=True)]
user_id_fk = Annotated[int, mapped_column(ForeignKey("users.id"))]
user_id_fk_pk = Annotated[int, mapped_column(ForeignKey("users.id"), primary_key=True)]
post_id_fk = Annotated[int, mapped_column(ForeignKey("posts.id"))]
post_id_fk_pk = Annotated[int, mapped_column(ForeignKey("posts.id"), primary_key=True)]
comment_id_fk = Annotated[int, mapped_column(ForeignKey("comments.id"))]
comment_id_fk_pk = Annotated[int, mapped_column(ForeignKey("comments.id"), primary_key=True)]

View File

@ -27,4 +27,4 @@ def badge_grant(user, badge_id, description=None, url=None, notify=True):
if notify:
send_repeatable_notification(user.id,
"@AutoJanny has given you the following profile badge:\n\n" +
f"{badge.path}\n\n**{badge.name}**\n\n{badge.badge.description}")
f"{badge.path}\n\n**{badge.name}**\n\n{badge.badge.description}").created_utc += 1

View File

@ -135,7 +135,7 @@ def remove_admin(v, username):
user = get_user(username)
if user.admin_level > v.admin_level:
abort(403)
abort(403, "You can't remove an admin with higher level than you.")
if user.admin_level:
user.admin_level = 0
@ -164,7 +164,8 @@ def distribute(v, kind, option_id):
option = g.db.get(cls, option_id)
if option.exclusive != 2: abort(403)
if option.exclusive != 2:
abort(400, "This is not a bet.")
option.exclusive = 3
g.db.add(option)
@ -226,7 +227,7 @@ def revert_actions(v, username):
revertee = get_user(username)
if revertee.admin_level > v.admin_level:
abort(403)
abort(403, "You can't revert the actions of an admin with higher level that you.")
ma = ModAction(
kind="revert",
@ -341,7 +342,7 @@ def reported_posts(v):
is_approved=None,
is_banned=False,
deleted_utc=0
).join(Post.reports)
).join(Post.reports).join(User, User.id == Report.user_id).filter(User.shadowbanned == None, User.is_muted == False)
total = listing.count()
@ -364,7 +365,7 @@ def reported_comments(v):
is_approved=None,
is_banned=False,
deleted_utc=0
).join(Comment.reports)
).join(Comment.reports).join(User, User.id == CommentReport.user_id).filter(User.shadowbanned == None, User.is_muted == False)
total = listing.count()
@ -485,7 +486,7 @@ def badge_grant_post(v):
user = get_user(username)
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
except: abort(400, "Invalid badge id.")
if badge_id not in [b.id for b in badges]:
abort(403, "You can't grant this badge!")
@ -497,7 +498,7 @@ def badge_grant_post(v):
abort(400, "This badge requires a url!")
if url:
if '\\' in url: abort(400)
if '\\' in url: abort(400, "Nice try nigger.")
if url.startswith(f'{SITE_FULL}/'):
url = url.split(SITE_FULL, 1)[1]
else:
@ -563,10 +564,10 @@ def badge_remove_post(v):
user = get_user(username)
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
except: abort(400, "Invalid badge id.")
if badge_id not in [b.id for b in badges]:
abort(403)
abort(403, "You're not allowed to remove this badge.")
badge = user.has_badge(badge_id)
if not badge: continue
@ -749,7 +750,7 @@ def admin_delink_relink_alt(v, username, other):
user2 = get_account(other)
ids = [user1.id, user2.id]
a = g.db.query(Alt).filter(Alt.user1.in_(ids), Alt.user2.in_(ids)).one_or_none()
if not a: abort(404)
if not a: abort(404, "Alt doesn't exist.")
g.db.delete(a)
cache.delete_memoized(get_alt_graph_ids, user1.id)
@ -869,7 +870,7 @@ def unchud(fullname, v):
def shadowban(user_id, v):
user = get_account(user_id)
if user.admin_level > v.admin_level:
abort(403)
abort(403, "You can't shadowban an admin with higher level than you.")
user.shadowbanned = v.id
reason = request.values.get("reason", "").strip()
@ -1000,7 +1001,7 @@ def ban_user(fullname, v):
user = get_account(fullname)
if user.admin_level > v.admin_level:
abort(403)
abort(403, "You can't ban an admin with higher level than you.")
if user.is_permabanned:
abort(403, f"@{user.username} is already banned permanently!")
@ -1442,36 +1443,6 @@ def approve_post(post_id, v):
return {"message": "Post approved!"}
@app.post("/distinguish/<int:post_id>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@admin_level_required(PERMS['POST_COMMENT_DISTINGUISH'])
def distinguish_post(post_id, v):
post = get_post(post_id)
if post.distinguish_level:
post.distinguish_level = 0
kind = 'undistinguish_post'
else:
post.distinguish_level = v.admin_level
kind = 'distinguish_post'
g.db.add(post)
ma = ModAction(
kind=kind,
user_id=v.id,
target_post_id=post.id
)
g.db.add(ma)
if post.distinguish_level: return {"message": "Post distinguished!"}
else: return {"message": "Post undistinguished!"}
@app.post("/sticky/<int:post_id>")
@feature_required('PINS')
@limiter.limit('1/second', scope=rpath)
@ -1676,36 +1647,6 @@ def approve_comment(c_id, v):
return {"message": "Comment approved!"}
@app.post("/distinguish_comment/<int:c_id>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@admin_level_required(PERMS['POST_COMMENT_DISTINGUISH'])
def admin_distinguish_comment(c_id, v):
comment = get_comment(c_id, v=v)
if comment.distinguish_level:
comment.distinguish_level = 0
kind = 'undistinguish_comment'
else:
comment.distinguish_level = v.admin_level
kind = 'distinguish_comment'
g.db.add(comment)
ma = ModAction(
kind=kind,
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if comment.distinguish_level: return {"message": "Comment distinguished!"}
else: return {"message": "Comment undistinguished!"}
@app.get("/admin/banned_domains/")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)

View File

@ -173,16 +173,18 @@ def approve_emoji(v, name):
nsfw = request.values.get("nsfw") == 'true'
author = request.values.get('author').strip()
author = get_user(author)
old_name = emoji.name
emoji.name = new_name
emoji.kind = new_kind
emoji.tags = tags
emoji.nsfw = nsfw
emoji.author_id = author.id
g.db.add(emoji)
author = get_account(emoji.author_id)
if emoji.kind == "Marsey":
all_by_author = g.db.query(Emoji).filter_by(kind="Marsey", author_id=author.id).count()

View File

@ -205,7 +205,7 @@ def award_thing(v, thing_type, id):
if isinstance(obj, Post) and obj.id == 210983:
abort(403, "You can't award this post!")
if isinstance(obj, Post) and obj.distinguish_level and AWARDS[kind]['cosmetic']:
if isinstance(obj, Post) and obj.distinguished and AWARDS[kind]['cosmetic']:
abort(403, "Distinguished posts are immune to cosmetic awards!")
if kind == "benefactor":

View File

@ -326,7 +326,7 @@ def comment(v):
c_jannied = Comment(author_id=AUTOJANNY_ID,
parent_post=post_target.id if posting_to_post else None,
wall_user_id=post_target.id if not posting_to_post else None,
distinguish_level=6,
distinguished=True,
parent_comment_id=c.id,
level=level+1,
is_bot=True,
@ -611,7 +611,7 @@ def diff_words(answer, guess):
def toggle_comment_nsfw(cid, v):
comment = get_comment(cid)
if comment.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (comment.post and comment.post.hole and v.mods_hole(comment.post.hole)) and comment.wall_user_id != v.id:
if comment.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (comment.post and v.mods_hole(comment.post.hole)) and comment.wall_user_id != v.id:
abort(403)
comment.nsfw = not comment.nsfw
@ -625,7 +625,7 @@ def toggle_comment_nsfw(cid, v):
target_comment_id = comment.id,
)
g.db.add(ma)
elif comment.post and comment.post.hole and v.mods_hole(comment.post.hole):
elif comment.post and v.mods_hole(comment.post.hole):
ma = HoleAction(
hole = comment.post.hole,
kind = "set_nsfw_comment" if comment.nsfw else "unset_nsfw_comment",
@ -782,3 +782,36 @@ def postprocess_comment(comment_body, comment_body_html, cid):
g.db.close()
stdout.flush()
@app.post("/distinguish_comment/<int:c_id>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def admin_distinguish_comment(c_id, v):
comment = get_comment(c_id, v=v)
if v.admin_level < PERMS['POST_COMMENT_DISTINGUISH'] and not (comment.parent_post and v.mods_hole(comment.post.hole)):
abort(403, "You can't distinguish this comment")
if comment.distinguished:
comment.distinguished = False
kind = 'undistinguish_comment'
else:
comment.distinguished = True
kind = 'distinguish_comment'
g.db.add(comment)
ma = ModAction(
kind=kind,
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if comment.distinguished: return {"message": "Comment distinguished!"}
else: return {"message": "Comment undistinguished!"}

View File

@ -901,7 +901,7 @@ def pin_comment_mod(cid, v):
comment = get_comment(cid, v=v)
if not comment.stickied:
if not (comment.post.hole and v.mods_hole(comment.post.hole)): abort(403)
if not v.mods_hole(comment.post.hole): abort(403)
comment.stickied = v.username + " (Mod)"
@ -934,7 +934,7 @@ def unpin_comment_mod(cid, v):
comment = get_comment(cid, v=v)
if comment.stickied:
if not (comment.post.hole and v.mods_hole(comment.post.hole)): abort(403)
if not v.mods_hole(comment.post.hole): abort(403)
comment.stickied = None
comment.stickied_utc = None

View File

@ -20,6 +20,8 @@ from files.helpers.settings import *
from files.helpers.cloudflare import *
from files.helpers.sorting_and_time import make_age_string
from files.helpers.can_see import *
from files.helpers.alerts import send_notification
from files.helpers.useractions import *
from files.routes.routehelpers import get_alt_graph, get_formkey
from files.routes.wrappers import calc_users
from files.__main__ import app, cache
@ -135,12 +137,21 @@ def poster_of_the_day_id():
db = db_session()
uid = db.query(User.id, func.sum(Post.upvotes)).join(Post, Post.author_id == User.id).filter(
user = db.query(User, func.sum(Post.upvotes)).join(Post, Post.author_id == User.id).filter(
Post.created_utc > t,
User.admin_level == 0,
).group_by(User.id).order_by(func.sum(Post.upvotes).desc()).first()[0]
).group_by(User).order_by(func.sum(Post.upvotes).desc()).one_or_none()
db.rollback()
if not user: return SNAPPY_ID
user = user[0]
t = datetime.datetime.now()
send_notification(user.id, f":marseyjam: You're the Top Poster of the Day for the day of {t.year}-{t.month}-{t.day} :marseyjam:")
badge_grant(badge_id=327, user=user)
uid = user.id
db.commit()
db.close()
return uid

View File

@ -323,7 +323,7 @@ def sign_up_post(v):
new_user.admin_level = 5
new_user.coins = 100000000
new_user.marseybux = 100000000
del session["history"]
session.pop("history", None)
if ref_id and ref_id not in session.get("history", []):
ref_user = get_account(ref_id)

View File

@ -32,7 +32,7 @@ def clear(v):
v.last_viewed_modmail_notifs = int(time.time())
v.last_viewed_post_notifs = int(time.time())
v.last_viewed_log_notifs = int(time.time())
v.last_viewed_reddit_notifs = int(time.time())
v.last_viewed_offsite_notifs = int(time.time())
g.db.add(v)
return {"message": "Notifications marked as read!"}
@ -268,17 +268,17 @@ def notifications_modactions(v):
@app.get("/notifications/reddit")
@app.get("/notifications/offsite")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def notifications_reddit(v):
def notifications_offsite(v):
page = get_page()
if not v.can_view_offsitementions: abort(403)
if not v.can_view_offsite_mentions: abort(403)
listing = g.db.query(Comment).filter(
Comment.body_html.like('%<p>New site mention%<a href="https://old.reddit.com/r/%'),
Comment.body_html.like('%<p>New site mention by <a href=%'),
Comment.parent_post == None,
Comment.author_id == AUTOJANNY_ID
)
@ -287,10 +287,10 @@ def notifications_reddit(v):
listing = listing.order_by(Comment.created_utc.desc()).offset(PAGE_SIZE*(page-1)).limit(PAGE_SIZE).all()
for ma in listing:
ma.unread = ma.created_utc > v.last_viewed_reddit_notifs
ma.unread = ma.created_utc > v.last_viewed_offsite_notifs
if not session.get("GLOBAL") and not request.values.get('nr'):
v.last_viewed_reddit_notifs = int(time.time())
v.last_viewed_offsite_notifs = int(time.time())
g.db.add(v)
if v.client: return {"data":[x.json for x in listing]}

View File

@ -674,7 +674,7 @@ def submit_post(v, hole=None):
nsfw=False,
is_bot=True,
app_id=None,
distinguish_level=6,
distinguished=True,
body=body,
body_html=body_jannied_html,
ghost=p.ghost
@ -795,7 +795,7 @@ def undelete_post_pid(pid, v):
def mark_post_nsfw(pid, v):
p = get_post(pid)
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (p.hole and v.mods_hole(p.hole)):
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not v.mods_hole(p.hole):
abort(403)
p.nsfw = True
@ -834,7 +834,7 @@ def mark_post_nsfw(pid, v):
def unmark_post_nsfw(pid, v):
p = get_post(pid)
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (p.hole and v.mods_hole(p.hole)):
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not v.mods_hole(p.hole):
abort(403)
if p.nsfw and v.is_permabanned:
@ -1148,3 +1148,36 @@ if SITE_NAME == 'WPD':
send_repeatable_notification(p.author_id, f"@{v.username} (a site admin) has removed the child warning from [{p.title}](/post/{p.id})")
return {"message": "The child warning has been removed from the post!"}
@app.post("/distinguish/<int:post_id>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def distinguish_post(post_id, v):
post = get_post(post_id)
if v.admin_level < PERMS['POST_COMMENT_DISTINGUISH'] and not v.mods_hole(post.hole):
abort(403, "You can't distinguish this post")
if post.distinguished:
post.distinguished = False
kind = 'undistinguish_post'
else:
post.distinguished = True
kind = 'distinguish_post'
g.db.add(post)
ma = ModAction(
kind=kind,
user_id=v.id,
target_post_id=post.id
)
g.db.add(ma)
if post.distinguished: return {"message": "Post distinguished!"}
else: return {"message": "Post undistinguished!"}

View File

@ -31,7 +31,7 @@ def report_post(pid, v):
if len(reason_html) > 350:
abort(400, "Rendered report reason is too long!")
if reason.startswith('!') and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or post.hole and v.mods_hole(post.hole)):
if reason.startswith('!') and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.mods_hole(post.hole)):
post.flair = reason_html[1:]
g.db.add(post)
if v.admin_level >= PERMS['POST_COMMENT_MODERATION']:

View File

@ -52,7 +52,7 @@ def get_alt_graph(uid):
def add_alt(user1, user2):
if user1 in {1375580,14713} or user2 in {1375580,14713}:
del session["history"]
session.pop("history", None)
if session.get("GLOBAL"):
return

View File

@ -3,6 +3,7 @@ import time
from calendar import timegm
from sqlalchemy.orm import load_only
from sqlalchemy import select
from files.helpers.regex import *
from files.helpers.sorting_and_time import *
@ -17,7 +18,6 @@ valid_params = [
'post',
'before',
'after',
'exact',
'title',
'sentto',
'hole',
@ -228,16 +228,16 @@ def searchcomments(v):
else: comments = comments.filter(Comment.author_id == author.id)
if 'q' in criteria:
tokens = map(lambda x: search_regex_1.sub('', x), criteria['q'])
tokens = filter(lambda x: len(x) > 0, tokens)
tokens = map(lambda x: search_regex_2.sub("\\'", x), tokens)
tokens = map(lambda x: x.strip(), tokens)
tokens = map(lambda x: search_regex_3.sub(' <-> ', x), tokens)
comments = comments.filter(Comment.body_ts.match(
' & '.join(tokens),
postgresql_regconfig='english'))
text = criteria['full_text']
comments = comments.filter(
Comment.body_ts.bool_op("@@")(
func.websearch_to_tsquery("english", text)
)
)
if 'nsfw' in criteria: comments = comments.filter(Comment.nsfw == True)
if 'nsfw' in criteria:
nsfw = criteria['nsfw'].lower().strip() == 'true'
comments = comments.filter(Comment.nsfw == nsfw)
if 'hole' in criteria:
comments = comments.filter(Post.hole == criteria['hole'])
@ -327,14 +327,12 @@ def searchmessages(v):
else: comments = comments.filter(Comment.author_id == author.id)
if 'q' in criteria:
tokens = map(lambda x: search_regex_1.sub('', x), criteria['q'])
tokens = filter(lambda x: len(x) > 0, tokens)
tokens = map(lambda x: search_regex_2.sub("\\'", x), tokens)
tokens = map(lambda x: x.strip(), tokens)
tokens = map(lambda x: search_regex_3.sub(' <-> ', x), tokens)
comments = comments.filter(Comment.body_ts.match(
' & '.join(tokens),
postgresql_regconfig='english'))
text = criteria['full_text']
comments = comments.filter(
Comment.body_ts.bool_op("@@")(
func.websearch_to_tsquery("english", text)
)
)
comments = apply_time_filter(t, comments, Comment)

View File

@ -337,7 +337,8 @@ def settings_personal_post(v):
if len(bio_html) > BIO_FRIENDS_ENEMIES_HTML_LENGTH_LIMIT:
abort(400, "Your rendered bio is too long!")
v.bio_html=bio_html
v.bio = bio
v.bio_html = bio_html
g.db.add(v)
return {"message": "Your bio has been updated."}

View File

@ -31,7 +31,7 @@
<div class="user-info">
<span class="comment-collapse-icon" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
{% if standalone and c.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}
{% if c.is_banned %}Removed by @{{c.ban_reason}} (Site Admin){% elif c.deleted_utc %}Deleted by author{% endif %}
{% if c.is_banned %}Removed by @{{c.ban_reason}} (site admin){% elif c.deleted_utc %}Deleted by author{% endif %}
</div>
</div>
{% if render_replies %}
@ -153,8 +153,14 @@
<i id='pinned-{{c.id}}'class="fas fa-thumbtack fa-rotate--45 pr-1 text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned by @{{c.stickied}}" {% if c.stickied_utc %}data-onmouseover="pinned_timestamp('pinned-{{c.id}}')" data-timestamp={{c.stickied_utc}} data-nonce="{{g.nonce}}"{% endif %}></i>
{% endif %}
{% if c.distinguish_level and not c.ghost %}
<i class="fas fa-broom text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{SITE_NAME}} Admin, speaking officially"></i>
{% if c.distinguished and not c.ghost %}
<i class="fas fa-broom text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="
{%- if c.parent_post and c.author.mods_hole(c.post.hole) -%}
/h/{{c.post.hole}} mod
{%- else -%}
{{SITE_NAME}} admin
{%- endif -%}
, speaking officially"></i>
{% endif %}
{% if c.is_op %}
@ -174,7 +180,7 @@
{% endif %}
{% if c.ghost %}
<span {% if c.distinguish_level %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %}>👻</span>
<span {% if c.distinguished %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %}>👻</span>
{% else %}
{% if FEATURES['PATRON_ICONS'] and c.author.patron > 1 %}
<img loading="lazy" src="{{SITE_FULL_IMAGES}}/i/{{SITE_NAME}}/badges/2{{c.author.patron}}.webp?b=11" class="patron-img" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{c.author.patron_tooltip}}" alt="{{c.author.patron_tooltip}}">
@ -194,7 +200,7 @@
<img class="profile-pic-30-hat hat" loading="lazy" src="{{c.author.hat_active(v)[0]}}?x=7" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{c.author.hat_active(v)[1]}}" alt="@{{c.author.username}}'s hat">
{%- endif %}
</div>
<span {% if c.author.patron and not c.distinguish_level %}class="patron" style="background-color:#{{c.author.name_color}}"{% elif c.distinguish_level %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %} {% if c.author.pride_username(v) %}pride_username{% endif %}>{{c.author_name}}</span>
<span {% if c.author.patron and not c.distinguished %}class="patron" style="background-color:#{{c.author.name_color}}"{% elif c.distinguished %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %} {% if c.author.pride_username(v) %}pride_username{% endif %}>{{c.author_name}}</span>
</a>
{% if FEATURES['PRONOUNS'] %}
<span class="pronouns" style="color:#{{c.author.flaircolor}} !important;border-color:#{{c.author.flaircolor}} !important">{{c.author.pronouns_display}}</span>
@ -267,7 +273,7 @@
{{macros.reports(c, 'comment')}}
{% if c.is_banned %}
<div id="comment-banned-warning" class="comment-text text-removed mb-0">Removed by @{{c.ban_reason}} (Site Admin)</div>
<div id="comment-banned-warning" class="comment-text text-removed mb-0">Removed by @{{c.ban_reason}} (site admin)</div>
{% endif %}
{% set realbody = c.realbody(v) %}
@ -421,10 +427,10 @@
<a href="{{c.oauth_app.permalink}}/comments" class="dropdown-item list-inline-item d-mob-none text-primary"><i class="fas fa-code text-primary fa-fw"></i>API App</a>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.distinguish_level %}d-md-block{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Distinguish</button>
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] or (c.parent_post and v.mods_hole(c.post.hole)) %}
<button type="button" id="distinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.distinguished %}d-md-block{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Distinguish</button>
<button type="button" id="undistinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.distinguish_level %}d-md-block{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Undistinguish</button>
<button type="button" id="undistinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.distinguished %}d-md-block{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Undistinguish</button>
{% endif %}
{% if c.parent_post %}
@ -434,7 +440,7 @@
{% set url = "sticky_comment" %}
{% elif v.id == c.post.author_id %}
{% set url = "pin_comment" %}
{% elif c.post.hole and v.mods_hole(c.post.hole) %}
{% elif v.mods_hole(c.post.hole) %}
{% set url = "pin_comment_mod" %}
{% endif %}
@ -465,7 +471,7 @@
{% if c.parent_post %}
{% set hole = c.post.hole %}
{% if hole and v.mods_hole(hole) and not c.author.mods_hole(hole) %}
{% if v.mods_hole(hole) and not c.author.mods_hole(hole) %}
<button type="button" id="exile-{{c.id}}" class="d-none {% if not c.author.exiler_username(hole) %}d-md-block{% endif %} dropdown-item list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/exile/comment/{{c.id}}','exile-{{c.id}}','unexile-{{c.id}}','d-none')"><i class="fas fa-campfire text-danger fa-fw"></i>Exile user</button>
<button type="button" id="unexile-{{c.id}}" class="d-none {% if c.author.exiler_username(hole) %}d-md-block{% endif %} dropdown-item list-inline-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/h/{{hole}}/unexile/{{c.author_id}}','exile-{{c.id}}','unexile-{{c.id}}','d-none')"><i class="fas fa-campfire text-success fa-fw"></i>Unexile user</button>
{% endif %}
@ -476,7 +482,7 @@
<button type="button" id="unprogstack-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.is_approved == PROGSTACK_ID %}d-md-block{% endif %} text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/admin/unprogstack/comment/{{c.id}}','progstack-{{c.id}}','unprogstack-{{c.id}}','d-md-block')"><i class="fas fa-bullhorn text-danger fa-fw"></i>Remove Progressive Stack</button>
{% endif %}
{% if FEATURES['NSFW_MARKING'] and (c.parent_post or c.wall_user_id) and (c.author_id == v.id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (c.post.hole and v.mods_hole(c.post.hole))) or c.wall_user_id == v.id %}
{% if FEATURES['NSFW_MARKING'] and (c.parent_post or c.wall_user_id) and (c.author_id == v.id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.mods_hole(c.post.hole)) or c.wall_user_id == v.id %}
<button type="button" id="mark-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.nsfw %}d-md-block{% endif %} text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/toggle_comment_nsfw/{{c.id}}','mark-{{c.id}}','unmark-{{c.id}}','d-md-block')"><i class="fas fa-eye-evil text-danger fa-fw"></i>Mark NSFW</button>
<button type="button" id="unmark-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.nsfw %}d-md-block{% endif %} text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/toggle_comment_nsfw/{{c.id}}','mark-{{c.id}}','unmark-{{c.id}}','d-md-block')"><i class="fas fa-eye-evil text-success fa-fw"></i>Unmark NSFW</button>
{% endif %}
@ -634,17 +640,22 @@
{% endif %}
{% endif %}
{% if FEATURES['NSFW_MARKING'] and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and (c.author_id == v.id or (c.post.hole and v.mods_hole(c.post.hole))) or c.wall_user_id == v.id %}
{% if FEATURES['NSFW_MARKING'] and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and (c.author_id == v.id or v.mods_hole(c.post.hole)) or c.wall_user_id == v.id %}
<button type="button" id="mark2-{{c.id}}" class="{% if c.nsfw %}d-none{% endif %} list-group-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/toggle_comment_nsfw/{{c.id}}','mark2-{{c.id}}','unmark2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-danger mr-2"></i>Mark NSFW</button>
<button type="button" id="unmark2-{{c.id}}" class="{% if not c.nsfw %}d-none{% endif %} list-group-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/toggle_comment_nsfw/{{c.id}}','mark2-{{c.id}}','unmark2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-success mr-2"></i>Unmark NSFW</button>
{% endif %}
{% if v.admin_level < PERMS['POST_COMMENT_DISTINGUISH'] and v.mods_hole(c.post.hole) %}
<button type="button" id="distinguish2-{{c.id}}" class="list-group-item {% if c.distinguished %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Distinguish</button>
<button type="button" id="undistinguish2-{{c.id}}" class="list-group-item {% if not c.distinguished %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Undistinguish</button>
{% endif %}
{% if v.admin_level < PERMS['POST_COMMENT_MODERATION'] %}
{% if c.parent_post and v.id == c.post.author_id %}
<button type="button" id="pin2-{{c.id}}" class="list-group-item {% if c.stickied %}d-none{% endif %} text-info" data-bs-target="#actionsModal-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/pin_comment/{{c.id}}','pin2-{{c.id}}','unpin2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-info mr-2"></i>Pin</button>
<button type="button" id="unpin2-{{c.id}}" class="list-group-item {% if not c.stickied %}d-none{% endif %} text-info" data-bs-target="#actionsModal-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unpin_comment/{{c.id}}','pin2-{{c.id}}','unpin2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-info mr-2"></i>Unpin</button>
{% elif c.post.hole and v.mods_hole(c.post.hole) %}
{% elif v.mods_hole(c.post.hole) %}
<button type="button" id="pin2-{{c.id}}" class="list-group-item {% if c.stickied %}d-none{% endif %} text-info" data-bs-target="#actionsModal-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/pin_comment_mod/{{c.id}}','pin2-{{c.id}}','unpin2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-info mr-2"></i>Pin</button>
<button type="button" id="unpin2-{{c.id}}" class="list-group-item {% if not c.stickied %}d-none{% endif %} text-info" data-bs-target="#actionsModal-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unpin_comment_mod/{{c.id}}','pin2-{{c.id}}','unpin2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-info mr-2"></i>Unpin</button>
{% endif %}
@ -652,7 +663,7 @@
{% if c.parent_post %}
{% set hole = c.post.hole %}
{% if hole and v.mods_hole(hole) and not c.author.mods_hole(hole) %}
{% if v.mods_hole(hole) and not c.author.mods_hole(hole) %}
<button type="button" data-bs-dismiss="modal" id="exile2-{{c.id}}" class="{% if c.author.exiler_username(hole) %}d-none{% endif %} list-group-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/exile/comment/{{c.id}}','exile2-{{c.id}}','unexile2-{{c.id}}','d-none')"><i class="fas fa-campfire text-danger mr-2"></i>Exile user</button>
<button type="button" data-bs-dismiss="modal" id="unexile2-{{c.id}}" class="{% if not c.author.exiler_username(hole) %}d-none{% endif %} list-group-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/h/{{hole}}/unexile/{{c.author_id}}','exile2-{{c.id}}','unexile2-{{c.id}}','d-none')"><i class="fas fa-campfire text-success mr-2"></i>Unexile user</button>
{% endif %}
@ -688,8 +699,8 @@
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish2-{{c.id}}" class="list-group-item {% if c.distinguish_level %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Distinguish</button>
<button type="button" id="undistinguish2-{{c.id}}" class="list-group-item {% if not c.distinguish_level %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Undistinguish</button>
<button type="button" id="distinguish2-{{c.id}}" class="list-group-item {% if c.distinguished %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Distinguish</button>
<button type="button" id="undistinguish2-{{c.id}}" class="list-group-item {% if not c.distinguished %}d-none{% endif %} text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish_comment/{{c.id}}','distinguish2-{{c.id}}','undistinguish2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-id-badge text-info mr-2"></i>Undistinguish</button>
{% endif %}
<button type="button" id="pin2-{{c.id}}" class="list-group-item {% if c.stickied %}d-none{% endif %} text-info" data-bs-target="#adminModal-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/sticky_comment/{{c.id}}','pin2-{{c.id}}','unpin2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-info mr-2"></i>Pin</button>

View File

@ -38,10 +38,10 @@
</a>
</li>
{% endif %}
{% if v.can_view_offsitementions %}
{% if v.can_view_offsite_mentions %}
<li class="nav-item">
<a class="nav-link py-3{% if request.path == '/notifications/reddit' %} active{% endif %}" href="/notifications/reddit">
Reddit {% if v.reddit_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.reddit_notifications_count}})</span>{% endif %}
<a class="nav-link py-3{% if request.path == '/notifications/offsite' %} active{% endif %}" href="/notifications/offsite">
Offsite {% if v.offsite_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.offsite_notifications_count}})</span>{% endif %}
</a>
</li>
{% endif %}

View File

@ -175,7 +175,7 @@
{{p.realbody(v) | safe}}
{% if p.is_banned %}
<div class="text-removed mb-0">Removed by @{{p.ban_reason}} (Site Admin)</div>
<div class="text-removed mb-0">Removed by @{{p.ban_reason}} (site admin)</div>
{% endif %}
</div>
{% if not p.ghost %}

View File

@ -58,9 +58,9 @@
<button type="button" class="dropdown-item {% if not p.new %} d-none{% endif %} list-inline-item text-info" id="{{p.id}}-unsort-new" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this, '/post/{{p.id}}/hot', '{{p.id}}-unsort-new', '{{p.id}}-sort-new', 'd-none', 'null')"><i class="fas fa-fire text-center mr-2"></i>Set Default Sort Hot</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish-{{p.id}}" class="dropdown-item {% if p.distinguish_level %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Distinguish</button>
<button type="button" id="undistinguish-{{p.id}}" class="dropdown-item {% if not p.distinguish_level %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Undistinguish</button>
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] or v.mods_hole(p.hole) %}
<button type="button" id="distinguish-{{p.id}}" class="dropdown-item {% if p.distinguished %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Distinguish</button>
<button type="button" id="undistinguish-{{p.id}}" class="dropdown-item {% if not p.distinguished %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Undistinguish</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
@ -68,7 +68,7 @@
<button type="button" id="unpin-{{p.id}}" class="dropdown-item {% if not p.stickied %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="unpinPost(this, '{{p.id}}')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin</button>
{% endif %}
{% if p.hole and v.mods_hole(p.hole) %}
{% if v.mods_hole(p.hole) %}
<button type="button" id="hole-pin-{{p.id}}" class="dropdown-item {% if p.hole_pinned %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_pin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Pin to /h/{{p.hole}}</button>
<button type="button" id="hole-unpin-{{p.id}}" class="dropdown-item {% if not p.hole_pinned %}d-none{% endif %} list-inline-item text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_unpin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin from /h/{{p.hole}}</button>
{% endif %}
@ -83,7 +83,7 @@
<button type="button" id="unblock-{{p.id}}" class="dropdown-item text-success list-inline-item {% if not p.is_blocking %}d-none{% endif %}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unblock_user?username={{p.author_name}}','block-{{p.id}}','unblock-{{p.id}}','d-none')"><i class="fas fa-eye text-success"></i>Unblock user</button>
{% endif %}
{% if p.hole and v.mods_hole(p.hole) %}
{% if v.mods_hole(p.hole) %}
<button type="button" class="dropdown-item list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/kick/{{p.id}}')"><i class="fas fa-sign-out text-danger"></i>Kick</button>
{% if not p.author.mods_hole(p.hole) %}
<button type="button" id="exile-{{p.id}}" class="{% if p.author.exiler_username(p.hole) %}d-none{% endif %} dropdown-item list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/exile/post/{{p.id}}','exile-{{p.id}}','unexile-{{p.id}}','d-none')"><i class="fas fa-campfire text-danger"></i>Exile user</button>
@ -91,7 +91,7 @@
{% endif %}
{% endif %}
{% if (v.id == p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (p.hole and v.mods_hole(p.hole))) %}
{% if (v.id == p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.mods_hole(p.hole)) %}
{% if FEATURES['NSFW_MARKING'] %}
<button type="button" id="mark-{{p.id}}" class="dropdown-item {% if p.nsfw %}d-none{% endif %} list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Mark NSFW</button>
<button type="button" id="unmark-{{p.id}}" class="dropdown-item {% if not p.nsfw %}d-none{% endif %} list-inline-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Unmark NSFW</button>

View File

@ -35,11 +35,6 @@
<button type="button" id="unpin-profile2-{{p.id}}" class="{% if not p.is_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-muted text-left" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/pin/{{p.id}}','pin-profile2-{{p.id}}','unpin-profile2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center mr-2"></i>Unpin from profile</button>
{% endif %}
{% if p.hole and v.mods_hole(p.hole) %}
<button type="button" id="hole-pin2-{{p.id}}" class="{% if p.hole_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_pin/{{p.id}}','hole-pin2-{{p.id}}','hole-unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-info mr-2"></i>Pin to /h/{{p.hole}}</button>
<button type="button" id="hole-unpin2-{{p.id}}" class="{% if not p.hole_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_unpin/{{p.id}}','hole-pin2-{{p.id}}','hole-unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-info mr-2"></i>Unpin from /h/{{p.hole}}</button>
{% endif %}
{% if v.id == p.author_id %}
<button type="button" id="delete-{{p.id}}" class="{% if p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" data-nonce="{{g.nonce}}" data-onclick="delete_postModal('{{p.id}}')"><i class="fas fa-trash-alt mr-2"></i>Delete</button>
<button type="button" id="undelete-{{p.id}}" class="{% if not p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/undelete_post/{{p.id}}', 'delete-{{p.id}}', 'undelete-{{p.id}}','d-none')" data-toggleelement="#post-{{p.id}}" data-toggleattr="deleted" data-bs-dismiss="modal"><i class="fas fa-trash-alt text-center mr-2"></i>Undelete</button>
@ -54,7 +49,7 @@
{% endif %}
{% endif %}
{% if v.id == p.author_id or (p.hole and v.mods_hole(p.hole)) %}
{% if v.id == p.author_id or v.mods_hole(p.hole) %}
{% if FEATURES['NSFW_MARKING'] %}
<button type="button" id="mark3-{{p.id}}" class="{% if p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_nsfw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Mark NSFW</button>
<button type="button" id="unmark3-{{p.id}}" class="{% if not p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_nsfw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Unmark NSFW</button>
@ -66,7 +61,16 @@
{% endif %}
{% endif %}
{% if p.hole and v.mods_hole(p.hole) %}
{% if v.mods_hole(p.hole) %}
{% if v.admin_level < PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish2-{{p.id}}" class="{% if p.distinguished %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Distinguish</button>
<button type="button" id="undistinguish2-{{p.id}}" class="{% if not p.distinguished %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Undistinguish</button>
{% endif %}
<button type="button" id="hole-pin2-{{p.id}}" class="{% if p.hole_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_pin/{{p.id}}','hole-pin2-{{p.id}}','hole-unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-info mr-2"></i>Pin to /h/{{p.hole}}</button>
<button type="button" id="hole-unpin2-{{p.id}}" class="{% if not p.hole_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-info" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/hole_unpin/{{p.id}}','hole-pin2-{{p.id}}','hole-unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-info mr-2"></i>Unpin from /h/{{p.hole}}</button>
<button type="button" data-bs-dismiss="modal" class="nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/kick/{{p.id}}')"><i class="fas fa-sign-out text-danger text-center mr-2"></i>Kick</button>
{% if not p.author.mods_hole(p.hole) %}

View File

@ -23,9 +23,9 @@
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish2-{{p.id}}" class="{% if p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Distinguish</button>
<button type="button" id="distinguish2-{{p.id}}" class="{% if p.distinguished %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Distinguish</button>
<button type="button" id="undistinguish2-{{p.id}}" class="{% if not p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Undistinguish</button>
<button type="button" id="undistinguish2-{{p.id}}" class="{% if not p.distinguished %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Undistinguish</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}

View File

@ -19,9 +19,9 @@
<div class="col-12">
<div id="post-{{p.id}}" class="banned {% if p.award_count('glowie', v) %}glow{% endif %} card d-flex flex-row-reverse flex-nowrap justify-content-end border-0 p-0 {% if voted == 1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
<div class="card-block my-md-auto">
<div class="post-meta text-left d-md-none mb-1">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
<div class="post-meta text-left d-md-none mb-1">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (site admin){% else %}[Deleted by user]{% endif %}</div>
<h5 class="card-title post-title text-left mb-0 mb-md-1">{{p.plaintitle(v)}}</h5>
<div class="post-meta text-left d-mob-none">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
<div class="post-meta text-left d-mob-none">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (site admin){% else %}[Deleted by user]{% endif %}</div>
</div>
<div id="voting" class="d-md-block my-auto mr-3 text-center">
<div class="post-{{p.id}}-up arrow-up mx-auto"></div>

View File

@ -1,6 +1,5 @@
<h3 style="filter: sepia(100%) saturate(500%) hue-rotate(60deg) drop-shadow(-4px 4px 10px lime);">CURRENT EVENTS:</h3>
<h4 style="filter: sepia(50%) saturate(500%) hue-rotate(10deg) drop-shadow(-1px 1px 5px hotpink);"><a href="/post/213373/the-rdrama-marseysex-project" rel="nofollow" target="_blank">rDrama Marseydex Project</a></h4><sup>8 badges, 500dc per emoji, no limit</sup><br>
<h4 style="filter: sepia(50%) saturate(500%) hue-rotate(10deg) drop-shadow(-1px 1px 5px hotpink);"><a href="/post/155945/platyrich-marseybux-opportunity-real-platyrich" rel="nofollow" target="_blank">Find Rightoid Drama</a></h4><sup>500 mbux per post, no limit</sup><br>
<h4 style="filter: sepia(50%) saturate(500%) hue-rotate(10deg) drop-shadow(-1px 1px 5px hotpink);"><a href="/post/155945/platyrich-marseybux-opportunity-real-platyrich" target="_blank" rel="nofollow">Find Rightoid Drama</a></h4><sup>500 mbux per post, no limit</sup><br>
<hr>
<hr>
<hr>
@ -10,7 +9,7 @@
<ul>
<li><p>Asking to see who saved comments/posts=1 day ban</p></li>
<li><p>You must be 18 or older to view this site.</p></li>
<li><p><strong><a href="/post/19711/a-short-guide-on-how-to" rel="nofollow" target="_blank">NO RIGHTWING AGENDAPOSTING.</a></strong></p></li>
<li><p><strong><a href="/post/19711/a-short-guide-on-how-to" target="_blank" rel="nofollow">NO RIGHTWING AGENDAPOSTING.</a></strong></p></li>
<li><p>Discord users will be banned on sight.</p></li>
<li><p>Don't post anything illegal.</p></li>
<li><p>No sexualizing minors, even as a joke. This includes cartoons.</p></li>

View File

@ -73,10 +73,6 @@
<div style="display: inline-block; width: 150px; text-align: center">Post Title Only:</div>
<button type="button" data-nonce="{{g.nonce}}" data-onclick="addParam(this, 'bool')" class="searchparam mb-1">title:true</button>
</div>
<div>
<div style="display: inline-block; width: 150px; text-align: center">Exact Match Only:</div>
<button type="button" data-nonce="{{g.nonce}}" data-onclick="addParam(this, 'bool')" class="searchparam mb-1">exact:true</button>
</div>
{% if SITE_NAME != 'WPD' %}
<div>
<div style="display: inline-block; width: 150px; text-align: center">Subreddit:</div>

View File

@ -1,7 +1,7 @@
<div class="col sidebar text-left {% if not request.path.startswith('/sidebar') %}d-none d-lg-block{% endif %} pt-2" {% if request.path != '/sidebar' %}id="sidebar-content"{% endif %}>
<h5 class="text-center mt-0 mt-md-2 mb-4">
Top poster of the day:
Top Poster of the Day:
<p>
{% with user=poster_of_the_day() %}
{% include "user_in_table.html" %}
@ -11,7 +11,7 @@
<a href="/users">
<h5 class="text-center mt-0 mt-md-2 mb-4">
Current registered users: {{current_registered_users()}}
Current Registered Users: {{current_registered_users()}}
</h5>
</a>

View File

@ -1,7 +1,7 @@
<div class="col sidebar text-left {% if not request.path.startswith('/sidebar') %}d-none d-lg-block{% endif %} pt-2" {% if request.path != '/sidebar' %}id="sidebar-content"{% endif %}>
<h5 class="text-center mt-0 mt-md-2 mb-4">
Top poster of the day:
Top Poster of the Day:
<p>
{% with user=poster_of_the_day() %}
{% include "user_in_table.html" %}
@ -11,7 +11,7 @@
<a href="/users">
<h5 class="text-center mt-0 mt-md-2 mb-4">
Current registered users: {{current_registered_users()}}
Current Registered Users: {{current_registered_users()}}
</h5>
</a>

View File

@ -38,7 +38,7 @@
<input autocomplete="off" type="text" id="name" class="form-control" name="name" maxlength="30" pattern='[a-zA-Z0-9]{1,30}' placeholder="Required" value="{{name}}" required>
<label class="mt-3" for="author">Author</label>
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}' placeholder="Required" value="{{username}}" required>
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" value="{{username}}" required>
<label class="mt-3" for="tags">Tags (must be separated by spaces)</label>
<input autocomplete="off" type="text" id="tags" class="form-control" name="tags" maxlength="200" pattern='[a-zA-Z0-9: ]{1,200}' placeholder="Required" value="{{tags}}" required>
@ -71,14 +71,14 @@
<div id="{{emoji.name}}-emoji" class="settings-section rounded">
<div class="d-lg-flex">
<div class="body w-lg-100">
<input hidden name="formkey" value="{{v|formkey}}">
<input hidden value="{{v|formkey}}">
<div><label class="mt-3">Image</label></div>
<img loading="lazy" src="{{SITE_FULL_IMAGES}}/asset_submissions/emojis/{{emoji.name}}.webp?s={{range(1, 10000000)|random}}" style="max-width:50%;border:5px white solid">
<div><label class="mt-3" for="{{emoji.name}}-kind">Kind</label></div>
<div class="input-group">
<select autocomplete="off" id='{{emoji.name}}-kind' class="form-control" name="kind" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}disabled readonly{% endif %}>
<select autocomplete="off" id='{{emoji.name}}-kind' class="form-control" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}disabled readonly{% endif %}>
{% for entry in EMOJI_KINDS %}
<option value="{{entry}}" {% if emoji.kind == entry %}selected{% endif %}>
{{entry}}
@ -91,23 +91,23 @@
<input autocomplete="off" type="text" id="{{emoji.name}}-submitter" class="form-control" maxlength="30" value="{{emoji.submitter}}" readonly>
<label class="mt-3" for="{{emoji.name}}-author">Author</label>
<input autocomplete="off" type="text" id="{{emoji.name}}-author" class="form-control" maxlength="30" value="{{emoji.author}}" readonly>
<input autocomplete="off" type="text" id="{{emoji.name}}-author" class="form-control" maxlength="30" value="{{emoji.author}}" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
<label class="mt-3" for="{{emoji.name}}-name">Emoji Name</label>
<input autocomplete="off" type="text" id="{{emoji.name}}-name" class="form-control" name="name" maxlength="30" value="{{emoji.name}}" pattern='emoji[a-z0-9]{1,24}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
<input autocomplete="off" type="text" id="{{emoji.name}}-name" class="form-control" maxlength="30" value="{{emoji.name}}" pattern='emoji[a-z0-9]{1,24}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
<label class="mt-3" for="{{emoji.name}}-tags">Tags</label>
<input autocomplete="off" type="text" id="{{emoji.name}}-tags" class="form-control" name="tags" maxlength="200" value="{{emoji.tags}}" pattern='[a-z0-9: ]{1,200}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
<input autocomplete="off" type="text" id="{{emoji.name}}-tags" class="form-control" maxlength="200" value="{{emoji.tags}}" pattern='[a-z0-9: ]{1,200}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
<div class="custom-control custom-checkbox mt-4 pt-1 ml-1">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="{{emoji.name}}-nsfw" name="nsfw" {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %} {% if emoji.nsfw %}checked{% endif %}>
<input autocomplete="off" type="checkbox" class="custom-control-input" id="{{emoji.name}}-nsfw" {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %} {% if emoji.nsfw %}checked{% endif %}>
<label class="custom-control-label" for="{{emoji.name}}-nsfw">NSFW</label>
</div>
</div>
</div>
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] or v.id == emoji.submitter_id %}
<div class="d-flex my-4 mx-3">
<input autocomplete="off" type="text" id="{{emoji.name}}-comment" class="form-control mr-4" name="comment" placeholder="Comment..." maxlength="500" {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}hidden{% endif %}>
<input autocomplete="off" type="text" id="{{emoji.name}}-comment" class="form-control mr-4" placeholder="Comment..." maxlength="500" {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}hidden{% endif %}>
<button type="button" class="btn btn-danger ml-auto" data-nonce="{{g.nonce}}" data-onclick="remove_emoji(this, '{{emoji.name}}')">Remove</button>
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}

View File

@ -51,7 +51,15 @@
<i id='hole-pinned-{{p.id}}' class="fas fa-thumbtack fa-rotate--45 pr-1 ml-1 mt-3 text-blue" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned to /h/{{p.hole}} by @{{p.hole_pinned}}"></i>
{% endif %}
{% if p.distinguish_level %}<i class="fas fa-broom text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{SITE_NAME}} Admin, speaking officially"></i>{% endif %}
{% if p.distinguished %}
<i class="fas fa-broom text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="
{%- if p.author.mods_hole(p.hole) -%}
/h/{{p.hole}} mod
{%- else -%}
{{SITE_NAME}} admin
{%- endif -%}
, speaking officially"></i>
{% endif %}
{% if p.is_pinned and request.path != '/' %}
<i class="fas fa-thumbtack fa-rotate--45 pr-1 ml-1 mt-3 text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned to profile"></i>
{% endif %}
@ -66,7 +74,7 @@
{% endif %}
{% if p.ghost %}
<span {% if p.distinguish_level %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %}>👻</span>
<span {% if p.distinguished %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %}>👻</span>
{% else %}
{% if FEATURES['PATRON_ICONS'] and p.author.patron > 1 %}
<img loading="lazy" src="{{SITE_FULL_IMAGES}}/i/{{SITE_NAME}}/badges/2{{p.author.patron}}.webp?b=11" class="patron-img" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{p.author.patron_tooltip}}" alt="{{p.author.patron_tooltip}}">
@ -85,7 +93,7 @@
<img class="profile-pic-30-hat hat" loading="lazy" src="{{p.author.hat_active(v)[0]}}?x=7" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{p.author.hat_active(v)[1]}}" alt="@{{p.author.username}}'s hat">
{%- endif %}
</div>
<span {% if p.author.patron and not p.distinguish_level %}class="patron" style="background-color:#{{p.author.name_color}}"{% elif p.distinguish_level %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %} {% if p.author.pride_username(v) %}pride_username{% endif %}>{{p.author_name}}</span>
<span {% if p.author.patron and not p.distinguished %}class="patron" style="background-color:#{{p.author.name_color}}"{% elif p.distinguished %}class="mod {% if SITE_NAME == 'rDrama' %}mod-rdrama{% endif %}"{% endif %} {% if p.author.pride_username(v) %}pride_username{% endif %}>{{p.author_name}}</span>
</a>
{% if FEATURES['PRONOUNS'] %}
<span class="pronouns" style="color:#{{p.author.flaircolor}} !important;border-color:#{{p.author.flaircolor}} !important">{{p.author.pronouns_display}}</span>

View File

@ -0,0 +1 @@
alter table users rename column last_viewed_reddit_notifs to last_viewed_offsite_notifs;

View File

@ -0,0 +1,9 @@
alter table posts add column distinguished bool default false not null;
update posts set distinguished=true where distinguish_level>0;
alter table comments add column distinguished bool default false not null;
update comments set distinguished=true where distinguish_level>0;
alter table posts alter column distinguished drop default;
alter table posts drop column distinguish_level;
alter table comments alter column distinguished drop default;
alter table comments drop column distinguish_level;

View File

@ -163,7 +163,7 @@ CREATE TABLE public.users (
rainbow integer,
spider integer,
profanityreplacer integer DEFAULT 1 NOT NULL,
last_viewed_reddit_notifs integer NOT NULL,
last_viewed_offsite_notifs integer NOT NULL,
profile_background character varying(167),
chudded_by integer,
blacklisted_by integer,
@ -426,7 +426,6 @@ CREATE TABLE public.comments (
created_utc integer NOT NULL,
parent_post integer,
is_banned boolean DEFAULT false NOT NULL,
distinguish_level integer DEFAULT 0 NOT NULL,
edited_utc integer DEFAULT 0 NOT NULL,
deleted_utc integer DEFAULT 0 NOT NULL,
is_approved integer,
@ -459,7 +458,8 @@ CREATE TABLE public.comments (
queened boolean NOT NULL,
sharpened boolean NOT NULL,
num_of_pinned_children integer NOT NULL,
body_ts tsvector GENERATED ALWAYS AS (to_tsvector('english'::regconfig, (body)::text)) STORED
body_ts tsvector GENERATED ALWAYS AS (to_tsvector('english'::regconfig, (body)::text)) STORED,
distinguished boolean NOT NULL
);
@ -834,7 +834,6 @@ CREATE TABLE public.posts (
created_utc integer NOT NULL,
is_banned boolean DEFAULT false NOT NULL,
nsfw boolean DEFAULT false NOT NULL,
distinguish_level integer DEFAULT 0 NOT NULL,
deleted_utc integer DEFAULT 0 NOT NULL,
is_approved integer,
edited_utc integer DEFAULT 0 NOT NULL,
@ -872,7 +871,8 @@ CREATE TABLE public.posts (
rainbowed boolean NOT NULL,
queened boolean NOT NULL,
sharpened boolean NOT NULL,
effortpost boolean NOT NULL
effortpost boolean NOT NULL,
distinguished boolean NOT NULL
);
@ -3073,4 +3073,3 @@ ALTER TABLE ONLY public.comments
--
-- PostgreSQL database dump complete
--

View File

@ -31,6 +31,7 @@ INSERT INTO public.badge_defs VALUES (319, 'Fistmas 23 Survivor', 'Awarded for s
INSERT INTO public.badge_defs VALUES (320, 'Best Marsey 2023', 'The creator of the best Marsey emote of 2023, as voted by rdrama users.', 1705457637);
INSERT INTO public.badge_defs VALUES (325, 'Best rdrama activist 2023', 'Went out there and touched grass for the sake of drama, and was voted the best rdrama activist of 2023.', 1705459943);
INSERT INTO public.badge_defs VALUES (326, 'Chud Hunter', 'Successfully reported wrongthink and got paid 🤑', 1707748016);
INSERT INTO public.badge_defs VALUES (327, 'Top Poster of the Day', 'Has been Top Poster of the Day at least once', 1708092535);
INSERT INTO public.badge_defs VALUES (296, 'SEX!', 'This user verifiably had sex with another dramatard.', 1692195588);
INSERT INTO public.badge_defs VALUES (297, 'Weather Balloon', 'This user was shot down over the territory of /r/UFOs.', 1694716945);
INSERT INTO public.badge_defs VALUES (180, 'Marsey Consoomer', 'Conned rDrama out of sick merch in exchange for a donation to Redbubble.', 1664417205);
@ -289,7 +290,7 @@ INSERT INTO public.badge_defs VALUES (87, 'Unblockable', 'This user is unblockab
-- Name: badge_defs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
--
SELECT pg_catalog.setval('public.badge_defs_id_seq', 326, true);
SELECT pg_catalog.setval('public.badge_defs_id_seq', 327, true);
--

View File

@ -611,6 +611,7 @@ INSERT INTO public.hat_defs VALUES (563, 'The Dundee', 'That''s not a hat. THIS
INSERT INTO public.hat_defs VALUES (566, 'Heart Crown (rainbow)', '❤🧡💛💚💙💜', 2, 500, NULL, 1662167687);
INSERT INTO public.hat_defs VALUES (565, 'Sparkles Crown', '✨✨✨', 2, 500, NULL, 1662167687);
INSERT INTO public.hat_defs VALUES (572, 'Heart Crown (sparkles)', '💕💛💔💗♥🖤✨💙 or something idk', 2, 500, NULL, 1662167687);
INSERT INTO public.hat_defs VALUES (1447, 'Pink bowtie', 'to show everyone your cute aesthetic!', 2, 500, NULL, 1708075863);
INSERT INTO public.hat_defs VALUES (574, 'Heart Crown (Rainbow II)', 'More gay than the other rainbow heart crown 👍', 2, 500, NULL, 1662167687);
INSERT INTO public.hat_defs VALUES (575, 'Heart Crown (superstraight)', '🖤🧡🖤🧡🖤🧡', 2, 500, NULL, 1662167687);
INSERT INTO public.hat_defs VALUES (567, 'Heart Crown (blue and purple)', '💙💜', 2, 500, NULL, 1662167687);
@ -1202,7 +1203,7 @@ INSERT INTO public.hat_defs VALUES (170, 'Kenny', 'User''s life insurance claim
-- Name: hat_defs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
--
SELECT pg_catalog.setval('public.hat_defs_id_seq', 1446, true);
SELECT pg_catalog.setval('public.hat_defs_id_seq', 1447, true);
--

View File

@ -2,7 +2,7 @@ INSERT INTO public.users (
username, passhash, created_utc, admin_level, email_verified,
original_username, defaultsorting, defaultsortingcomments, defaulttime, namecolor, flaircolor, theme, themecolor,
reddit, pronouns, verified, profileurl, highres,
marsify, last_viewed_modmail_notifs, last_viewed_post_notifs, last_viewed_log_notifs, last_viewed_reddit_notifs, lifetimedonated, lifetimedonated_visible, show_sigs, grinch
marsify, last_viewed_modmail_notifs, last_viewed_post_notifs, last_viewed_log_notifs, last_viewed_offsite_notifs, lifetimedonated, lifetimedonated_visible, show_sigs, grinch
) VALUES
('AutoJanny', '', extract(epoch from now()), 0, true,
'AutoJanny', 'hot', 'top', 'day', 'ff459a', 'ff459a', 'dark', 'ff459a',

View File

@ -3296,7 +3296,7 @@ You are one of the dumbest people I have ever seen, ever. Your reading comprehen
[para]
Can we get a new leaderbaord state for deleted posts/comments. because 999% of what i post gets deleted for some reason. but im a really good person and ive never done anything wrong And this is like that book i red called 1984 last week (My techer told me to read it but i onlt red like 10 pages 😂) but if you Idiots are going to delete my post atleast please give me something to Brag about. thanks
> Removed by @G-tix (Site Admin)
> Removed by @G-tix (site admin)
[para]
I'm a proud conservative American from Murica' but I have fallen in love with Colombia 🇨🇴 over the past couple of years. I had my wedding down there last summer and look forward to moving there next year for a few years on their new nomad visa.

View File

@ -0,0 +1,65 @@
from files.classes.user import User
from flask.ctx import _AppCtxGlobals
from sqlalchemy.orm import Session
from sqlalchemy.schema import Column
import typing as t
# According to PEP 484, "all objects imported into a stub using `from ... import *` are considered
# exported", but this didn't work with Pyright for some reason, so everything has to be exported
# manually. The alternative is to put `def __getattr__(name: str) -> t.Any: ...` here instead, but
# then any symbol not in this file will be treated as Any, which prevents it from being checked.
from flask import json as json
from flask.app import Flask as Flask
from flask.blueprints import Blueprint as Blueprint
from flask.config import Config as Config
from flask.ctx import after_this_request as after_this_request
from flask.ctx import copy_current_request_context as copy_current_request_context
from flask.ctx import has_app_context as has_app_context
from flask.ctx import has_request_context as has_request_context
from flask.globals import current_app as current_app
from flask.globals import request as request
from flask.globals import session as session
from flask.helpers import abort as abort
from flask.helpers import flash as flash
from flask.helpers import get_flashed_messages as get_flashed_messages
from flask.helpers import get_template_attribute as get_template_attribute
from flask.helpers import make_response as make_response
from flask.helpers import redirect as redirect
from flask.helpers import send_file as send_file
from flask.helpers import send_from_directory as send_from_directory
from flask.helpers import stream_with_context as stream_with_context
from flask.helpers import url_for as url_for
from flask.json import jsonify as jsonify
from flask.signals import appcontext_popped as appcontext_popped
from flask.signals import appcontext_pushed as appcontext_pushed
from flask.signals import appcontext_tearing_down as appcontext_tearing_down
from flask.signals import before_render_template as before_render_template
from flask.signals import got_request_exception as got_request_exception
from flask.signals import message_flashed as message_flashed
from flask.signals import request_finished as request_finished
from flask.signals import request_started as request_started
from flask.signals import request_tearing_down as request_tearing_down
from flask.signals import template_rendered as template_rendered
from flask.templating import render_template as render_template
from flask.templating import render_template_string as render_template_string
from flask.templating import stream_template as stream_template
from flask.templating import stream_template_string as stream_template_string
from flask.wrappers import Request as Request
from flask.wrappers import Response as Response
class Globals(_AppCtxGlobals):
agent: str
browser: t.Literal["webview", "firefox", "iphone", "mac", "chromium"]
db: Session
desires_auth: bool
is_api_or_xhr: bool
is_tor: bool
loggedin_chat: int
loggedin_counter: int
loggedout_counter: int
nonce: str
show_nsfw: bool
username: Column[str]
v: User | None
g: Globals