Skip to content

Commit 3cd631f

Browse files
committed
feat(casino): Казино для групп
Closes #154
1 parent 52f0a3e commit 3cd631f

File tree

4 files changed

+264
-1
lines changed

4 files changed

+264
-1
lines changed

tgbot/filters/group_casino.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Фильтры для проверки доступа к казино в группах."""
2+
3+
from aiogram.dispatcher.event.bases import CancelHandler
4+
from aiogram.filters import BaseFilter
5+
from aiogram.types import Message
6+
from stp_database import Employee, MainRequestsRepo
7+
8+
9+
class IsGroupCasinoAllowed(BaseFilter):
10+
"""Фильтр проверки доступа к казино в группе.
11+
12+
Проверяет:
13+
1. Зарегистрирована ли группа в базе данных
14+
2. Разрешен ли казино в группе (is_casino_allowed=True)
15+
3. Разрешен ли доступ к казино пользователю (is_casino_allowed=True)
16+
17+
При отсутствии любого из условий прерывает обработку без сообщения.
18+
"""
19+
20+
async def __call__(
21+
self, message: Message, user: Employee, stp_repo: MainRequestsRepo, **kwargs
22+
) -> bool:
23+
"""Проверяет разрешен ли доступ к казино для пользователя и группы.
24+
25+
Args:
26+
message: Входящее сообщение.
27+
user: Экземпляр пользователя с моделью Employee
28+
stp_repo: Репозиторий операций с базой STP
29+
**kwargs: Дополнительные аргументы.
30+
31+
Returns:
32+
True если доступ разрешен.
33+
34+
Raises:
35+
CancelHandler: Если доступ запрещен (без отправки сообщения).
36+
"""
37+
# Проверяем пользователя
38+
if user is None or not user.is_casino_allowed:
39+
raise CancelHandler()
40+
41+
# Проверяем группу
42+
try:
43+
group = await stp_repo.group.get_groups(message.chat.id)
44+
if not group or not group.is_casino_allowed:
45+
raise CancelHandler()
46+
except Exception:
47+
# Если ошибка при получении группы - запрещаем доступ
48+
raise CancelHandler()
49+
50+
return True

tgbot/handlers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from tgbot.handlers.groups.admin.admin import group_admin_router
99
from tgbot.handlers.groups.admin.settings import group_settings_router
1010
from tgbot.handlers.groups.join import groups_router
11+
from tgbot.handlers.groups.user.casino import group_casino_router
1112
from tgbot.handlers.groups.user.main import group_user_router
1213
from tgbot.handlers.groups.user.whois import group_whois_router
1314
from tgbot.handlers.inline.inline import user_inline_router
@@ -23,6 +24,7 @@
2324
group_whois_router,
2425
group_admin_router,
2526
group_user_router,
27+
group_casino_router,
2628
group_settings_router,
2729
channels_router,
2830
groups_router,
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
"""Обработчики команд казино для групп."""
2+
3+
import asyncio
4+
import logging
5+
import re
6+
7+
from aiogram import F, Router
8+
from aiogram.enums import DiceEmoji
9+
from aiogram.filters import Command
10+
from aiogram.types import Message
11+
from stp_database import Employee, MainRequestsRepo
12+
13+
from tgbot.dialogs.events.common.game.casino import (
14+
calculate_simple_multiplier,
15+
calculate_slots_multiplier,
16+
format_result,
17+
)
18+
from tgbot.filters.group_casino import IsGroupCasinoAllowed
19+
20+
logger = logging.getLogger(__name__)
21+
22+
group_casino_router = Router()
23+
group_casino_router.message.filter(F.chat.type.in_(("group", "supergroup")))
24+
25+
26+
def parse_casino_command(message_text: str) -> int:
27+
"""Извлечь ставку из команды казино.
28+
29+
Args:
30+
message_text: Текст команды (например, "/slots 50" или "/dice")
31+
32+
Returns:
33+
Размер ставки (минимум 10)
34+
"""
35+
# Паттерн для извлечения числа из команды
36+
match = re.search(r"/(?:slots|dice|darts|bowling)\s+(\d+)", message_text)
37+
if match:
38+
bet_amount = int(match.group(1))
39+
return max(10, bet_amount) # Минимальная ставка 10
40+
return 10 # Ставка по умолчанию
41+
42+
43+
async def process_casino_game(
44+
message: Message,
45+
user: Employee,
46+
stp_repo: MainRequestsRepo,
47+
game_type: str,
48+
dice_emoji: DiceEmoji,
49+
bet_amount: int,
50+
) -> None:
51+
"""Обработать игру в казино.
52+
53+
Args:
54+
message: Сообщение от пользователя
55+
user: Экземпляр пользователя с моделью Employee
56+
stp_repo: Репозиторий операций с базой STP
57+
game_type: Тип игры (slots, dice, darts, bowling)
58+
dice_emoji: Emoji для dice API
59+
bet_amount: Размер ставки
60+
"""
61+
try:
62+
# Проверяем баланс
63+
user_balance = await stp_repo.transaction.get_user_balance(user.user_id)
64+
65+
if user_balance < bet_amount:
66+
await message.reply(
67+
f"❌ Недостаточно баллов для игры!\n"
68+
f"💰 Твой баланс: {user_balance} баллов\n"
69+
f"🎲 Нужно для ставки: {bet_amount} баллов"
70+
)
71+
return
72+
73+
# Отправляем dice и ждем результата
74+
dice_message = await message.reply_dice(emoji=dice_emoji)
75+
dice_value = dice_message.dice.value
76+
77+
# Ждем анимацию (3 секунды)
78+
await asyncio.sleep(3)
79+
80+
# Рассчитываем выигрыш
81+
if game_type == "slots":
82+
multiplier = calculate_slots_multiplier(dice_value)
83+
else:
84+
multiplier = calculate_simple_multiplier(dice_value)
85+
86+
# Вычисляем чистый выигрыш/проигрыш
87+
if multiplier > 0:
88+
gross_win = int(bet_amount * multiplier)
89+
net_win = gross_win - bet_amount
90+
else:
91+
net_win = -bet_amount
92+
93+
# Обновляем баланс
94+
if net_win > 0:
95+
await stp_repo.transaction.add_transaction(
96+
user_id=user.user_id,
97+
transaction_type="earn",
98+
source_type="casino",
99+
amount=net_win,
100+
comment=f"Выигрыш в {game_type}: {dice_value} (ставка {bet_amount})",
101+
)
102+
elif net_win < 0:
103+
await stp_repo.transaction.add_transaction(
104+
user_id=user.user_id,
105+
transaction_type="spend",
106+
source_type="casino",
107+
amount=abs(net_win),
108+
comment=f"Проигрыш в {game_type}: {dice_value} (ставка {bet_amount})",
109+
)
110+
111+
# Получаем новый баланс
112+
new_balance = await stp_repo.transaction.get_user_balance(user.user_id)
113+
114+
# Формируем сообщение о результате используя общую функцию
115+
result_data = format_result(game_type, dice_value, multiplier, net_win)
116+
117+
# Дополняем информацией о ставке и балансе для групповых команд
118+
message_parts = [
119+
f"{result_data['result_icon']} <b>{result_data['result_title']}</b>",
120+
result_data["result_message"],
121+
]
122+
123+
# Информация о ставке и выигрыше
124+
if net_win > 0:
125+
message_parts.append(f"\n💰 <b>Выиграно:</b> +{net_win} баллов")
126+
elif net_win < 0:
127+
message_parts.append(f"\n💸 <b>Проиграно:</b> {abs(net_win)} баллов")
128+
129+
message_parts.append(f"\n🎯 <b>Ставка:</b> {bet_amount} баллов")
130+
message_parts.append(f"\n\n💳 <b>Баланс:</b> {new_balance} баллов")
131+
132+
result_message = "\n".join(message_parts)
133+
134+
await dice_message.reply(result_message)
135+
136+
# Логируем игру
137+
user_name = (
138+
user.fullname
139+
if user
140+
else f"@{message.from_user.username}"
141+
if message.from_user.username
142+
else message.from_user.full_name
143+
)
144+
logger.info(
145+
f"[Casino/{game_type}] {user_name} ({user.user_id}) играл с ставкой {bet_amount}, "
146+
f"результат {dice_value}, выигрыш {net_win}"
147+
)
148+
149+
except Exception as e:
150+
logger.error(f"Ошибка в казино {game_type}: {e}")
151+
await message.reply("🚨 Произошла ошибка при игре в казино. Попробуй позже.")
152+
153+
154+
@group_casino_router.message(Command("slots"), IsGroupCasinoAllowed())
155+
async def slots_cmd(message: Message, user: Employee, stp_repo: MainRequestsRepo):
156+
"""Обработчик команды /slots для групп.
157+
158+
Args:
159+
message: Сообщение от пользователя
160+
user: Экземпляр пользователя с моделью Employee
161+
stp_repo: Репозиторий операций с базой STP
162+
"""
163+
bet_amount = parse_casino_command(message.text)
164+
await process_casino_game(
165+
message, user, stp_repo, "slots", DiceEmoji.SLOT_MACHINE, bet_amount
166+
)
167+
168+
169+
@group_casino_router.message(Command("dice"), IsGroupCasinoAllowed())
170+
async def dice_cmd(message: Message, user: Employee, stp_repo: MainRequestsRepo):
171+
"""Обработчик команды /dice для групп.
172+
173+
Args:
174+
message: Сообщение от пользователя
175+
user: Экземпляр пользователя с моделью Employee
176+
stp_repo: Репозиторий операций с базой STP
177+
"""
178+
bet_amount = parse_casino_command(message.text)
179+
await process_casino_game(
180+
message, user, stp_repo, "dice", DiceEmoji.DICE, bet_amount
181+
)
182+
183+
184+
@group_casino_router.message(Command("darts"), IsGroupCasinoAllowed())
185+
async def darts_cmd(message: Message, user: Employee, stp_repo: MainRequestsRepo):
186+
"""Обработчик команды /darts для групп.
187+
188+
Args:
189+
message: Сообщение от пользователя
190+
user: Экземпляр пользователя с моделью Employee
191+
stp_repo: Репозиторий операций с базой STP
192+
"""
193+
bet_amount = parse_casino_command(message.text)
194+
await process_casino_game(
195+
message, user, stp_repo, "darts", DiceEmoji.DART, bet_amount
196+
)
197+
198+
199+
@group_casino_router.message(Command("bowling"), IsGroupCasinoAllowed())
200+
async def bowling_cmd(message: Message, user: Employee, stp_repo: MainRequestsRepo):
201+
"""Обработчик команды /bowling для групп.
202+
203+
Args:
204+
message: Сообщение от пользователя
205+
user: Экземпляр пользователя с моделью Employee
206+
stp_repo: Репозиторий операций с базой STP
207+
"""
208+
bet_amount = parse_casino_command(message.text)
209+
await process_casino_game(
210+
message, user, stp_repo, "bowling", DiceEmoji.BOWLING, bet_amount
211+
)

tgbot/services/mailing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ async def send_activation_product_email(
9393
9494
<a href="https://t.me/{user.username}"<b>{user.fullname}</b></a> из {user.division} просит активировать предмет <b>{product.name}</b><br><br>
9595
96-
<b>О предмете</b>
96+
<b>О предмете</b><br>
9797
💵 Стоимость: {product.cost} баллов<br>
9898
📝 Описание: {product.description}<br>
9999
📍 Всего активаций: {product.count} (Осталось у специалиста - {product.count - (purchase.usage_count + 1)})<br>

0 commit comments

Comments
 (0)