add private timeline and /v1/explore/feed

This commit is contained in:
Yusur 2026-02-26 11:09:43 +01:00
parent f2b75bf731
commit d8746f90ef
4 changed files with 67 additions and 8 deletions

View file

@ -26,7 +26,7 @@ from suou import twocolon_list, WantsContentType
from .colors import color_themes, theme_classes from .colors import color_themes, theme_classes
__version__ = '0.5.0-dev50' __version__ = '0.5.0-dev60'
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__)) APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@ -234,7 +234,7 @@ async def error_404(body):
except Exception as e: except Exception as e:
logger.error(f'Exception in find_guild_or_user: {e}') logger.error(f'Exception in find_guild_or_user: {e}')
pass pass
if app_config.server_name not in (None, request.host): if app_config.server_name not in (None, request.host) and app_config.force_server_name:
logger.warning(f'request host {request.host!r} is different from configured server name {app_config.server_name!r}') logger.warning(f'request host {request.host!r} is different from configured server name {app_config.server_name!r}')
if request.referrer: if request.referrer:
logger.warning(f'(referrer is {request.referrer!r}') logger.warning(f'(referrer is {request.referrer!r}')

View file

@ -1,12 +1,14 @@
from __future__ import annotations
from freak.accounts import UserLoader
from quart_auth import current_user from quart_auth import current_user
from sqlalchemy import and_, distinct, func, select from sqlalchemy import and_, distinct, func, or_, select
from suou import not_implemented from suou import not_implemented
from .models import Comment, Member, Post, Guild, User from .models import Comment, Member, Post, Guild, User
current_user: UserLoader
def cuser() -> User: def cuser() -> User:
return current_user.user if current_user else None return current_user.user if current_user else None
@ -19,6 +21,21 @@ def public_timeline():
Post.privacy == 0, User.not_suspended(), Post.not_removed(), User.has_not_blocked(Post.author_id, cuser_id()) Post.privacy == 0, User.not_suspended(), Post.not_removed(), User.has_not_blocked(Post.author_id, cuser_id())
).order_by(Post.created_at.desc()) ).order_by(Post.created_at.desc())
def private_timeline(cuser: User):
return select(Post).join(User, User.id == Post.author_id).join(Guild, Guild.id == Post.topic_id
).join(Member, Member.guild_id == Guild.id, isouter = True
##).join(Friendship
).where(
or_(
Member.user_id == cuser_id(),
##Friendship.,
),
User.not_suspended(), Post.not_removed(), User.has_not_blocked(Post.author_id, cuser_id()),
or_(Post.privacy == 0, Post.privacy == 1,
##and_(Post.privacy == 2, Friendsip.)
)
).order_by(Post.created_at.desc())
def topic_timeline(gname): def topic_timeline(gname):
return select(Post).join(Guild, Guild.id == Post.topic_id).join(User, User.id == Post.author_id).where( return select(Post).join(Guild, Guild.id == Post.topic_id).join(User, User.id == Post.author_id).where(
Post.privacy == 0, Guild.name == gname, User.not_suspended(), Post.not_removed(), User.has_not_blocked(Post.author_id, cuser_id()) Post.privacy == 0, Guild.name == gname, User.not_suspended(), Post.not_removed(), User.has_not_blocked(Post.author_id, cuser_id())

View file

@ -89,6 +89,7 @@ ILLEGAL_USERNAMES = tuple((
## VVVVIP ## VVVVIP
'potus realdonaldtrump elonmusk teddysphotos mrbeast jkrowling pewdiepie ' 'potus realdonaldtrump elonmusk teddysphotos mrbeast jkrowling pewdiepie '
'elizabethii elizabeth2 king queen pontifex hogwarts lumos alohomora isis daesh retards ' 'elizabethii elizabeth2 king queen pontifex hogwarts lumos alohomora isis daesh retards '
'charliekirk '
).split()) ).split())
def username_is_legal(username: str) -> bool: def username_is_legal(username: str) -> bool:
@ -303,11 +304,11 @@ class User(Base):
Remove any relationship between two users. Remove any relationship between two users.
Executed before block. Executed before block.
""" """
# TODO implement in 0.5 # TODO implement in 0.6
... ...
def has_subscriber(self, other: User) -> bool: def has_subscriber(self, other: User) -> bool:
# TODO implement in 0.5 # TODO implement in 0.6
return False #bool(session.execute(select(Friendship).where(...)).scalar()) return False #bool(session.execute(select(Friendship).where(...)).scalar())
@classmethod @classmethod

View file

@ -19,7 +19,7 @@ from werkzeug.security import check_password_hash
from suou.quart import add_rest from suou.quart import add_rest
from freak.accounts import LoginStatus, check_login from freak.accounts import LoginStatus, check_login
from freak.algorithms import public_timeline, top_guilds_query, topic_timeline, user_timeline from freak.algorithms import private_timeline, public_timeline, top_guilds_query, topic_timeline, user_timeline
from freak.search import SearchQuery from freak.search import SearchQuery
from ..models import Comment, Guild, Post, PostUpvote, User, db from ..models import Comment, Guild, Post, PostUpvote, User, db
@ -276,6 +276,34 @@ async def guild_feed(gname: str):
return dict(guilds={f'{Snowflake(gu.id):l}': gj}, feed=feed) return dict(guilds={f'{Snowflake(gu.id):l}': gj}, feed=feed)
@bp.get('/guild/@<gname>/mods')
@login_required
async def guild_moderators(gname: str):
async with db as session:
gu: Guild | None = (await session.execute(select(Guild).where(Guild.name == gname))).scalar()
if gu is None:
return dict(error='Not found'), 404
obj = dict(
banned = False,
unmoderated = False,
has = []
)
if not gu.owner_id:
obj['unmoderated'] = True
# unmoderated
elif await gu.has_exiled(current_user.user):
obj['banned'] = True
obj['error'] = 'Moderator list is hidden because you are banned'
# TODO appeal button
else:
async for moder in gu.moderators():
mu = moder.user.simple_info()
mu['is_owner'] = moder.is_owner
obj['has'].append(mu)
return obj
## CREATE ## ## CREATE ##
@ -356,9 +384,22 @@ async def logout():
async def home_feed(): async def home_feed():
async with db as session: async with db as session:
me = current_user.user me = current_user.user
posts = await db.paginate(private_timeline(me))
feed = []
async for post in posts:
post: Post
feed.append(await post.feed_info_counts())
return dict(feed=feed)
@bp.get('/explore/feed')
@login_required
async def explore_feed():
async with db as session:
posts = await db.paginate(public_timeline()) posts = await db.paginate(public_timeline())
feed = [] feed = []
async for post in posts: async for post in posts:
post: Post
feed.append(await post.feed_info_counts()) feed.append(await post.feed_info_counts())
return dict(feed=feed) return dict(feed=feed)