Skip to content

Commit

Permalink
Merge pull request #102 from bomzheg/feature/#21
Browse files Browse the repository at this point in the history
#21 change chat for team
  • Loading branch information
bomzheg authored Jul 29, 2024
2 parents 813b8ea + 9c4ef6d commit 127593c
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 25 deletions.
8 changes: 8 additions & 0 deletions shvatka/core/interfaces/dal/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ async def upsert_chat(self, chat: dto.Chat) -> dto.Chat:
class ChatIdUpdater(Committer, Protocol):
async def update_chat_id(self, chat: dto.Chat, new_id: int) -> None:
raise NotImplementedError


class TeamChatChanger(Committer, Protocol):
async def is_team_in_chat(self, chat: dto.Chat) -> bool:
raise NotImplementedError

async def change_team_chat(self, chat: dto.Chat, team: dto.Team) -> None:
raise NotImplementedError
65 changes: 53 additions & 12 deletions shvatka/core/services/team.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import contextlib
from typing import Sequence

from shvatka.core.interfaces.dal.chat import TeamChatChanger
from shvatka.core.interfaces.dal.complex import TeamMerger
from shvatka.core.interfaces.dal.team import (
TeamCreator,
Expand All @@ -15,6 +17,7 @@
from shvatka.core.models import dto
from shvatka.core.models import enums
from shvatka.core.services.player import check_allow_be_author
from shvatka.core.utils import exceptions
from shvatka.core.utils.defaults_constants import CAPTAIN_ROLE
from shvatka.core.utils.exceptions import SHDataBreach, PermissionsError
from shvatka.core.views.game import GameLogWriter, GameLogEvent, GameLogType
Expand All @@ -28,20 +31,32 @@ async def create_team(
) -> dto.Team:
check_allow_be_author(captain)
await dao.check_player_free(captain)
await dao.check_no_team_in_chat(chat)

team = await dao.create(chat, captain)
await dao.join_team(captain, team, CAPTAIN_ROLE, as_captain=True)
try:
await dao.check_no_team_in_chat(chat)
except exceptions.AnotherTeamInChat as e:
if not e.team or not e.team.captain:
raise
if e.team.captain.id == captain.id:
team = e.team
created = False
else:
raise
else:
team = await dao.create(chat, captain)
created = True
with contextlib.suppress(exceptions.PlayerRestoredInTeam):
await dao.join_team(captain, team, CAPTAIN_ROLE, as_captain=True)
await dao.commit()
await game_log.log(
GameLogEvent(
GameLogType.TEAM_CREATED,
data={
"team": team.name,
"captain": captain.name_mention,
},
if created:
await game_log.log(
GameLogEvent(
GameLogType.TEAM_CREATED,
data={
"team": team.name,
"captain": captain.name_mention,
},
)
)
)
return team


Expand Down Expand Up @@ -87,6 +102,16 @@ async def get_team_by_forum_team_id(forum_team_id: int, dao: ByForumTeamIdGetter
return await dao.get_by_forum_team_id(forum_team_id)


async def change_chat(
team: dto.Team, captain: dto.FullTeamPlayer, chat: dto.Chat, dao: TeamChatChanger
) -> None:
check_can_change_chat(team, captain)
if await dao.is_team_in_chat(chat):
raise exceptions.AnotherTeamInChat(chat_id=chat.tg_id, chat=chat)
await dao.change_team_chat(team=team, chat=chat)
await dao.commit()


async def merge_teams(
manager: dto.Player,
primary: dto.Team,
Expand Down Expand Up @@ -124,6 +149,22 @@ async def merge_teams(
)


def check_can_change_chat(team: dto.Team, captain: dto.FullTeamPlayer):
if team.id != captain.team_id or captain.team_id != captain.team.id:
raise SHDataBreach(
team=team, player=captain.player, notify_user="Вы не игрок этой команды"
)
assert captain.player is not None
assert team.captain is not None
if team.captain.id == captain.player.id:
return
raise PermissionsError(
permission_name="change_chat", # TODO
team=team,
player=captain.player,
)


def check_can_change_name(team: dto.Team, captain: dto.FullTeamPlayer):
if team.id != captain.team_id or captain.team_id != captain.team.id:
raise SHDataBreach(
Expand Down
4 changes: 4 additions & 0 deletions shvatka/core/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def __str__(self) -> str:
return result


class ChatNotFound(SHError):
notify_user = "Такой чат не найден"


class FileNotFound(SHError, AttributeError):
notify_user = "Файл не найден"

Expand Down
41 changes: 30 additions & 11 deletions shvatka/infrastructure/db/dao/rdb/chat.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,58 @@
from datetime import datetime, tzinfo
import typing
from sqlalchemy import select, ScalarResult
from sqlalchemy import select, ScalarResult, update
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.exc import NoResultFound
from sqlalchemy.ext.asyncio import AsyncSession

from shvatka.core.models import dto
from shvatka.infrastructure.db.models import Chat
from shvatka.core.utils import exceptions
from shvatka.infrastructure.db import models
from .base import BaseDAO


class ChatDao(BaseDAO[Chat]):
class ChatDao(BaseDAO[models.Chat]):
def __init__(
self, session: AsyncSession, clock: typing.Callable[[tzinfo], datetime] = datetime.now
) -> None:
super().__init__(Chat, session, clock=clock)
super().__init__(models.Chat, session, clock=clock)

async def get_by_tg_id(self, tg_id: int) -> dto.Chat:
chat = await self._get_by_tg_id(tg_id)
return chat.to_dto()

async def _get_by_tg_id(self, tg_id: int) -> Chat:
result: ScalarResult[Chat] = await self.session.scalars(
select(Chat).where(Chat.tg_id == tg_id)
async def _get_by_tg_id(self, tg_id: int) -> models.Chat:
result: ScalarResult[models.Chat] = await self.session.scalars(
select(models.Chat).where(models.Chat.tg_id == tg_id)
)
return result.one()
try:
return result.one()
except NoResultFound as e:
raise exceptions.ChatNotFound(chat_id=tg_id) from e

async def change_team_chat(self, team: dto.Team, chat: dto.Chat) -> None:
await self.session.execute(
update(models.Chat).where(models.Chat.tg_id == team.get_chat_id()).values(team_id=None)
)
await self.session.execute(
update(models.Chat).where(models.Chat.tg_id == chat.tg_id).values(team_id=team.id)
)

async def is_team_in_chat(self, chat: dto.Chat) -> bool:
chat_db = await self._get_by_tg_id(chat.tg_id)
return chat_db.team_id is not None

async def upsert_chat(self, chat: dto.Chat) -> dto.Chat:
kwargs = dict(tg_id=chat.tg_id, title=chat.title, username=chat.username, type=chat.type)
saved_chat = await self.session.execute(
insert(Chat)
insert(models.Chat)
.values(**kwargs)
.on_conflict_do_update(
index_elements=(Chat.tg_id,), set_=kwargs, where=Chat.tg_id == chat.tg_id
index_elements=(models.Chat.tg_id,),
set_=kwargs,
where=models.Chat.tg_id == chat.tg_id,
)
.returning(Chat)
.returning(models.Chat)
)
return saved_chat.scalar_one().to_dto()

Expand Down
8 changes: 8 additions & 0 deletions shvatka/tgbot/dialogs/team_manage/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
send_user_request,
gotten_user_request,
remove_user_request,
send_chat_request,
gotten_chat_request,
)

TEAM_PLAYER_CARD = Jinja(
Expand Down Expand Up @@ -62,6 +64,12 @@
state=states.CaptainsBridgeSG.players,
when=F["team_player"].can_manage_players | F["team_player"].can_remove_players,
),
Button(
Const("🔀Перенести в другой чат"),
id="change_chat",
on_click=send_chat_request,
),
MessageInput(func=gotten_chat_request, filter=F.chat_shared),
Button(
Const("🔮Былые свершения команды"),
id="merge_teams",
Expand Down
56 changes: 56 additions & 0 deletions shvatka/tgbot/dialogs/team_manage/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from shvatka.core.services.team import (
rename_team,
change_team_desc,
change_chat,
)
from shvatka.core.utils import exceptions
from shvatka.infrastructure.db.dao.holder import HolderDao
Expand Down Expand Up @@ -120,6 +121,15 @@ async def change_emoji_handler(m: Message, widget: Any, manager: DialogManager,
await manager.switch_to(states.CaptainsBridgeSG.player)


async def send_chat_request(c: CallbackQuery, widget: Any, manager: DialogManager):
assert isinstance(c.message, Message)
msg = await c.message.answer(
"Чтобы перенести команду в другой чат <b><u>нажми кнопку в самом низу</u></b>",
reply_markup=kb.get_chat_request_kb(),
)
manager.dialog_data["chat_request_message"] = msg.message_id


async def send_user_request(c: CallbackQuery, widget: Any, manager: DialogManager):
assert isinstance(c.message, Message)
msg = await c.message.answer(
Expand All @@ -129,6 +139,52 @@ async def send_user_request(c: CallbackQuery, widget: Any, manager: DialogManage
manager.dialog_data["user_request_message"] = msg.message_id


async def gotten_chat_request(m: Message, widget: Any, manager: DialogManager):
assert m.chat_shared
target_id = m.chat_shared.chat_id
dao: HolderDao = manager.middleware_data["dao"]
captain: dto.Player = manager.middleware_data["player"]
team = await get_my_team(captain, dao.team_player)
assert team
old_chat_id = team.get_chat_id()
assert team
try:
chat = await dao.chat.get_by_tg_id(tg_id=target_id)
except exceptions.ChatNotFound:
await m.answer(
"Чат почему-то не найден. "
"Попробуй добавить в чат бота, написать в чат какое-нибудь сообщение и повторить",
)
return
if chat.type != enums.ChatType.supergroup:
await m.answer(
f"Чат {chat.name} не является супергруппой. "
"Подробнее: https://telegra.ph/Preobrazovanie-gruppy-v-supergruppu-08-25",
disable_web_page_preview=True,
)
return
team_player = await get_full_team_player(player=captain, team=team, dao=dao.team_player)
bot: Bot = manager.middleware_data["bot"]
try:
await change_chat(team, team_player, chat, dao.chat)
except exceptions.AnotherTeamInChat:
await bot.send_message(
chat_id=captain.get_chat_id(),
text=f"‼️Другая команда уже находится в чате " f"({hd.quote(chat.name)}).\n",
)
return
await bot.send_message(
chat_id=captain.get_chat_id(),
text=(
f"Команда {hd.bold(team.name)} перенесена в чат {hd.bold(chat.name)}\n"
f"{old_chat_id}➡️{chat.tg_id}"
),
)
await total_remove_msg(
bot, chat_id=m.chat.id, msg_id=manager.dialog_data.pop("chat_request_message")
)


async def gotten_user_request(m: Message, widget: Any, manager: DialogManager):
assert m.user_shared
target_id = m.user_shared.user_id
Expand Down
1 change: 0 additions & 1 deletion shvatka/tgbot/handlers/team/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ def setup() -> Router:
router.message.register(
cmd_create_team,
Command(commands=CREATE_TEAM_COMMAND.command),
IsTeamFilter(is_team=False),
F.chat.type == ChatType.SUPERGROUP,
)
router.message.register(
Expand Down
1 change: 1 addition & 0 deletions shvatka/tgbot/keyboards/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
JoinToTeamRequestCD,
get_join_team_kb,
get_user_request_kb,
get_chat_request_kb,
)
from .waiver import (
get_kb_waivers,
Expand Down
24 changes: 23 additions & 1 deletion shvatka/tgbot/keyboards/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
InlineKeyboardMarkup,
KeyboardButton,
KeyboardButtonRequestUser,
KeyboardButtonRequestChat,
)
from aiogram.utils.keyboard import InlineKeyboardBuilder

Expand Down Expand Up @@ -39,7 +40,28 @@ def get_user_request_kb() -> ReplyKeyboardMarkup:
KeyboardButton(
text="ВЫБРАТЬ ИГРОКА В КОМАНДУ\n\n⏩НАЖАТЬ ПРЯМО СЮДА⏪",
request_user=KeyboardButtonRequestUser(
user_is_bot=False, request_id=random.randint(0, 1000)
user_is_bot=False,
request_name=True,
request_username=True,
request_id=random.randint(0, 1000),
),
)
]
]
)


def get_chat_request_kb() -> ReplyKeyboardMarkup:
return ReplyKeyboardMarkup(
keyboard=[
[
KeyboardButton(
text="ВЫБРАТЬ ЧАТ В КОМАНДУ\n\n⏩НАЖАТЬ ПРЯМО СЮДА⏪",
request_chat=KeyboardButtonRequestChat(
request_id=random.randint(0, 1000),
bot_is_member=True,
chat_is_channel=False,
chat_has_username=False,
),
)
]
Expand Down

0 comments on commit 127593c

Please sign in to comment.