diff --git a/main.py b/__main__.py similarity index 78% rename from main.py rename to __main__.py index c7ab188..3ea28d6 100644 --- a/main.py +++ b/__main__.py @@ -8,10 +8,11 @@ from aiogram.enums import ParseMode from aiogram.types import ErrorEvent from aiogram_dialog import setup_dialogs from environs import Env +from faststream.nats import NatsBroker -from bot.commands import command_router -from bot.database import initialize_database -from bot.dialogs import ( +from shopbot.bot.commands import command_router +from shopbot.bot.database import initialize_database +from shopbot.bot.dialogs import ( admin_dialog, distribution_dialog, gift_balance_dialog, @@ -24,6 +25,7 @@ from bot.dialogs import ( up_balance_dialog, up_balance_lolz_dialog, ) +from shopbot.consumers.new_account import router as new_account_router async def on_error(event: ErrorEvent, bot: Bot): @@ -39,6 +41,9 @@ async def main(): ) dp = Dispatcher() + broker = NatsBroker() + broker.include_router(new_account_router) + await initialize_database() ROUTERS = [ @@ -62,7 +67,9 @@ async def main(): dp.error.register(on_error) setup_dialogs(dp) try: - await dp.start_polling(bot) + async with broker: + await broker.start() + await dp.start_polling(bot) finally: await bot.session.close() diff --git a/bot/commands/command.py b/bot/commands/command.py index 47115e2..0aaefec 100644 --- a/bot/commands/command.py +++ b/bot/commands/command.py @@ -1,14 +1,14 @@ import logging from aiogram import Router +from aiogram.filters import Command, CommandStart from aiogram.types import Message -from aiogram.filters import CommandStart, Command from aiogram_dialog import DialogManager, StartMode +from environs import Env from sqlalchemy.exc import SQLAlchemyError -from bot.database import register_user -from bot.states import StartSG, AdminSG -from environs import Env +from shopbot.bot.database import register_user +from shopbot.bot.states import AdminSG, StartSG router = Router() @@ -26,10 +26,7 @@ async def command_start_process(message: Message, dialog_manager: DialogManager) return user_id = message.from_user.id username = message.from_user.username - await register_user( - tg_id=user_id, - username=username - ) + await register_user(tg_id=user_id, username=username) await dialog_manager.start(state=StartSG.start, mode=StartMode.RESET_STACK) except SQLAlchemyError as e: @@ -37,6 +34,7 @@ async def command_start_process(message: Message, dialog_manager: DialogManager) except Exception as e: logging.error(f"An unexpected error occurred: {e}", exc_info=True) + @router.message(Command("admin_panel")) async def command_admin_panel(message: Message, dialog_manager: DialogManager): if not message.from_user or not message.from_user.id: @@ -45,4 +43,4 @@ async def command_admin_panel(message: Message, dialog_manager: DialogManager): if str(message.from_user.id) == ADMIN_ID: await dialog_manager.start(state=AdminSG.panel) else: - logging.warning(f"User with tg_id {message.from_user.id} is not admin") \ No newline at end of file + logging.warning(f"User with tg_id {message.from_user.id} is not admin") diff --git a/bot/database/db.py b/bot/database/db.py index 51dbedc..fda0eba 100644 --- a/bot/database/db.py +++ b/bot/database/db.py @@ -1,14 +1,14 @@ import logging from typing import Optional -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession -from sqlalchemy.orm import sessionmaker -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.future import select -from sqlalchemy.dialects.postgresql import insert - -from bot.database.models import Base, UserModel from environs import Env +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine +from sqlalchemy.future import select +from sqlalchemy.orm import sessionmaker + +from shopbot.bot.database.models import Base, UserModel env = Env() env.read_env() @@ -26,7 +26,9 @@ async def initialize_database(): except SQLAlchemyError as e: logging.error(f"Database error during initialization: {e}", exc_info=True) except Exception as e: - logging.error(f"An unexpected error occurred during initialization: {e}", exc_info=True) + logging.error( + f"An unexpected error occurred during initialization: {e}", exc_info=True + ) async def register_user(tg_id: int, username: Optional[str] = None): @@ -43,7 +45,9 @@ async def register_user(tg_id: int, username: Optional[str] = None): except SQLAlchemyError as e: logging.error(f"Database error during user registration: {e}", exc_info=True) except Exception as e: - logging.error(f"An unexpected error occurred during user registration: {e}", exc_info=True) + logging.error( + f"An unexpected error occurred during user registration: {e}", exc_info=True + ) async def update_balance(tg_id: int, amount: int): @@ -61,4 +65,6 @@ async def update_balance(tg_id: int, amount: int): except SQLAlchemyError as e: logging.error(f"Database error during balance update: {e}", exc_info=True) except Exception as e: - logging.error(f"An unexpected error occurred during balance update: {e}", exc_info=True) \ No newline at end of file + logging.error( + f"An unexpected error occurred during balance update: {e}", exc_info=True + ) diff --git a/bot/dialogs/admin.py b/bot/dialogs/admin.py index 8237922..f6717a3 100644 --- a/bot/dialogs/admin.py +++ b/bot/dialogs/admin.py @@ -1,14 +1,15 @@ -from aiogram_dialog import Dialog, Window -from aiogram_dialog.widgets.kbd import Row, Start, Next -from aiogram_dialog.widgets.text import Const, Format -from aiogram_dialog.widgets.input import TextInput - -from bot.services import broadcast_message, get_admin_statistics -from bot.services.user import gift_balance_data, take_balance_user -from bot.states import AdminSG, DistributionSG, GiftBalanceSG, TakeBalanceSG -from bot.validations import error_number_handler from typing import Any +from aiogram_dialog import Dialog, Window +from aiogram_dialog.widgets.input import TextInput +from aiogram_dialog.widgets.kbd import Next, Row, Start +from aiogram_dialog.widgets.text import Const, Format + +from shopbot.bot.services import broadcast_message, get_admin_statistics +from shopbot.bot.services.user import gift_balance_data, take_balance_user +from shopbot.bot.states import AdminSG, DistributionSG, GiftBalanceSG, TakeBalanceSG +from shopbot.bot.validations import error_number_handler + ADMIN_PANEL_BACK_BUTTON = Start( Const("🔙 Вернуться в админ-панель"), id="back", state=AdminSG.panel ) @@ -16,7 +17,9 @@ ADMIN_PANEL_TEXT = "🔧 Админ панель" class AdminDialogWindow(Window): - def __init__(self, text: str, state: Any, input_id: str, type_factory: Any, **kwargs): + def __init__( + self, text: str, state: Any, input_id: str, type_factory: Any, **kwargs + ): super().__init__( Const(text), TextInput( @@ -27,7 +30,7 @@ class AdminDialogWindow(Window): ), ADMIN_PANEL_BACK_BUTTON, state=state, - **kwargs + **kwargs, ) @@ -105,13 +108,13 @@ gift_balance_dialog = Dialog( text="💸 Выдача баланса\n\nВведите ID пользователя:", state=GiftBalanceSG.gift_balance_user_id, input_id="user_id_input", - type_factory=int + type_factory=int, ), AdminDialogWindow( text="Введите сумму для пополнения баланса:", state=GiftBalanceSG.gift_balance_amount, input_id="amount_input", - type_factory=float + type_factory=float, ), Window( Format("{result}"), @@ -126,13 +129,13 @@ take_balance_dialog = Dialog( text="❌ Отнятие баланса\n\nВведите ID пользователя:", state=TakeBalanceSG.take_balance_user_id, input_id="user_id_input", - type_factory=int + type_factory=int, ), AdminDialogWindow( text="Введите сумму для вычета из баланса:", state=TakeBalanceSG.take_balance_amount, input_id="amount_input", - type_factory=float + type_factory=float, ), Window( Format("{result}"), @@ -140,4 +143,4 @@ take_balance_dialog = Dialog( state=TakeBalanceSG.take_balance_confirmation, getter=take_balance_user, ), -) \ No newline at end of file +) diff --git a/bot/dialogs/balance.py b/bot/dialogs/balance.py index eb6ba07..cef955e 100644 --- a/bot/dialogs/balance.py +++ b/bot/dialogs/balance.py @@ -1,16 +1,21 @@ -from aiogram_dialog import Dialog, Window -from aiogram_dialog.widgets.kbd import Row, Start, Button -from aiogram_dialog.widgets.text import Format, Const -from aiogram_dialog.widgets.input import TextInput, ManagedTextInput -from aiogram_dialog import DialogManager -from aiogram.types import Message - -from bot.services import check_pay_crypto_bot, check_pay_lolz -from bot.getters import getter_amount, getter_amount_cryptobot -from bot.states import UpBalanceCryptoSG, UpBalanceSG, UpBalanceLolzSG, ProfileSG -from bot.validations import error_number_handler from typing import Any +from aiogram.types import Message +from aiogram_dialog import Dialog, DialogManager, Window +from aiogram_dialog.widgets.input import ManagedTextInput, TextInput +from aiogram_dialog.widgets.kbd import Button, Row, Start +from aiogram_dialog.widgets.text import Const, Format + +from shopbot.bot.getters import getter_amount, getter_amount_cryptobot +from shopbot.bot.services import check_pay_crypto_bot, check_pay_lolz +from shopbot.bot.states import ( + ProfileSG, + UpBalanceCryptoSG, + UpBalanceLolzSG, + UpBalanceSG, +) +from shopbot.bot.validations import error_number_handler + class UpBalanceWindow(Window): def __init__(self, text: str, state: Any, on_error: Any, **kwargs): @@ -23,10 +28,16 @@ class UpBalanceWindow(Window): on_error=on_error, ), state=state, - **kwargs + **kwargs, ) - async def _on_success_callback(self, message: Message, widget: ManagedTextInput, dialog_manager: DialogManager, data: str): + async def _on_success_callback( + self, + message: Message, + widget: ManagedTextInput, + dialog_manager: DialogManager, + data: str, + ): if not dialog_manager.current_context().dialog_data.get("error"): await dialog_manager.next() @@ -34,23 +45,32 @@ class UpBalanceWindow(Window): class PaymentWindow(Window): def __init__(self, state: Any, check_pay_function: Any, getter: Any, **kwargs): super().__init__( - Format(text="{paylink}", when=lambda data, widget, manager: not data.get("error")), - Format(text="{error}", when=lambda data, widget, manager: data.get("error") is not None), + Format( + text="{paylink}", + when=lambda data, widget, manager: not data.get("error"), + ), + Format( + text="{error}", + when=lambda data, widget, manager: data.get("error") is not None, + ), Button( Const("🔍 Проверить оплату"), id="check_pay_button", on_click=check_pay_function, - when=lambda data, widget, manager: not data.get("error") and data.get("paylink") is not None + when=lambda data, widget, manager: not data.get("error") + and data.get("paylink") is not None, ), Button( Const("🔙 Отмена"), id="cancel_button", - on_click=lambda callback, button, manager: manager.switch_to(ProfileSG.profile), - when=lambda data, widget, manager: data.get("error") is not None + on_click=lambda callback, button, manager: manager.switch_to( + ProfileSG.profile + ), + when=lambda data, widget, manager: data.get("error") is not None, ), state=state, getter=getter, - **kwargs + **kwargs, ) @@ -85,8 +105,7 @@ up_balance_lolz_dialog = Dialog( state=UpBalanceLolzSG.pay_link, check_pay_function=check_pay_lolz, getter=getter_amount, - ) - + ), ) @@ -94,11 +113,11 @@ up_balance_cryptobot_dialog = Dialog( UpBalanceWindow( text="💸 Введите сумму пополнения", state=UpBalanceCryptoSG.up_balance_crypto, - on_error=error_number_handler + on_error=error_number_handler, ), PaymentWindow( state=UpBalanceCryptoSG.pay_link, check_pay_function=check_pay_crypto_bot, getter=getter_amount_cryptobot, ), -) \ No newline at end of file +) diff --git a/bot/dialogs/profile.py b/bot/dialogs/profile.py index 2d14b1c..c43b8b9 100644 --- a/bot/dialogs/profile.py +++ b/bot/dialogs/profile.py @@ -1,11 +1,14 @@ from aiogram_dialog import Dialog, Window -from aiogram_dialog.widgets.kbd import Row, Start, Back, Button -from aiogram_dialog.widgets.text import Format, Const -from aiogram_dialog.widgets.kbd import ScrollingGroup, Select -from bot.getters import get_history_account_details, get_purchase_history -from bot.getters import username_getter -from bot.services import on_history_account_selected -from bot.states import PurchaseHistorySG, UpBalanceSG, ProfileSG, StartSG +from aiogram_dialog.widgets.kbd import Back, Button, Row, ScrollingGroup, Select, Start +from aiogram_dialog.widgets.text import Const, Format + +from shopbot.bot.getters import ( + get_history_account_details, + get_purchase_history, + username_getter, +) +from shopbot.bot.services import on_history_account_selected +from shopbot.bot.states import ProfileSG, PurchaseHistorySG, StartSG, UpBalanceSG PROFILE_BACK_BUTTON = Start(Const("🔙 Назад"), id="back", state=StartSG.start) HISTORY_BACK_BUTTON = Start(Const("🔙 Назад"), id="back", state=ProfileSG.profile) @@ -59,4 +62,4 @@ history_dialog = Dialog( state=PurchaseHistorySG.details, getter=get_history_account_details, ), -) \ No newline at end of file +) diff --git a/bot/dialogs/purchase.py b/bot/dialogs/purchase.py index a6d4221..0b2b69a 100644 --- a/bot/dialogs/purchase.py +++ b/bot/dialogs/purchase.py @@ -14,14 +14,18 @@ from aiogram_dialog.widgets.kbd import ( from aiogram_dialog.widgets.text import Const, Format, Multi from pydantic import NonNegativeInt -from bot.getters import ( +from shopbot.bot.getters import ( get_account_details, get_account_info, get_accounts, ) -from bot.getters.filters import get_filters_info, on_filter_selected, update_filters -from bot.services import on_account_selected, on_buy_button_click -from bot.states import ( +from shopbot.bot.getters.filters import ( + get_filters_info, + on_filter_selected, + update_filters, +) +from shopbot.bot.services import on_account_selected, on_buy_button_click +from shopbot.bot.states import ( PurchaseAccountsSG, StartSG, ) diff --git a/bot/dialogs/start.py b/bot/dialogs/start.py index aaf971f..94c5ea4 100644 --- a/bot/dialogs/start.py +++ b/bot/dialogs/start.py @@ -1,9 +1,9 @@ from aiogram_dialog import Dialog, Window -from aiogram_dialog.widgets.kbd import Start, Row +from aiogram_dialog.widgets.kbd import Row, Start from aiogram_dialog.widgets.text import Const, Format -from bot.getters import username_getter -from bot.states import StartSG, ProfileSG, PurchaseAccountsSG +from shopbot.bot.getters import username_getter +from shopbot.bot.states import ProfileSG, PurchaseAccountsSG, StartSG start_dialog = Dialog( Window( @@ -20,4 +20,4 @@ start_dialog = Dialog( getter=username_getter, state=StartSG.start, ) -) \ No newline at end of file +) diff --git a/bot/getters/accounts.py b/bot/getters/accounts.py index a12acaa..50abd0d 100644 --- a/bot/getters/accounts.py +++ b/bot/getters/accounts.py @@ -5,9 +5,9 @@ from aiogram_dialog import DialogManager from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.db import async_session -from bot.database.models import Account -from bot.getters.filters import FilterDTO, FilterType +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import Account +from shopbot.bot.getters.filters import FilterDTO, FilterType def _format_account_short_info(account: Account) -> str: diff --git a/bot/getters/filters.py b/bot/getters/filters.py index 9a58729..930bb6d 100644 --- a/bot/getters/filters.py +++ b/bot/getters/filters.py @@ -6,7 +6,7 @@ from aiogram.types import CallbackQuery, Message from aiogram_dialog import DialogManager from pydantic import NonNegativeInt -from bot.states.states import PurchaseAccountsSG +from shopbot.bot.states.states import PurchaseAccountsSG class FilterType(Enum): diff --git a/bot/getters/history.py b/bot/getters/history.py index a296f18..095ee14 100644 --- a/bot/getters/history.py +++ b/bot/getters/history.py @@ -1,31 +1,31 @@ import logging +from typing import Any, Dict from aiogram_dialog import DialogManager from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.models import Account -from bot.database.db import async_session -from typing import Dict, Any +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import Account def _format_account_short_info(account: Account) -> str: - return ( - f"🌍 {account.region} | 🎮 {account.current_rank} | 💰 {account.price:.2f}₽" - ) + return f"🌍 {account.region} | 🎮 {account.current_rank} | 💰 {account.price:.2f}₽" -async def get_purchase_history(dialog_manager: DialogManager, **kwargs) -> Dict[str, Any]: +async def get_purchase_history( + dialog_manager: DialogManager, **kwargs +) -> Dict[str, Any]: user_tg_id = dialog_manager.event.from_user.id try: async with async_session() as session: - result = await session.execute( + result = await session.execute( select(Account).where(Account.buyer == user_tg_id) ) - purchased_accounts = result.scalars().all() + purchased_accounts = result.scalars().all() if purchased_accounts: - text = "🛍️ Ваша история покупок:" + text = "🛍️ Ваша история покупок:" else: logging.warning(f"User with tg_id {user_tg_id} has no purchases") text = "🛍️ У вас пока нет покупок, пора купить что-нибудь)" @@ -35,20 +35,25 @@ async def get_purchase_history(dialog_manager: DialogManager, **kwargs) -> Dict[ (_format_account_short_info(account), account.id) for account in purchased_accounts ], - "text": text + "text": text, } except SQLAlchemyError as e: logging.error(f"Database error: {e}", exc_info=True) - return {"purchased_accounts": [], "text": "❌ Произошла ошибка при работе с базой данных."} + return { + "purchased_accounts": [], + "text": "❌ Произошла ошибка при работе с базой данных.", + } except Exception as e: logging.error(f"An unexpected error occurred: {e}", exc_info=True) return {"purchased_accounts": [], "text": "❌ Произошла непредвиденная ошибка."} -async def get_history_account_details(dialog_manager: DialogManager, **kwargs) -> Dict[str, str]: +async def get_history_account_details( + dialog_manager: DialogManager, **kwargs +) -> Dict[str, str]: account_info = dialog_manager.current_context().dialog_data.get("account_info") if not account_info: logging.warning("No account info found in dialog data") return {"account_info": "❌ Информация об аккаунте недоступна."} - return {"account_info": account_info} \ No newline at end of file + return {"account_info": account_info} diff --git a/bot/getters/payment.py b/bot/getters/payment.py index 68d89d6..63788ba 100644 --- a/bot/getters/payment.py +++ b/bot/getters/payment.py @@ -1,10 +1,10 @@ import logging -from typing import Dict, Any -from urllib.parse import urlparse, parse_qs +from typing import Any, Dict +from urllib.parse import parse_qs, urlparse from aiogram_dialog import DialogManager -from bot.services import create_paylink_lolz, create_paylink_cryptobot +from shopbot.bot.services import create_paylink_cryptobot, create_paylink_lolz async def getter_amount(dialog_manager: DialogManager, **kwargs) -> Dict[str, Any]: @@ -21,10 +21,14 @@ async def getter_amount(dialog_manager: DialogManager, **kwargs) -> Dict[str, An return pay_data except Exception as e: logging.error(f"Error creating Lolz paylink: {e}", exc_info=True) - return {"error": "Произошла ошибка при создании ссылки для оплаты Lolz. Пожалуйста, попробуйте позже."} + return { + "error": "Произошла ошибка при создании ссылки для оплаты Lolz. Пожалуйста, попробуйте позже." + } -async def getter_amount_cryptobot(dialog_manager: DialogManager, **kwargs) -> Dict[str, Any]: +async def getter_amount_cryptobot( + dialog_manager: DialogManager, **kwargs +) -> Dict[str, Any]: dialog_manager.current_context().dialog_data.pop("pay_data", None) count = dialog_manager.find("count").get_value() @@ -39,4 +43,6 @@ async def getter_amount_cryptobot(dialog_manager: DialogManager, **kwargs) -> Di return pay_data except Exception as e: logging.error(f"Error creating CryptoBot paylink: {e}", exc_info=True) - return {"error": "Произошла ошибка при создании ссылки для оплаты через CryptoBot. Пожалуйста, проверьте настройки и попробуйте снова."} \ No newline at end of file + return { + "error": "Произошла ошибка при создании ссылки для оплаты через CryptoBot. Пожалуйста, проверьте настройки и попробуйте снова." + } diff --git a/bot/getters/user.py b/bot/getters/user.py index d99a7c5..b7cb26f 100644 --- a/bot/getters/user.py +++ b/bot/getters/user.py @@ -1,13 +1,13 @@ import logging -from typing import Dict, Any +from typing import Any, Dict from aiogram.types import User from aiogram_dialog import DialogManager from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.models import UserModel -from bot.database.db import async_session +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import UserModel async def username_getter( @@ -26,12 +26,14 @@ async def username_getter( "purchased": user.purchased, } else: - logging.warning(f"User with tg_id {event_from_user.id} not found in database.") + logging.warning( + f"User with tg_id {event_from_user.id} not found in database." + ) return { "username": event_from_user.username, "balance": 0, "purchased": 0, - } + } except SQLAlchemyError as e: logging.error(f"Database error: {e}", exc_info=True) return { @@ -42,7 +44,7 @@ async def username_getter( except Exception as e: logging.error(f"An unexpected error occurred: {e}", exc_info=True) return { - "username": event_from_user.username, - "balance": 0, - "purchased": 0, - } \ No newline at end of file + "username": event_from_user.username, + "balance": 0, + "purchased": 0, + } diff --git a/bot/services/broadcast.py b/bot/services/broadcast.py index 8b2e568..e983be4 100644 --- a/bot/services/broadcast.py +++ b/bot/services/broadcast.py @@ -1,22 +1,30 @@ import asyncio import logging +from aiogram.exceptions import ( + TelegramAPIError, + TelegramForbiddenError, + TelegramRetryAfter, +) from aiogram_dialog import DialogManager -from aiogram.exceptions import TelegramRetryAfter, TelegramForbiddenError, TelegramAPIError from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.db import async_session -from bot.database.models import UserModel +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import UserModel -async def _send_message_with_retry(bot, user_id: int, message_text: str, max_retries=3, delay=1) -> bool: +async def _send_message_with_retry( + bot, user_id: int, message_text: str, max_retries=3, delay=1 +) -> bool: for attempt in range(max_retries): try: await bot.send_message(user_id, message_text) return True except TelegramRetryAfter as e: - logging.warning(f"Retry after {e.retry_after} for user {user_id}, attempt {attempt + 1}") + logging.warning( + f"Retry after {e.retry_after} for user {user_id}, attempt {attempt + 1}" + ) await asyncio.sleep(e.retry_after) except TelegramForbiddenError: logging.warning(f"User {user_id} blocked bot") @@ -49,17 +57,13 @@ async def broadcast_message(dialog_manager: DialogManager, **kwargs): except SQLAlchemyError as e: logging.error(f"Database error: {e}") - return { - "progress": f"❌ Ошибка при работе с базой данных: {e}" - } + return {"progress": f"❌ Ошибка при работе с базой данных: {e}"} except Exception as e: logging.error(f"An unexpected error occurred: {e}") - return { - "progress": f"❌ Непредвиденная ошибка: {e}" - } + return {"progress": f"❌ Непредвиденная ошибка: {e}"} return { "progress": f"✅ Рассылка завершена!\n" - f"📨 Всего отправлено: {total_sent}/{total_users}\n" - f"❌ Ошибок: {total_failed}" - } \ No newline at end of file + f"📨 Всего отправлено: {total_sent}/{total_users}\n" + f"❌ Ошибок: {total_failed}" + } diff --git a/bot/services/buy_account.py b/bot/services/buy_account.py index 11e9f4d..37c6a39 100644 --- a/bot/services/buy_account.py +++ b/bot/services/buy_account.py @@ -7,9 +7,9 @@ from aiogram_dialog.widgets.kbd import Select from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.db import async_session -from bot.database.models import Account, UserModel -from bot.states import PurchaseAccountsSG +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import Account, UserModel +from shopbot.bot.states import PurchaseAccountsSG ACCOUNT_INFO_TEMPLATE = ( "Подробная информация об аккаунте:\n\n" diff --git a/bot/services/history.py b/bot/services/history.py index e574626..37ee405 100644 --- a/bot/services/history.py +++ b/bot/services/history.py @@ -1,14 +1,13 @@ import logging -from aiogram_dialog import DialogManager from aiogram.types import CallbackQuery +from aiogram_dialog import DialogManager from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from bot.database.db import async_session -from bot.database.models import Account -from bot.states import PurchaseHistorySG - +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import Account +from shopbot.bot.states import PurchaseHistorySG ACCOUNT_INFO_TEMPLATE = ( "Подробная информация об аккаунте:\n\n" @@ -43,7 +42,9 @@ async def _format_account_info(account: Account) -> str: ) -async def on_history_account_selected(callback: CallbackQuery, selecte, manager: DialogManager, item_id: int): +async def on_history_account_selected( + callback: CallbackQuery, selecte, manager: DialogManager, item_id: int +): try: if not isinstance(item_id, int): item_id = int(item_id) @@ -52,20 +53,24 @@ async def on_history_account_selected(callback: CallbackQuery, selecte, manager: async with async_session() as session: async with session.begin(): - account = await session.scalar(select(Account).where(Account.id == item_id)) - - if account: - manager.current_context().dialog_data["account_info"] = await _format_account_info(account) - else: - logging.warning(f"Account with id {item_id} not found") - manager.current_context().dialog_data["account_info"] = "Аккаунт не найден!" + account = await session.scalar( + select(Account).where(Account.id == item_id) + ) + if account: + manager.current_context().dialog_data[ + "account_info" + ] = await _format_account_info(account) + else: + logging.warning(f"Account with id {item_id} not found") + manager.current_context().dialog_data["account_info"] = ( + "Аккаунт не найден!" + ) await manager.switch_to(PurchaseHistorySG.details) - except SQLAlchemyError as e: logging.error(f"Database error: {e}", exc_info=True) except Exception as e: - logging.error(f"An unexpected error occurred: {e}", exc_info=True) \ No newline at end of file + logging.error(f"An unexpected error occurred: {e}", exc_info=True) diff --git a/bot/services/payments.py b/bot/services/payments.py index d7cdcc3..75ea465 100644 --- a/bot/services/payments.py +++ b/bot/services/payments.py @@ -1,15 +1,15 @@ import logging from typing import Tuple -from AsyncPayments.lolz import AsyncLolzteamMarketPayment -from AsyncPayments.cryptoBot import AsyncCryptoBot from aiogram.types import CallbackQuery from aiogram_dialog import DialogManager, StartMode from aiogram_dialog.widgets.kbd import Button - -from bot.database import update_balance +from AsyncPayments.cryptoBot import AsyncCryptoBot +from AsyncPayments.lolz import AsyncLolzteamMarketPayment from environs import Env -from bot.states import ProfileSG + +from shopbot.bot.database import update_balance +from shopbot.bot.states import ProfileSG env = Env() env.read_env() @@ -28,15 +28,35 @@ async def create_paylink_lolz(amount: int) -> str: async def create_paylink_cryptobot(amount: int) -> Tuple[str, str]: try: invoice = await cryptobot_payment.create_invoice( - amount=float(amount),currency_type="fiat", fiat="RUB", description="Пополнение баланса", accepted_assets=["USDT", "TON", "SOL", "BTC", "LTC", "ETH", "BNB", "TRX", "USDC"] + amount=float(amount), + currency_type="fiat", + fiat="RUB", + description="Пополнение баланса", + accepted_assets=[ + "USDT", + "TON", + "SOL", + "BTC", + "LTC", + "ETH", + "BNB", + "TRX", + "USDC", + ], ) return invoice.pay_url, str(invoice.invoice_id) except Exception as e: - if "AMOUNT_TOO_SMALL" in str(e): + if "AMOUNT_TOO_SMALL" in str(e): logging.error(f"Error creating CryptoBot invoice: {e}", exc_info=True) - return None, "Сумма для пополнения должна быть не менее 0.01 USD (примерно 1 руб). Пожалуйста, проверьте и попробуйте снова, либо выберите другой способ оплаты." - logging.error(f"Error creating CryptoBot invoice: {e}", exc_info=True) - return None, "Произошла ошибка при создании ссылки для оплаты через CryptoBot. Попробуйте позже." + return ( + None, + "Сумма для пополнения должна быть не менее 0.01 USD (примерно 1 руб). Пожалуйста, проверьте и попробуйте снова, либо выберите другой способ оплаты.", + ) + logging.error(f"Error creating CryptoBot invoice: {e}", exc_info=True) + return ( + None, + "Произошла ошибка при создании ссылки для оплаты через CryptoBot. Попробуйте позже.", + ) async def check_pay_lolz( @@ -44,14 +64,18 @@ async def check_pay_lolz( ): pay_data = manager.current_context().dialog_data.get("pay_data") if not pay_data: - await callback.message.answer("Ошибка: данные оплаты не найдены. Попробуйте еще раз. ❌") + await callback.message.answer( + "Ошибка: данные оплаты не найдены. Попробуйте еще раз. ❌" + ) return order_id = pay_data.get("order_id") amount = pay_data.get("amount") if not order_id or not amount: - await callback.message.answer("Ошибка: неверные данные оплаты. Попробуйте еще раз. ❌") + await callback.message.answer( + "Ошибка: неверные данные оплаты. Попробуйте еще раз. ❌" + ) return if manager.current_context().dialog_data.get("is_payment_processed", False): await callback.answer("Оплата уже обработана.", show_alert=True) @@ -65,13 +89,19 @@ async def check_pay_lolz( if status: manager.current_context().dialog_data["is_payment_processed"] = True await update_balance(callback.from_user.id, amount) - await callback.message.edit_text(f"Поздравляем, счет пополнен на {amount} руб 🎉") + await callback.message.edit_text( + f"Поздравляем, счет пополнен на {amount} руб 🎉" + ) else: - await callback.message.answer("Счет не оплачен, проверьте оплату или проверьте еще раз! ⚠️") + await callback.message.answer( + "Счет не оплачен, проверьте оплату или проверьте еще раз! ⚠️" + ) except Exception as e: logging.error(f"Error checking Lolz payment: {e}", exc_info=True) - await callback.message.answer("Произошла ошибка при проверке оплаты Lolz. Попробуйте позже. ❌") + await callback.message.answer( + "Произошла ошибка при проверке оплаты Lolz. Попробуйте позже. ❌" + ) async def check_pay_crypto_bot( @@ -80,14 +110,18 @@ async def check_pay_crypto_bot( pay_data = manager.current_context().dialog_data.get("pay_data") if not pay_data: - await callback.message.answer("Ошибка: данные для оплаты не найдены. Попробуйте позже. ❌") + await callback.message.answer( + "Ошибка: данные для оплаты не найдены. Попробуйте позже. ❌" + ) return order_id = pay_data.get("order_id") amount = pay_data.get("amount") if not order_id or not amount: - await callback.message.answer("Ошибка: неверные данные оплаты. Попробуйте еще раз. ❌") + await callback.message.answer( + "Ошибка: неверные данные оплаты. Попробуйте еще раз. ❌" + ) return if manager.current_context().dialog_data.get("is_payment_processed", False): await callback.answer("Оплата уже обработана.", show_alert=True) @@ -105,7 +139,9 @@ async def check_pay_crypto_bot( if invoice.status == "paid": manager.current_context().dialog_data["is_payment_processed"] = True await update_balance(callback.from_user.id, amount) - await callback.message.answer(f"Поздравляем, счет пополнен на {amount} руб 🎉") + await callback.message.answer( + f"Поздравляем, счет пополнен на {amount} руб 🎉" + ) await callback.message.delete() await manager.start(state=ProfileSG.profile, mode=StartMode.RESET_STACK) else: @@ -113,4 +149,6 @@ async def check_pay_crypto_bot( await callback.message.answer("Оплата не найдена. Попробуйте позже. ⏳") except Exception as e: logging.error(f"Error checking CryptoBot payment: {e}", exc_info=True) - await callback.message.answer("Произошла ошибка при проверке оплаты через CryptoBot. Попробуйте позже. ❌") \ No newline at end of file + await callback.message.answer( + "Произошла ошибка при проверке оплаты через CryptoBot. Попробуйте позже. ❌" + ) diff --git a/bot/services/stats.py b/bot/services/stats.py index 17df3e6..e787822 100644 --- a/bot/services/stats.py +++ b/bot/services/stats.py @@ -6,8 +6,8 @@ from aiogram_dialog import DialogManager from sqlalchemy import func, select from sqlalchemy.exc import SQLAlchemyError -from bot.database.db import async_session -from bot.database.models import Account, UserModel +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import Account, UserModel def _get_start_of_day(): diff --git a/bot/services/user.py b/bot/services/user.py index 27993fa..8c7c5c0 100644 --- a/bot/services/user.py +++ b/bot/services/user.py @@ -1,13 +1,13 @@ import logging -from typing import Dict, Any +from typing import Any, Dict from aiogram_dialog import DialogManager from sqlalchemy import select -from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession -from bot.database.models import UserModel -from bot.database.db import async_session +from shopbot.bot.database.db import async_session +from shopbot.bot.database.models import UserModel RESULT_MESSAGES = { "not_enough_funds": "❌ Недостаточно средств на балансе пользователя!", @@ -23,7 +23,7 @@ async def _get_user_and_validate( session: AsyncSession, user_id: str, amount: str ) -> Dict[str, Any]: if not user_id or not amount: - return {"result": RESULT_MESSAGES["no_user_id_or_amount"]} + return {"result": RESULT_MESSAGES["no_user_id_or_amount"]} try: user_id = int(user_id) amount = float(amount) @@ -59,14 +59,14 @@ async def take_balance_user(dialog_manager: DialogManager, **kwargs) -> Dict[str await session.commit() return { "result": RESULT_MESSAGES["balance_decreased"].format( - username=user.username, amount=amount - ) + username=user.username, amount=amount + ) } else: - return {"result": RESULT_MESSAGES["not_enough_funds"]} + return {"result": RESULT_MESSAGES["not_enough_funds"]} except SQLAlchemyError as e: - logging.error(f"Database error: {e}", exc_info=True) - return {"result": "❌ Произошла ошибка при работе с базой данных."} + logging.error(f"Database error: {e}", exc_info=True) + return {"result": "❌ Произошла ошибка при работе с базой данных."} except Exception as e: logging.error(f"An unexpected error occurred: {e}", exc_info=True) return {"result": "❌ Произошла непредвиденная ошибка."} @@ -83,16 +83,16 @@ async def gift_balance_data(dialog_manager: DialogManager, **kwargs) -> Dict[str user = user_data["user"] amount = user_data["amount"] try: - user.balance += amount - await session.commit() - return { - "result": RESULT_MESSAGES["balance_increased"].format( - username=user.username, amount=amount - ) - } + user.balance += amount + await session.commit() + return { + "result": RESULT_MESSAGES["balance_increased"].format( + username=user.username, amount=amount + ) + } except SQLAlchemyError as e: - logging.error(f"Database error: {e}", exc_info=True) - return {"result": "❌ Произошла ошибка при работе с базой данных."} + logging.error(f"Database error: {e}", exc_info=True) + return {"result": "❌ Произошла ошибка при работе с базой данных."} except Exception as e: - logging.error(f"An unexpected error occurred: {e}", exc_info=True) - return {"result": "❌ Произошла непредвиденная ошибка."} \ No newline at end of file + logging.error(f"An unexpected error occurred: {e}", exc_info=True) + return {"result": "❌ Произошла непредвиденная ошибка."} diff --git a/consumers/new_account.py b/consumers/new_account.py new file mode 100644 index 0000000..e0c3127 --- /dev/null +++ b/consumers/new_account.py @@ -0,0 +1,8 @@ +from faststream.nats import NatsRouter + +router = NatsRouter() + + +@router.subscriber("shop.new_account") +async def new_account_proceed(msg: int): + print(msg) diff --git a/hello.py b/hello.py index 2c350d3..102f9d6 100644 --- a/hello.py +++ b/hello.py @@ -1,4 +1,4 @@ -from bot.getters.filters import FilterType +from shopbot.bot.getters.filters import FilterType for x in FilterType: print(x.name, x.value)