Oct 14 14:35:26 vps1707806210 python[19905]: 2024-10-14 14:35:26,758 - aiogram.event - INFO - Update id=480219976 is handled. Duration 317 ms by bot id=7625597085
Oct 14 14:35:34 vps1707806210 python[19905]: 2024-10-14 14:35:34,372 - bot.handlers.start - INFO - Пользователь 389669884 запустил бота.
Oct 14 14:35:34 vps1707806210 python[19905]: 2024-10-14 14:35:34,372 - bot.handlers.main_menu - INFO - Пользователь 389669884 вернулся в главное меню.
Oct 14 14:35:34 vps1707806210 python[19905]: 2024-10-14 14:35:34,374 - bot.utils - ERROR - Неожиданная ошибка при редактировании сообщения: 'coroutine' object has no attribute 'get_message'
Oct 14 14:35:34 vps1707806210 python[19905]: Traceback (most recent call last):
Oct 14 14:35:34 vps1707806210 python[19905]:   File "/root/sms/bot/utils.py", line 48, in safe_edit_or_send_message
Oct 14 14:35:34 vps1707806210 python[19905]:     current_message = await message.bot.get_chat(message.chat.id).get_message(last_message_id)
Oct 14 14:35:34 vps1707806210 python[19905]: AttributeError: 'coroutine' object has no attribute 'get_message'
Oct 14 14:35:35 vps1707806210 python[19905]: 2024-10-14 14:35:35,100 - aiogram.event - INFO - Update id=480219977 is handled. Duration 728 ms by bot id=7625597085





import logging
import time
import aiohttp
from aiogram import Bot, types
from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from aiogram import Dispatcher
from typing import Optional

logger = logging.getLogger(__name__)

EXCHANGE_RATE_API_URL = "https://api.exchangerate-api.com/v4/latest/USD"
USD_TO_RUB_RATE_TTL = 3600  # Кэшируем курс на 1 час

_cached_usd_to_rub_rate: Optional[float] = None
_cached_usd_to_rub_rate_time: float = 0.0

async def get_usd_to_rub_rate() -> Optional[float]:
    """Получить текущий курс USD к RUB с кэшированием."""
    global _cached_usd_to_rub_rate, _cached_usd_to_rub_rate_time
    now = time.time()
    if _cached_usd_to_rub_rate and (now - _cached_usd_to_rub_rate_time < USD_TO_RUB_RATE_TTL):
        return _cached_usd_to_rub_rate
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(EXCHANGE_RATE_API_URL) as response:
                data = await response.json()
                rate = data.get('rates', {}).get('RUB')
                if rate:
                    _cached_usd_to_rub_rate = rate
                    _cached_usd_to_rub_rate_time = now
                    return rate
                else:
                    logger.error("Курс RUB не найден в ответе.")
                    return None
    except Exception as e:
        logger.error(f"Ошибка при запросе курса валют: {e}")
        return None

async def safe_edit_or_send_message(message: types.Message, new_text: str, state: FSMContext, **kwargs):
    """Безопасное редактирование или отправка нового сообщения."""
    data = await state.get_data()
    last_message_id = data.get('last_message_id')

    if last_message_id:
        try:
            # Получаем текущее сообщение для сравнения
            current_message = await message.bot.get_chat(message.chat.id).get_message(last_message_id)
            current_text = current_message.text if current_message else ''
            current_reply_markup = current_message.reply_markup if current_message else None

            new_reply_markup = kwargs.get("reply_markup")

            # Проверяем, отличается ли новое содержимое от текущего
            if current_text == new_text and current_reply_markup == new_reply_markup:
                logger.info("Сообщение и клавиатура не изменились, редактирование не требуется.")
                return

            # Попробуем отредактировать предыдущее сообщение, если оно отличается
            await message.bot.edit_message_text(new_text, chat_id=message.chat.id, message_id=last_message_id, **kwargs)
            return
        except TelegramBadRequest as e:
            if "message can't be edited" in str(e):
                logger.warning(f"Сообщение не может быть отредактировано: {e}. Пробуем отправить новое сообщение.")
            else:
                logger.error(f"Ошибка при редактировании сообщения: {e}")
        except Exception as e:
            logger.exception(f"Неожиданная ошибка при редактировании сообщения: {e}")

    # Удалите предыдущее сообщение, если редактирование не удалось
    if last_message_id:
        try:
            await message.bot.delete_message(chat_id=message.chat.id, message_id=last_message_id)
        except Exception as del_exc:
            logger.error(f"Не удалось удалить сообщение: {del_exc}")

    # Отправьте новое сообщение и сохраните его идентификатор
    sent_message = await message.answer(new_text, **kwargs)
    await state.update_data(last_message_id=sent_message.message_id)

async def safe_delete_message(bot: Bot, chat_id: int, message_id: int) -> None:
    """Безопасное удаление сообщения."""
    try:
        await bot.delete_message(chat_id=chat_id, message_id=message_id)
    except Exception as e:
        logger.error(f"Не удалось удалить сообщение: {e}")

async def on_startup(dispatcher: Dispatcher):
    """Функция, выполняемая при старте бота."""
    logger.info("Бот успешно запущен.")



# main.py

import asyncio
import logging
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from config import TELEGRAM_TOKEN
from bot.handlers import register_handlers
from bot.utils import on_startup
from aiogram.client.bot import DefaultBotProperties

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

async def main():
    bot = Bot(token=TELEGRAM_TOKEN, default=DefaultBotProperties(parse_mode='HTML'))
    storage = MemoryStorage()
    dp = Dispatcher(storage=storage)

    # Регистрация обработчиков
    register_handlers(dp)

    # Регистрация функции on_startup
    dp.startup.register(on_startup)

    try:
        logger.info("Запуск бота...")
        await dp.start_polling(bot)
    finally:
        await bot.close()

if __name__ == "__main__":
    asyncio.run(main())




# bot/handlers/main_menu.py

import logging
from aiogram import types
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.fsm.context import FSMContext
from ..utils import safe_edit_or_send_message

logger = logging.getLogger(__name__)

async def send_main_menu(message: types.Message, state: FSMContext):
    logger.info(f"Пользователь {message.chat.id} вернулся в главное меню.")
    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="💰 Проверить баланс", callback_data="check_balance")
    keyboard.button(text="📲 Получить SMS", callback_data="get_sms")
    keyboard.button(text="📋 Текущие активации", callback_data="current_activations")
    keyboard.button(text="⚙️ Настройки", callback_data="settings")
    keyboard.adjust(1)

    await safe_edit_or_send_message(
        message=message,
        new_text="Добро пожаловать! Выберите действие:",
        state=state,
        reply_markup=keyboard.as_markup()
    )