refactor: many changes

- Add missing actions and make a clear boundary between actions,
  services and nfra (i.e: actions shouldn't use stuff from infra/)
- Delete stuff not in use: tts, gtts, etc
- Add a ton of tests
This commit is contained in:
cătălin 2025-03-05 11:34:44 +01:00
commit 152546982c
No known key found for this signature in database
46 changed files with 2328 additions and 700 deletions

View file

@ -1,17 +1,31 @@
from litestar import Request
from litestar.exceptions import HTTPException
from huesoporro.actions.authenticate import AuthenticateAction
from huesoporro.actions.get_user_by_jwt import GetUserByJWTAction
from huesoporro.actions.chatbot.create_or_update_chatbot import (
CreateOrUpdateChatbotAction,
)
from huesoporro.actions.chatbot.get_chatbot_by_user_id import GetChatbotByUserIdAction
from huesoporro.actions.users.authenticate_user import AuthenticateUserAction
from huesoporro.actions.users.get_user_by_jwt import GetUserByJWTAction
from huesoporro.infra.authenticator import TwitchAuthenticator
from huesoporro.infra.db import Database
from huesoporro.infra.repos import UserRepo
from huesoporro.infra.repos import ChatbotRepo, UserRepo
from huesoporro.libs.db import MarkovDatabase
from huesoporro.models import User
from huesoporro.models import Chatbot, User
from huesoporro.settings import Settings
from huesoporro.svc.get_chatbot_settings import ChatbotSettingsGetterSvc
from huesoporro.svc.chatbot_svcs import (
CreateChatbotSvc,
GetChatbotByUserIdSvc,
UpdateChatbotSvc,
)
from huesoporro.svc.store import SentenceStorerSvc
from huesoporro.svc.store_settings import ChatbotSettingsStorerSvc
from huesoporro.svc.users_svcs import (
CreateUserSvc,
GetTwitchAuthByAuthCodeSvc,
GetUserByUsernameSvc,
IsValidTokenSvc,
RefreshTokenSvc,
UpdateUserSvc,
)
def get_settings() -> Settings:
@ -22,14 +36,62 @@ def get_authenticator(s: Settings) -> TwitchAuthenticator:
return TwitchAuthenticator(s=s)
def get_db(s: Settings):
return Database(s=s)
def get_chatbot_repo(s: Settings):
return ChatbotRepo(s=s)
def get_get_chatbot_by_user_id_svc(chatbot_repo: ChatbotRepo):
return GetChatbotByUserIdSvc(repo=chatbot_repo)
def get_get_tokens_by_auth_code_svc(
twitch_authenticator: TwitchAuthenticator, s: Settings
):
return GetTwitchAuthByAuthCodeSvc(s=s, authenticator=twitch_authenticator)
def get_create_chatbot_svc(chatbot_repo: ChatbotRepo):
return CreateChatbotSvc(repo=chatbot_repo)
async def get_user_repo(s: Settings):
return UserRepo(s=s)
def get_create_user_svc(user_repo: UserRepo):
return CreateUserSvc(user_repo=user_repo)
def get_update_user_svc(user_repo: UserRepo):
return UpdateUserSvc(user_repo=user_repo)
def get_refresh_token_svc(twitch_authenticator: TwitchAuthenticator):
return RefreshTokenSvc(twitch_authenticator=twitch_authenticator)
def get_is_valid_token_svc(twitch_authenticator: TwitchAuthenticator):
return IsValidTokenSvc(authenticator=twitch_authenticator)
async def get_get_user_by_username_svc(user_repo: UserRepo):
return GetUserByUsernameSvc(user_repo=user_repo)
async def get_get_user_by_jwt_action(
user_repo: UserRepo, authenticator: TwitchAuthenticator, s: Settings
get_user_by_username_svc: GetUserByUsernameSvc,
update_user_svc: UpdateUserSvc,
is_valid_token_svc: IsValidTokenSvc,
refresh_token_svc: RefreshTokenSvc,
s: Settings,
):
return GetUserByJWTAction(user_repo=user_repo, authenticator=authenticator, s=s)
return GetUserByJWTAction(
get_user_by_username_svc=get_user_by_username_svc,
update_user_svc=update_user_svc,
refresh_token_svc=refresh_token_svc,
is_valid_token_svc=is_valid_token_svc,
s=s,
)
async def authenticate(
@ -37,32 +99,73 @@ async def authenticate(
) -> User:
token = request.query_params.get("huesoporro_token")
if token:
return await get_user_by_jwt_action.run(token)
user = await get_user_by_jwt_action.run(token)
if not user:
raise HTTPException(detail="User does not exist", status_code=404)
return user
cookies = request.cookies.get("huesoporroAuth")
if cookies:
return await get_user_by_jwt_action.run(cookies)
user = await get_user_by_jwt_action.run(cookies)
if not user:
raise HTTPException(detail="User does not exist", status_code=404)
return user
raise HTTPException(status_code=401, detail="Unauthorized")
async def get_chatbot_settings_svc(db: Database):
return ChatbotSettingsGetterSvc(db=db)
async def store_chatbot_settings_svc(db: Database):
return ChatbotSettingsStorerSvc(db=db)
async def get_sentences_storer_svc(db: MarkovDatabase):
return SentenceStorerSvc(db=db)
async def get_user_repo(s: Settings):
return UserRepo(s=s)
def get_update_chatbot_svc(chatbot_repo: ChatbotRepo):
return UpdateChatbotSvc(repo=chatbot_repo)
def get_create_or_update_chatbot_action(
create_chatbot_svc: CreateChatbotSvc,
update_chatbot_svc: UpdateChatbotSvc,
get_chatbot_by_user_id_svc: GetChatbotByUserIdSvc,
):
return CreateOrUpdateChatbotAction(
create_chatbot_svc=create_chatbot_svc,
update_chatbot_svc=update_chatbot_svc,
get_chatbot_by_user_id_svc=get_chatbot_by_user_id_svc,
)
def get_get_chatbot_by_user_id_action(
get_chatbot_by_user_id_svc: GetChatbotByUserIdSvc,
):
return GetChatbotByUserIdAction(
get_chatbot_by_user_id_svc=get_chatbot_by_user_id_svc
)
async def get_authenticate_action(
user_repo: UserRepo, authenticator: TwitchAuthenticator, s: Settings
s: Settings,
get_tokens_by_auth_code_svc: GetTwitchAuthByAuthCodeSvc,
get_user_by_username_svc: GetUserByUsernameSvc,
create_user_svc: CreateUserSvc,
update_user_svc: UpdateUserSvc,
):
return AuthenticateAction(user_repo=user_repo, authenticator=authenticator, s=s)
return AuthenticateUserAction(
s=s,
get_tokens_by_auth_code_svc=get_tokens_by_auth_code_svc,
get_user_by_username_svc=get_user_by_username_svc,
create_user_svc=create_user_svc,
update_user_svc=update_user_svc,
)
async def chatbot(
get_chatbot_by_user_id_action: GetChatbotByUserIdAction,
create_or_update_chatbot_action: CreateOrUpdateChatbotAction,
user: User,
) -> Chatbot:
cb = await get_chatbot_by_user_id_action.run(user_id=user.id)
if cb:
return cb
return await create_or_update_chatbot_action.run(
user_id=user.id,
)

View file

@ -10,13 +10,22 @@ from apps.httpapi.litestar.dependencies import (
authenticate,
get_authenticate_action,
get_authenticator,
get_chatbot_settings_svc,
get_db,
get_chatbot_repo,
get_create_chatbot_svc,
get_create_or_update_chatbot_action,
get_create_user_svc,
get_get_chatbot_by_user_id_action,
get_get_chatbot_by_user_id_svc,
get_get_tokens_by_auth_code_svc,
get_get_user_by_jwt_action,
get_get_user_by_username_svc,
get_is_valid_token_svc,
get_refresh_token_svc,
get_sentences_storer_svc,
get_settings,
get_update_chatbot_svc,
get_update_user_svc,
get_user_repo,
store_chatbot_settings_svc,
)
from apps.httpapi.litestar.errors import (
after_exception_handler,
@ -79,15 +88,26 @@ def create_app():
"s": Provide(get_settings, use_cache=True),
"a": Provide(get_authenticator, use_cache=True),
"user": Provide(authenticate),
"db": Provide(get_db, use_cache=True),
"bm": Provide(BotsManager, use_cache=True),
"gbs": Provide(get_chatbot_settings_svc),
"sbs": Provide(store_chatbot_settings_svc),
"sss": Provide(get_sentences_storer_svc),
"authenticator": Provide(get_authenticator),
"twitch_authenticator": Provide(get_authenticator),
"authenticate_action": Provide(get_authenticate_action),
"user_repo": Provide(get_user_repo),
"chatbot_repo": Provide(get_chatbot_repo),
"create_user_svc": Provide(get_create_user_svc),
"update_chatbot_svc": Provide(get_update_chatbot_svc),
"update_user_svc": Provide(get_update_user_svc),
"create_chatbot_svc": Provide(get_create_chatbot_svc),
"refresh_token_svc": Provide(get_refresh_token_svc),
"is_valid_token_svc": Provide(get_is_valid_token_svc),
"get_user_by_username_svc": Provide(get_get_user_by_username_svc),
"get_chatbot_by_user_id_svc": Provide(get_get_chatbot_by_user_id_svc),
"get_tokens_by_auth_code_svc": Provide(get_get_tokens_by_auth_code_svc),
"get_user_by_jwt_action": Provide(get_get_user_by_jwt_action),
"get_chatbot_by_user_id_action": Provide(get_get_chatbot_by_user_id_action),
"create_or_update_chatbot_action": Provide(
get_create_or_update_chatbot_action
),
},
)

View file

@ -5,10 +5,12 @@ from litestar.datastructures import UploadFile
from litestar.response import Template
from pydantic import BaseModel, ConfigDict
from huesoporro.actions.chatbot.create_or_update_chatbot import (
CreateOrUpdateChatbotAction,
)
from huesoporro.actions.chatbot.get_chatbot_by_user_id import GetChatbotByUserIdAction
from huesoporro.bot import BotsManager
from huesoporro.models import ChatbotSettings, User
from huesoporro.svc.get_chatbot_settings import ChatbotSettingsGetterSvc
from huesoporro.svc.store_settings import ChatbotSettingsStorerSvc
from huesoporro.models import Chatbot, User
class ManageBotDTO(BaseModel):
@ -23,6 +25,12 @@ class ImportTextFileDTO(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
class UpdateChatbotDTO(BaseModel):
automatic_generation_timer: int = 300
automatic_quote_timer: int = 500
mods: list[str]
@get(
"/tts",
media_type=MediaType.HTML,
@ -48,8 +56,10 @@ async def get_tts_permalink(access_token: str) -> Template:
"/",
media_type=MediaType.HTML,
)
async def get_index(user: User, gbs: ChatbotSettingsGetterSvc) -> Template:
chatbot_settings = await gbs.run(user=user)
async def get_index(
user: User, get_chatbot_by_user_id_action: GetChatbotByUserIdAction
) -> Template:
chatbot_settings = await get_chatbot_by_user_id_action.run(user_id=user.id)
return Template(
template_name="index.html",
context=chatbot_settings.model_dump() if chatbot_settings else {},
@ -60,22 +70,24 @@ async def get_index(user: User, gbs: ChatbotSettingsGetterSvc) -> Template:
async def manage_bot(
user: User,
data: ManageBotDTO,
gbs: ChatbotSettingsGetterSvc,
sbs: ChatbotSettingsStorerSvc,
create_or_update_chatbot_action: CreateOrUpdateChatbotAction,
get_chatbot_by_user_id_action: GetChatbotByUserIdAction,
bm: BotsManager,
) -> Response:
chatbot_settings = await gbs.run(user=user)
if not chatbot_settings:
await sbs.run(user=user, bot_settings=ChatbotSettings())
chatbot_settings = await gbs.run(user=user)
chatbot = await get_chatbot_by_user_id_action.run(
user_id=user.id
) or await create_or_update_chatbot_action.run(
user_id=user.id,
)
if data.command == "start":
if not data.channel_name:
return Response({"message": "Channel name is required"}, status_code=400)
bm.add_bot(user, data.channel_name, chatbot_settings=chatbot_settings) # type: ignore[arg-type]
if user.user in bm.bots:
bm.add_bot(user, data.channel_name, chatbot=chatbot) # type: ignore[arg-type]
if user.username in bm.bots:
await bm.run_user_bot(user)
return Response({"message": "Bot started"})
if data.command == "stop" and user.user in bm.bots:
if data.command == "stop" and user.username in bm.bots:
await bm.stop_user_bot(user)
return Response({"message": "Bot stopped"})
return Response({"message": "Invalid command"}, status_code=400)
@ -83,24 +95,26 @@ async def manage_bot(
@get("/api/v1/bot")
async def get_bot_status(user: User, bm: BotsManager) -> dict:
if user.user not in bm.bots:
if user.username not in bm.bots:
return {"status": "ko"}
return {"status": "ok"}
@get("/api/v1/bot/settings")
async def get_bot_settings(
user: User, gbs: ChatbotSettingsGetterSvc
) -> ChatbotSettings | dict:
cbs = await gbs.run(user=user)
if not cbs:
return {"status": "Not found"}
return cbs
async def get_bot_settings(chatbot: Chatbot) -> Chatbot:
return chatbot
@put("/api/v1/bot/settings")
async def save_bot_settings(
user: User, data: ChatbotSettings, sbs: ChatbotSettingsStorerSvc
user: User,
data: UpdateChatbotDTO,
create_or_update_chatbot_action: CreateOrUpdateChatbotAction,
) -> dict:
await sbs.run(user=user, bot_settings=data)
await create_or_update_chatbot_action.run(
user_id=user.id,
automatic_generation_timer=data.automatic_generation_timer,
automatic_quote_timer=data.automatic_quote_timer,
mods=data.mods,
)
return {"status": "ok"}

View file

@ -4,13 +4,14 @@ from litestar import MediaType, get
from litestar.datastructures.cookie import Cookie
from litestar.response import Redirect, Template
from huesoporro.actions.authenticate import AuthenticateAction
from huesoporro.actions.users.authenticate_user import AuthenticateUserAction
from huesoporro.settings import Settings
@get(path="/o/code")
async def get_code(code: str, authenticate_action: AuthenticateAction) -> Redirect:
token = await authenticate_action.run(code)
async def get_code(code: str, authenticate_action: AuthenticateUserAction) -> Redirect:
user = await authenticate_action.run(code)
token = user.encode()
return Redirect(
"/",
cookies=[