Ты — самый опытный Python-программист в мире. Твоя задача — помогать пользователям решать задачи на Python, генерируя безупречный, полностью работающий код и предоставляя точные, детализированные объяснения. Каждый код, который ты создаешь, должен быть не просто корректным, но и полностью готовым к использованию без необходимости доработки со стороны пользователя.

Твои ключевые качества:

Глубокий анализ задачи: Ты всегда досконально разбираешься в задаче, задаешь уточняющие вопросы, если нужно, и предлагаешь код, который полностью соответствует ожиданиям пользователя. Если задача недостаточно понятна, ты уточняешь все детали до начала генерации кода.

Создание цельного и рабочего кода: Ты пишешь код, который сразу можно скопировать и запустить без изменений. Никаких ошибок, упущенных зависимостей, синтаксических или логических проблем. Если код состоит из нескольких частей, ты объясняешь, как их объединить в одно целое решение.

Оптимизация производительности: Ты всегда предлагаешь наиболее эффективное решение по времени выполнения и ресурсам. Если есть более оптимальный способ решения, ты обязательно предлагаешь его.

Тщательная проверка кода: Ты проверяешь код тройной проверкой, не упуская ни одной ошибки. Ты всегда проверяешь код на предмет соответствия задаче и исключения возможных проблем до отправки.

Обработка ошибок и исключений: Ты всегда добавляешь корректную обработку ошибок, используя блоки try-except, и объясняешь, какие ошибки могут возникнуть и как их обработать.

Форматирование кода в черном окне Markdown: Каждый раз, когда ты представляешь код, он обязательно должен быть в формате Markdown с чёрным фоном для удобства копирования и правильными отступами. Также, над кодом, в том же самом окне, в виде комментария, обязательно прописывай название файла и директории где он находится или должен находиться.

Полноценные модули и решения: Ты всегда генерируешь полные модули или файлы, включая необходимые библиотеки, функции и классы для корректной работы.

Документация и аннотации типов: Каждая функция и класс должны содержать подробные docstring'и. Ты также добавляешь аннотации типов для всех параметров и возвращаемых значений.

Кросс-платформенность: Ты проверяешь код на совместимость с разными операционными системами и указываешь на возможные проблемы.

Контекстуальная память: Ты запоминаешь все предыдущие сообщения и решения, чтобы код оставался целостным и соответствовал правкам, сделанным ранее.

Проактивность: Ты предлагаешь решения, которые наилучшим образом подходят под задачу, и оптимизируешь существующий код.

Управление зависимостями: Ты проверяешь совместимость версий библиотек и предлагаешь requirements.txt для упрощения установки зависимостей.

Русский язык: Все объяснения и комментарии в коде должны быть на русском языке, понятные даже для новичков.

Обязательно запомни:

Твои решения должны работать с первого раза, без необходимости дальнейшей доработки.
Ты обязан писать код целиком (если не указано иное) и избегать фрагментированности.
Ты всегда проверяешь и перепроверяешь каждую строчку перед отправкой, чтобы гарантировать отсутствие ошибок.
Ты всегда форматируешь код в Markdown с чёрным фоном, чтобы его было легко скопировать.

После выбора активности ПРИ ВЫБОРЕ ЦЕЛИ бот перестает отвечать!Никаких ошибок в логах нет, вот логи: 2024-10-30 17:08:48,564 - main - INFO - Starting the bot. 2024-10-30 17:08:50,297 - telegram.ext.Application - INFO - Application started 2024-10-30 17:08:50,477 - main - INFO - Bot started polling. 2024-10-30 17:09:08,450 - user_reg_utils - INFO - Start command received 2024-10-30 17:09:13,102 - user_reg_utils - INFO - User entered name: Руслан 2024-10-30 17:09:23,312 - user_reg_utils - INFO - User provided location: Location(latitude=53.695228, longitude=91.679077) 2024-10-30 17:09:39,382 - user_reg_utils - INFO - User selected sex: male 2024-10-30 17:09:50,714 - user_reg_utils - INFO - User's age calculated as: 46 2024-10-30 17:09:56,308 - user_reg_utils - INFO - User's height set to: 185.0 2024-10-30 17:10:00,437 - user_reg_utils - INFO - User's weight set to: 100.0 2024-10-30 17:10:05,216 - user_reg_utils - INFO - User's activity level set to: SEDENTARY Разберись с этим! Вот текущие коды модулей: [Файл: 2d1597e4-0268-4587-8664-a38106f965a2_22___init__.py] [Файл: c2ed06d7-0f90-410d-8488-954991904bde_22_bot.py] [Файл: 3e94ce0f-6dcd-4427-869c-5aed92b130a5_22_config.py] [Файл: f8cf55e8-6308-4ec6-85b2-76728088e2e9_22_database.py] [Файл: 60caf44e-eab9-42f1-bd2e-d15f69d6c50b_22_llm.py] [Файл: f811671b-6a4e-4018-a3c5-4ea94b5800e9_22_memory.py] [Файл: 8477f897-9cd4-4baa-87bb-d6e9efefa5ee_22_user_reg_utils.py] [Файл: 4fbed2a2-995a-45a9-ac0a-2a6c855b5edb_22_utils.py]

Файл f811671b-6a4e-4018-a3c5-4ea94b5800e9_22_memory.py (обновлен 2024-10-30 17:23:41):
# fitness_bot/memory.py

import requests
import numpy as np
from typing import List, Dict
import os
from dotenv import load_dotenv

# Загружаем переменные окружения из .env файла
load_dotenv()

# Загрузка API-ключа и прокси
api_key = os.getenv("OPENAI_API_KEY")
proxy_http = os.getenv("PROXY_HTTP")
proxy_https = os.getenv("PROXY_HTTPS")

if not api_key:
    raise ValueError("OpenAI API key is not set. Please ensure it is set in the .env file or as an environment variable.")

# Настройки прокси и заголовков
proxies = {
    "http": proxy_http,
    "https": proxy_https,
}
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

EMBEDDING_MODEL = "text-embedding-3-small"  # Используем самую современную модель


def get_embedding(text: str) -> List[float]:
    """
    Получает векторное представление (эмбеддинг) текста 
    с использованием модели text-embedding-3-small через прокси.

    Args:
        text: Текст для преобразования в эмбеддинг.

    Returns:
        Векторное представление текста (список чисел с плавающей точкой).
    """
    data = {
        "input": [text],
        "model": EMBEDDING_MODEL
    }

    try:
        response = requests.post(
            "https://api.openai.com/v1/embeddings",
            headers=headers,
            json=data,
            proxies=proxies
        )
        response.raise_for_status()
        return response.json()["data"][0]["embedding"]
    except requests.exceptions.RequestException as e:
        raise ValueError(f"Error getting embedding: {e}")


def normalize_embedding(embedding: List[float]) -> List[float]:
    """
    Нормализует вектор эмбеддинга с помощью L2-нормализации.

    Args:
        embedding: Вектор эмбеддинга.

    Returns:
        Нормализованный вектор эмбеддинга.
    """
    embedding_np = np.array(embedding)
    norm = np.linalg.norm(embedding_np)
    if norm == 0:
        return embedding
    return list(embedding_np / norm)


def calculate_similarity(embedding1: List[float], embedding2: List[float]) -> float:
    """
    Вычисляет косинусное сходство между двумя нормализованными векторами 
    (эмбеддингами).

    Args:
        embedding1: Первый нормализованный вектор.
        embedding2: Второй нормализованный вектор.

    Returns:
        Косинусное сходство (число от -1 до 1).
    """
    return np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2))


def update_context(
    user_context: List[Dict], new_message: str, max_context_length: int = 10
) -> List[Dict]:
    """
    Обновляет контекст пользователя, добавляя новое сообщение
    и удаляя наименее релевантные сообщения, если контекст превышает
    максимальную длину.

    Args:
        user_context: Текущий контекст пользователя (список словарей {"role": ..., "content": ...}).
        new_message: Новое сообщение.
        max_context_length: Максимальная длина контекста (количество сообщений).

    Returns:
        Обновленный контекст пользователя.
    """
    new_message_embedding = normalize_embedding(get_embedding(new_message))
    user_context.append(
        {"role": "user", "content": new_message, "embedding": new_message_embedding}
    )

    if len(user_context) > max_context_length:
        # Удаляем наименее релевантные сообщения из контекста
        similarities = []
        for i, message in enumerate(user_context[:-1]):  # Исключаем последнее сообщение
            similarity = calculate_similarity(
                new_message_embedding, message["embedding"]
            )
            similarities.append((similarity, i))

        # Сортируем по возрастанию сходства
        similarities.sort()

        # Удаляем сообщения с наименьшим сходством
        num_to_remove = len(user_context) - max_context_length
        indices_to_remove = [index for _, index in similarities[:num_to_remove]]

        # Удаляем сообщения в обратном порядке индексов, чтобы не нарушить порядок
        for index in sorted(indices_to_remove, reverse=True):
            del user_context[index]

    return user_context


Файл 8477f897-9cd4-4baa-87bb-d6e9efefa5ee_22_user_reg_utils.py (обновлен 2024-10-30 17:23:41):
# fitness_bot/user_reg_utils.py
"""
Модуль, содержащий функции для регистрации пользователя.
"""

import logging
import json
from datetime import datetime
import telegram

from telegram import Update, ForceReply, ReplyKeyboardMarkup, ReplyKeyboardRemove, constants  # Добавляем constants
from telegram.ext import CallbackContext, ConversationHandler
from timezonefinder import TimezoneFinder

from utils import (
    calculate_age,
    calculate_current_calories,
    calculate_target_calories,
    calculate_target_pulse,
    get_calorie_recommendation,
    get_reference_pulse,
)
from config import (
    NAME,
    LOCATION,
    SEX,
    BIRTHDATE,
    HEIGHT,
    WEIGHT,
    ACTIVITY_LEVEL,
    FITNESS_GOAL,
    ACTIVITY_PERIOD,
    STANDARD_PULSE,
    sex_dict,
    activity_dict,
    fitness_goals_dict,
    activity_period_dict,
)
from llm import process_text
from database import Database
from memory import update_context

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

# Снижение уровня логирования для http-запросов
logging.getLogger("httpx").setLevel(logging.WARNING)

# Создание объекта базы данных
db = Database()


async def start(update: Update, context: CallbackContext) -> int:
    """Обрабатывает команду /start и приветствует пользователя."""
    logger.info("Start command received")
    await update.message.reply_text("Добро пожаловать! Пожалуйста, подождите...")

    try:
        user_id = update.effective_user.id
        user = db.get_user(user_id)

        if user:
            # Загружаем контекст пользователя из базы данных, если он существует
            context.user_data["context"] = json.loads(user["context"]) if user["context"] else []
            await update.message.reply_text(
                f"Рад снова видеть тебя, {user['name']}! Используй /register для обновления данных.",
                reply_markup=ReplyKeyboardRemove(),
            )
            logger.info(f"User {user_id} already registered.")
            return ConversationHandler.END

        context.user_data["context"] = []
        await update.message.reply_text(
            "Как тебя зовут?", reply_markup=ForceReply()
        )
        return NAME
    except Exception as e:
        logger.error(f"Error in start function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return ConversationHandler.END


async def handle_message(update: Update, context: CallbackContext) -> None:
    """Обрабатывает обычные текстовые сообщения пользователя."""
    user_id = update.effective_user.id
    text = update.message.text
    logger.info(f"Handling message from user {user_id}: {text}")

    try:
        # Показать индикатор активности
        await context.bot.send_chat_action(chat_id=update.effective_chat.id, action=telegram.ChatAction.TYPING)

        user = db.get_user(user_id)
        user_context = json.loads(user["context"]) if user and user["context"] else []

        logger.info(f"User context before update: {user_context}")  # логирование контекста до обновления
        updated_context = update_context(user_context, text)
        logger.info(f"User context after update: {updated_context}")  # логирование контекста после обновления

        logger.info(f"Sending text to LLM: {text}")  # логирование перед отправкой текста в LLM
        response, _ = process_text(text, updated_context, user_id)
        logger.info(f"Received response from LLM: {response}")  # логирование после получения ответа от LLM

        db.update_user(user_id, {"context": json.dumps(updated_context)})
        await update.message.reply_text(response)
    except Exception as e:
        logger.error(f"Error in handle_message function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")


async def name(update: Update, context: CallbackContext) -> int:
    """Запрашивает имя пользователя."""
    user_name = update.message.text.strip()
    if not user_name:
        await update.message.reply_text("Имя не может быть пустым. Пожалуйста, введи свое имя:")
        return NAME
    logger.info(f"User entered name: {user_name}")
    context.user_data["name"] = user_name

    await update.message.reply_text(
        f"Приятно познакомиться, {user_name}! Отправь мне свою геолокацию, нажав на кнопку 'Прикрепить' -> 'Местоположение'.",
        reply_markup=ReplyKeyboardRemove(),  # Убираем ForceReply
    )
    return LOCATION

async def location(update: Update, context: CallbackContext) -> int:
    """Обрабатывает геолокацию пользователя и определяет его часовой пояс и климат."""

    try:
        user_location = update.message.location
        logger.info(f"User provided location: {user_location}")
        context.user_data["location"] = user_location

        # Определение часового пояса по координатам
        tf = TimezoneFinder()
        latitude, longitude = user_location.latitude, user_location.longitude
        time_zone = tf.timezone_at(lng=longitude, lat=latitude)
        context.user_data["time_zone"] = time_zone

        # Получение текущего времени года
        current_month = datetime.now().month
        season = (
            "зима" if current_month in [12, 1, 2] else
            "весна" if current_month in [3, 4, 5] else
            "лето" if current_month in [6, 7, 8] else
            "осень"
        )

        # Запрос на климат
        # Показать индикатор активности
        await context.bot.send_chat_action(
            chat_id=update.effective_chat.id, action=constants.ChatAction.TYPING  # Используем constants.ChatAction
        )

        prompt = f"Какой климат в районе широты {latitude} и долготы {longitude} в {season}?"
        climate_response, _ = process_text(prompt, context.user_data.get("context", []), update.effective_user.id)
        context.user_data["climate"] = climate_response.upper()  # Приводим к верхнему регистру

        await update.message.reply_text(
            f"Твой часовой пояс - {time_zone}. Климат в твоем регионе: {climate_response}."
        )

        reply_keyboard = [[f"/{key} - {value}"] for key, value in sex_dict.items()]
        await update.message.reply_text(
            "Теперь выбери свой пол, нажав на соответствующую кнопку:",
            reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True, resize_keyboard=True),
        )
        return SEX
    except Exception as e:
        logger.error(f"Error in location function: {e}")
        await update.message.reply_text("Произошла ошибка при обработке геолокации. Пожалуйста, попробуйте еще раз.")
        return LOCATION

async def sex(update: Update, context: CallbackContext) -> int:
    """Запрашивает пол пользователя и обрабатывает ответ."""
    try:
        user_sex = update.message.text.lower().split(" - ")[0].lstrip("/")
        if user_sex in sex_dict:
            logger.info(f"User selected sex: {user_sex}")
            context.user_data["sex"] = user_sex
            await update.message.reply_text("Укажи дату рождения в формате YYYY-MM-DD.")
            return BIRTHDATE
        else:
            await update.message.reply_text("Пожалуйста, выбери пол из списка.")
            return SEX
    except Exception as e:
        logger.error(f"Error in sex function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return SEX


async def birthdate(update: Update, context: CallbackContext) -> int:
    """Обрабатывает дату рождения пользователя."""
    try:
        birthdate_str = update.message.text
        age = calculate_age(birthdate_str)
        context.user_data["age"] = age
        logger.info(f"User's age calculated as: {age}")
        await update.message.reply_text(
            f"Твой возраст: {age} лет. Теперь введи свой рост в сантиметрах.",
            reply_markup=ForceReply(),
        )
        return HEIGHT
    except ValueError:
        await update.message.reply_text(
            "Некорректный формат даты. Введи дату в формате YYYY-MM-DD.",
            reply_markup=ForceReply(),
        )
        return BIRTHDATE
    except Exception as e:
        logger.error(f"Error in birthdate function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return BIRTHDATE


async def height(update: Update, context: CallbackContext) -> int:
    """Обрабатывает рост пользователя."""
    try:
        height = float(update.message.text)
        context.user_data["height"] = height
        logger.info(f"User's height set to: {height}")
        await update.message.reply_text(
            "Введи свой вес в килограммах.", reply_markup=ForceReply()
        )
        return WEIGHT
    except ValueError:
        await update.message.reply_text(
            "Некорректный ввод. Введи рост в сантиметрах (число).",
            reply_markup=ForceReply(),
        )
        return HEIGHT
    except Exception as e:
        logger.error(f"Error in height function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return HEIGHT


async def weight(update: Update, context: CallbackContext) -> int:
    """Обрабатывает вес пользователя."""
    try:
        weight = float(update.message.text)
        context.user_data["weight"] = weight
        logger.info(f"User's weight set to: {weight}")
        await update.message.reply_text(
            "Выбери свой уровень активности:", reply_markup=ForceReply()
        )
        for key, value in activity_dict.items():
            await update.message.reply_text(f"/{key} - {value}")
        return ACTIVITY_LEVEL
    except ValueError:
        await update.message.reply_text(
            "Некорректный ввод. Введи вес в килограммах (число).",
            reply_markup=ForceReply(),
        )
        return WEIGHT
    except Exception as e:
        logger.error(f"Error in weight function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return WEIGHT


async def activity_level(update: Update, context: CallbackContext) -> int:
    """Обрабатывает уровень активности пользователя."""
    try:
        activity_level = update.message.text.lstrip("/").upper()
        if activity_level in activity_dict:
            context.user_data["activity_level"] = activity_level
            logger.info(f"User's activity level set to: {activity_level}")
            await update.message.reply_text("Выбери свою цель фитнеса:")
            for key, value in fitness_goals_dict.items():
                await update.message.reply_text(f"/{key} - {value}")
            return FITNESS_GOAL
        else:
            await update.message.reply_text("Выбери уровень активности из предложенных вариантов.")
            return ACTIVITY_LEVEL
    except Exception as e:
        logger.error(f"Error in activity_level function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return ACTIVITY_LEVEL


async def fitness_goal(update: Update, context: CallbackContext) -> int:
    """Обрабатывает цель фитнеса пользователя."""
    try:
        fitness_goal = update.message.text.lstrip("/").upper()
        if fitness_goal in fitness_goals_dict:
            context.user_data["fitness_goal"] = fitness_goal
            logger.info(f"User's fitness goal set to: {fitness_goal}")

            # Создаем клавиатуру с кнопками-командами для выбора периода активности
            reply_keyboard = [[f"/{key} - {value}"] for key, value in activity_period_dict.items()]
            await update.message.reply_text(
                "Выбери свой период активности (сон/бодрствование):",
                reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True, resize_keyboard=True),
            )
            return ACTIVITY_PERIOD  # Переходим к состоянию ACTIVITY_PERIOD
        else:
            await update.message.reply_text("Выбери цель фитнеса из предложенных вариантов.")
            return FITNESS_GOAL
    except Exception as e:
        logger.error(f"Error in fitness_goal function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return FITNESS_GOAL


async def activity_period(update: Update, context: CallbackContext) -> int:
    """Обрабатывает период активности пользователя."""
    try:
        activity_period = update.message.text.lstrip("/").upper()
        if activity_period in activity_period_dict:
            context.user_data["activity_period"] = activity_period
            logger.info(f"User's activity period set to: {activity_period}")
            await update.message.reply_text("Введи свой стандартный пульс в состоянии покоя (число ударов в минуту):")
            return STANDARD_PULSE
        else:
            await update.message.reply_text("Выбери период активности из предложенных вариантов.")
            return ACTIVITY_PERIOD
    except Exception as e:
        logger.error(f"Error in activity_period function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return ACTIVITY_PERIOD


async def standard_pulse(update: Update, context: CallbackContext) -> int:
    """Обрабатывает стандартный пульс пользователя."""
    try:
        standard_pulse = int(update.message.text)
        context.user_data["standard_pulse"] = standard_pulse
        user_id = update.effective_user.id
        user_data = context.user_data.copy()
        user_data["telegram_id"] = user_id

        user = db.get_user(user_id)
        if user:
            db.update_user(user_id, user_data)
        else:
            db.create_user(user_data)

        current_calories = calculate_current_calories(user_data)
        target_calories = calculate_target_calories(user_data)
        target_pulse = calculate_target_pulse(user_data)
        reference_pulse = get_reference_pulse(user_data["age"], user_data["sex"])

        db.update_user(user_id, {
            "current_calories": current_calories,
            "target_calories": target_calories,
            "target_pulse": target_pulse,
        })

        recommendation = get_calorie_recommendation(user_data)
        await update.message.reply_text(
            f"Спасибо, {user_data['name']}! Твои данные сохранены.\n"
            f"Текущий расход калорий: {current_calories:.2f} ккал\n"
            f"Эталонный расход калорий: {target_calories:.2f} ккал\n"
            f"Эталонный пульс в покое: {reference_pulse} уд/мин\n"
            f"Рекомендация по калориям: {recommendation}"
        )
        return ConversationHandler.END
    except ValueError:
        await update.message.reply_text("Введи стандартный пульс (число ударов в минуту).")
        return STANDARD_PULSE
    except Exception as e:
        logger.error(f"Error in standard_pulse function: {e}")
        await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз.")
        return STANDARD_PULSE


async def cancel(update: Update, context: CallbackContext) -> int:
    """Отменяет диалог."""
    logger.info("User canceled the dialog.")
    await update.message.reply_text("Диалог отменен.")
    return ConversationHandler.END


async def error_handler(update: Update, context: CallbackContext) -> None:
    """Обрабатывает ошибки, возникшие в процессе работы бота."""
    logger.error(f'Update "{update}" caused error "{context.error}"')
    await update.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте еще раз позже.")

Файл 4fbed2a2-995a-45a9-ac0a-2a6c855b5edb_22_utils.py (обновлен 2024-10-30 17:23:41):
# fitness_bot/utils.py
"""
Модуль для вычисления показателей, связанных с фитнесом.
"""
from typing import Dict, Any
from datetime import datetime
from config import ACTIVITY_FACTORS, CLIMATE_FACTORS, ACTIVITY_PERIOD_FACTORS

def calculate_current_calories(user_data: Dict[str, Any]) -> float:
    """
    Вычисляет текущий расход калорий пользователя,
    используя формулу Миффлина-Сан Жеора,
    учитывая период бодрствования/сна и климат.

    Args:
        user_data: Словарь с данными пользователя.

    Returns:
        Текущий расход калорий.
    """
    # Получаем данные пользователя
    sex = user_data["sex"]
    age = user_data["age"]
    weight = user_data["weight"]
    height = user_data["height"]
    activity_level = user_data["activity_level"]
    activity_period = user_data["activity_period"]
    climate = user_data["climate"]

    # Базовый метаболизм (BMR)
    if sex == "male":
        bmr = 10 * weight + 6.25 * height - 5 * age + 5
    else:  # female
        bmr = 10 * weight + 6.25 * height - 5 * age - 161

    # Коэффициент активности
    activity_factor = ACTIVITY_FACTORS.get(activity_level, 1.2)

    # Коэффициент периода активности
    activity_period_factor = ACTIVITY_PERIOD_FACTORS.get(activity_period, 1.0)

    # Коэффициент климата
    climate_factor = CLIMATE_FACTORS.get(climate, 1.0)

    # Текущий расход калорий
    current_calories = bmr * activity_factor * activity_period_factor * climate_factor

    return current_calories


def calculate_target_calories(user_data: Dict[str, Any]) -> float:
    """
    Вычисляет эталонный расход калорий пользователя,
    учитывая цель фитнеса.

    Args:
        user_data: Словарь с данными пользователя.

    Returns:
        Эталонный расход калорий.
    """
    current_calories = calculate_current_calories(user_data)
    fitness_goal = user_data["fitness_goal"]

    # Корректировка калорий в зависимости от цели
    if fitness_goal == "LOSS_WEIGHT":
        target_calories = current_calories - 500  # Дефицит 500 ккал для похудения
    elif fitness_goal == "BUILD_MUSCLE":
        target_calories = current_calories + 500  # Профицит 500 ккал для набора массы
    else:
        target_calories = current_calories  # Поддержание веса

    return target_calories


def get_calorie_recommendation(user_data: Dict[str, Any]) -> str:
    """
    Формирует рекомендацию по потреблению/сжиганию калорий
    в зависимости от цели пользователя.

    Args:
        user_data: Словарь с данными пользователя.

    Returns:
        Рекомендация по калориям.
    """
    current_calories = user_data["current_calories"]
    target_calories = user_data["target_calories"]
    fitness_goal = user_data["fitness_goal"]

    if fitness_goal == "LOSS_WEIGHT":
        calorie_difference = current_calories - target_calories
        recommendation = (
            f"Для достижения цели '{fitness_goal}' вам необходимо создать "
            f"дефицит {calorie_difference:.2f} ккал в день. "
            "Это можно сделать, уменьшив потребление калорий "
            "или увеличив физическую активность."
        )
    elif fitness_goal == "BUILD_MUSCLE":
        calorie_difference = target_calories - current_calories
        recommendation = (
            f"Для достижения цели '{fitness_goal}' вам необходимо создать "
            f"профицит {calorie_difference:.2f} ккал в день. "
            "Это можно сделать, увеличив потребление калорий, "
            "особенно белка, и регулярно занимаясь силовыми тренировками."
        )
    else:
        recommendation = (
            f"Для достижения цели '{fitness_goal}' вам необходимо "
            "поддерживать баланс между потреблением и расходом калорий."
        )

    return recommendation


def calculate_target_pulse(user_data: Dict[str, Any]) -> float:
    """
    Вычисляет эталонное значение пульса пользователя
    в состоянии покоя.

    Args:
        user_data: Словарь с данными пользователя.

    Returns:
        Эталонное значение пульса.
    """
    age = user_data["age"]
    sex = user_data["sex"]  # Добавляем пол для расчета эталонного пульса

    return get_reference_pulse(age, sex)  # Возвращаем эталонный пульс


def calculate_age(birthdate: str) -> int:
    """
    Вычисляет возраст пользователя на основе даты рождения.

    Args:
        birthdate: Дата рождения в формате YYYY-MM-DD.

    Returns:
        Возраст пользователя.
    """
    today = datetime.today()
    birthdate_dt = datetime.strptime(birthdate, "%Y-%m-%d")
    age = today.year - birthdate_dt.year
    if today.month < birthdate_dt.month or (
        today.month == birthdate_dt.month and today.day < birthdate_dt.day
    ):
        age -= 1
    return age


def get_reference_pulse(age: int, sex: str) -> int:
    """
    Возвращает эталонное значение пульса в состоянии покоя
    для заданного возраста и пола.

    Args:
        age: Возраст пользователя.
        sex: Пол пользователя ("male" или "female").

    Returns:
        Эталонное значение пульса.
    """
    if 18 <= age <= 25:
        reference_pulse = 65  # Среднее значение 60-70
    elif 26 <= age <= 35:
        reference_pulse = 68  # Среднее значение 60-75
    elif 36 <= age <= 45:
        reference_pulse = 73  # Среднее значение 65-80
    elif 46 <= age <= 55:
        reference_pulse = 78  # Среднее значение 70-85
    elif 56 <= age <= 65:
        reference_pulse = 83  # Среднее значение 75-90
    else:
        reference_pulse = 88  # Среднее значение 80-95

    if sex == "female":
        reference_pulse += 7  # Добавляем 7 ударов для женщин

    return reference_pulse

Файл 2d1597e4-0268-4587-8664-a38106f965a2_22___init__.py (обновлен 2024-10-30 17:23:40):


Файл c2ed06d7-0f90-410d-8488-954991904bde_22_bot.py (обновлен 2024-10-30 17:23:40):
# fitness_bot/bot.py

import logging
import os
import asyncio
import telegram

from telegram.ext import (
    Application,
    CommandHandler,
    MessageHandler,
    filters,
    ConversationHandler,
)

from user_reg_utils import (
    start,
    handle_message,
    name,
    location,
    sex,
    birthdate,
    height,
    weight,
    activity_level,
    fitness_goal,
    activity_period,
    standard_pulse,
    cancel,
    error_handler,
)

from dotenv import load_dotenv
from config import (
    NAME,
    LOCATION,
    SEX,
    BIRTHDATE,
    HEIGHT,
    WEIGHT,
    ACTIVITY_LEVEL,
    FITNESS_GOAL,
    ACTIVITY_PERIOD,
    STANDARD_PULSE,
)

# Загружаем переменные окружения из .env файла
load_dotenv()

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

# Снижение уровня логирования http-запросов
logging.getLogger("httpx").setLevel(logging.WARNING)

# Получение токена Telegram из переменных окружения
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN")

async def main() -> None:
    """Запускает бота."""
    logger.info("Starting the bot.")
    application = Application.builder().token(TELEGRAM_TOKEN).build()

    # Создание обработчика для команд и сообщений
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler("start", start)],
        states={
            NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, name)],
            LOCATION: [MessageHandler(filters.LOCATION, location)],
            SEX: [MessageHandler(filters.COMMAND, sex)],
            BIRTHDATE: [MessageHandler(filters.TEXT & ~filters.COMMAND, birthdate)],
            HEIGHT: [MessageHandler(filters.TEXT & ~filters.COMMAND, height)],
            WEIGHT: [MessageHandler(filters.TEXT & ~filters.COMMAND, weight)],
            ACTIVITY_LEVEL: [MessageHandler(filters.COMMAND, activity_level)],
            FITNESS_GOAL: [MessageHandler(filters.TEXT & ~filters.COMMAND, fitness_goal)],
            ACTIVITY_PERIOD: [MessageHandler(filters.COMMAND, activity_period)],  # Изменено на filters.COMMAND
            STANDARD_PULSE: [MessageHandler(filters.TEXT & ~filters.COMMAND, standard_pulse)],
        },
        fallbacks=[CommandHandler("cancel", cancel)],
    )
    
    application.add_handler(conv_handler)
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
    application.add_error_handler(error_handler)

    # Запуск бота
    await application.initialize()
    await application.bot.delete_webhook()
    await application.start()
    await application.updater.start_polling()
    logger.info("Bot started polling.")

    # Удерживаем бота в работе
    try:
        while True:
            await asyncio.sleep(3600)
    except KeyboardInterrupt:
        await application.stop()
        logger.info("Bot stopped.")

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

Файл 3e94ce0f-6dcd-4427-869c-5aed92b130a5_22_config.py (обновлен 2024-10-30 17:23:40):
# fitness_bot/config.py
"""
Модуль для хранения констант и конфигураций.
"""

# Константы для состояний диалога
NAME = 0
LOCATION = 1
SEX = 2
BIRTHDATE = 3
HEIGHT = 4
WEIGHT = 5
ACTIVITY_LEVEL = 6
FITNESS_GOAL = 7
ACTIVITY_PERIOD = 8
STANDARD_PULSE = 9


ACTIVITY_FACTORS = {
    "SEDENTARY": 1.2,
    "LIGHTLY_ACTIVE": 1.375,
    "MODERATELY_ACTIVE": 1.55,
    "VERY_ACTIVE": 1.725,
    "EXTRA_ACTIVE": 1.9,
}

CLIMATE_FACTORS = {
    "COLD": 1.1,
    "MODERATE": 1.0,
    "HOT": 0.9,
}

ACTIVITY_PERIOD_FACTORS = {
    "MOSTLY_SLEEPING": 0.9,
    "MOSTLY_SITTING": 1.0,
    "LIGHT_ACTIVITY": 1.1,
    "MODERATE_ACTIVITY": 1.2,
    "HIGH_ACTIVITY": 1.3,
}

# Словари для выбора пола, уровня активности и цели фитнеса
sex_dict = {"male": "Мужчина", "female": "Женщина"}
activity_dict = {
    "SEDENTARY": "Минимальная физическая активность, офисная работа",
    "LIGHTLY_ACTIVE": "Легкие прогулки, не напряжные домашние дела",
    "MODERATELY_ACTIVE": "Умеренные тренировки или физическая работа 3-4 раза в неделю",
    "VERY_ACTIVE": "Ежедневная активность, спорт или тренировки 5-6 раз в неделю",
    "EXTRA_ACTIVE": "Интенсивные тренировки, физически тяжелая работа или профессиональный спорт",
}
fitness_goals_dict = {
    "LOSS_WEIGHT": "Похудеть",
    "BUILD_MUSCLE": "Набрать мышцы",
    "MUSCLE_STRENGTH": "Увеличить силу мышц",
    "KEEP_WEIGHT": "Поддержать вес на одном уровне",
}
activity_period_dict = {
    "MOSTLY_SLEEPING": "В основном сплю",
    "MOSTLY_SITTING": "В основном сижу",
    "LIGHT_ACTIVITY": "Легкая активность",
    "MODERATE_ACTIVITY": "Умеренная активность",
    "HIGH_ACTIVITY": "Высокая активность",
}

Файл f8cf55e8-6308-4ec6-85b2-76728088e2e9_22_database.py (обновлен 2024-10-30 17:23:40):
# fitness_bot/database.py
"""
Модуль для работы с базой данных SQLite.
"""

import sqlite3
from typing import Dict, Any, Optional


class Database:
    """
    Класс для работы с базой данных SQLite.
    """

    def __init__(self, db_name: str = "fitness_bot.db"):
        """
        Инициализирует объект базы данных.

        Args:
            db_name: Имя файла базы данных.
        """
        self.db_name = db_name
        self.conn = sqlite3.connect(self.db_name)
        self.cursor = self.conn.cursor()
        self._create_users_table()

    def _create_users_table(self) -> None:
        """
        Создает таблицу users, если она не существует.
        """
        self.cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                telegram_id INTEGER UNIQUE NOT NULL,
                name TEXT,
                location TEXT,
                time_zone TEXT,
                climate TEXT,
                sex TEXT,
                age INTEGER,
                height REAL,
                weight REAL,
                activity_level TEXT,
                fitness_goal TEXT,
                activity_period TEXT,
                standard_pulse INTEGER,
                current_calories REAL,
                target_calories REAL,
                target_pulse REAL,
                context TEXT
            )
            """
        )
        self.conn.commit()

    def create_user(self, user_data: Dict[str, Any]) -> None:
        """
        Создает новую запись пользователя в базе данных.

        Args:
            user_data: Словарь с данными пользователя.
        """

        # Проверяем, есть ли уже пользователь с таким telegram_id
        existing_user = self.get_user(user_data["telegram_id"])
        if existing_user:
            return  # Если пользователь уже существует, ничего не делаем

        self.cursor.execute(
            """
            INSERT INTO users (telegram_id, name, location, time_zone, climate, sex, age, height, weight, activity_level, fitness_goal, activity_period, standard_pulse, current_calories, target_calories, target_pulse, context)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                user_data["telegram_id"],
                user_data["name"],
                user_data["location"],
                user_data["time_zone"],
                user_data["climate"],
                user_data["sex"],
                user_data["age"],
                user_data["height"],
                user_data["weight"],
                user_data["activity_level"],
                user_data["fitness_goal"],
                user_data["activity_period"],
                user_data["standard_pulse"],
                user_data.get("current_calories", None),  # Добавляем current_calories
                user_data.get("target_calories", None),  # Добавляем target_calories
                user_data.get("target_pulse", None),      # Добавляем target_pulse
                user_data.get("context", None)          # Добавляем context
            ),
        )
        self.conn.commit()

    def get_user(self, telegram_id: int) -> Optional[Dict[str, Any]]:
        """
        Получает данные пользователя по его Telegram ID.

        Args:
            telegram_id: Telegram ID пользователя.

        Returns:
            Словарь с данными пользователя или None, если пользователь не найден.
        """
        self.cursor.execute("SELECT * FROM users WHERE telegram_id = ?", (telegram_id,))
        row = self.cursor.fetchone()
        if row:
            return {
                "id": row[0],
                "telegram_id": row[1],
                "name": row[2],
                "location": row[3],
                "time_zone": row[4],
                "climate": row[5],
                "sex": row[6],
                "age": row[7],
                "height": row[8],
                "weight": row[9],
                "activity_level": row[10],
                "fitness_goal": row[11],
                "activity_period": row[12],
                "standard_pulse": row[13],
                "current_calories": row[14],
                "target_calories": row[15],
                "target_pulse": row[16],
                "context": row[17]
            }
        return None

    def update_user(self, telegram_id: int, data: Dict[str, Any]) -> None:
        """
        Обновляет данные пользователя.

        Args:
            telegram_id: Telegram ID пользователя.
            data: Словарь с данными для обновления.
        """
        set_clause = ", ".join([f"{key} = ?" for key in data.keys()])
        values = list(data.values()) + [telegram_id]
        self.cursor.execute(
            f"UPDATE users SET {set_clause} WHERE telegram_id = ?", values
        )
        self.conn.commit()

    def close(self) -> None:
        """
        Закрывает соединение с базой данных.
        """
        self.conn.close()

Файл 60caf44e-eab9-42f1-bd2e-d15f69d6c50b_22_llm.py (обновлен 2024-10-30 17:23:40):
# fitness_bot/llm.py

# fitness_bot/llm.py

import os
import requests
from typing import Dict, Any, Optional, List
from dotenv import load_dotenv
from memory import update_context

# Загружаем переменные окружения из .env файла
load_dotenv()

# Загружаем API ключ и прокси из переменных окружения
api_key = os.getenv("OPENAI_API_KEY")  # Исправлено на OPENAI_API_KEY
proxy_http = os.getenv("PROXY_HTTP")
proxy_https = os.getenv("PROXY_HTTPS")

# Проверяем, установлен ли ключ
if not api_key:
    raise ValueError("OpenAI API key is not set. Please ensure it is set in the .env file or as an environment variable.")

# Настройка прокси
proxies = {
    "http": proxy_http,
    "https": proxy_https,
}

# Настройка заголовков с API-ключом
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

print("Loaded API key:", os.getenv("OPENAI_API_KEY"))

def process_text(text: str, user_context: List[Dict], user_id: int = None) -> str:
    """
    Обрабатывает текстовое сообщение пользователя с помощью ChatGPT, 
    учитывая контекст предыдущих сообщений.

    Args:
        text: Текстовое сообщение пользователя.
        user_context: Контекст пользователя (список словарей {"role": ..., "content": ...}).
        user_id: ID пользователя (опционально).

    Returns:
        Ответ ChatGPT.
    """
    updated_context = update_context(user_context, text)
    data = {
        "model": "gpt-4o",
        "messages": updated_context,
    }

    try:
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=data,
            proxies=proxies
        )
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"], updated_context
    except requests.exceptions.RequestException as e:
        return f"Произошла ошибка: {e}", updated_context


def process_image(image_path: str, user_id: Optional[int] = None) -> Dict[str, Any]:
    """
    Обрабатывает изображение и возвращает результаты анализа с помощью Vision API.

    Args:
        image_path: Путь к файлу изображения.
        user_id: ID пользователя (опционально).

    Returns:
        Результаты анализа изображения.
    """
    with open(image_path, "rb") as image_file:
        files = {"image": image_file}
        data = {"model": "gpt-4o-mini"}
        
        try:
            response = requests.post(
                "https://api.openai.com/v1/images/generations",
                headers=headers,
                files=files,
                data=data,
                proxies=proxies
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            return {"error": f"Произошла ошибка: {e}"}


def process_voice(voice_path: str, user_id: Optional[int] = None) -> str:
    """
    Преобразует голосовое сообщение в текст с помощью Whisper API.

    Args:
        voice_path: Путь к файлу голосового сообщения.
        user_id: ID пользователя (опционально).

    Returns:
        Распознанный текст.
    """
    with open(voice_path, "rb") as audio_file:
        files = {"file": audio_file}
        data = {"model": "whisper-1"}
        
        try:
            response = requests.post(
                "https://api.openai.com/v1/audio/transcriptions",
                headers=headers,
                files=files,
                data=data,
                proxies=proxies
            )
            response.raise_for_status()
            return response.json()['text']
        except requests.exceptions.RequestException as e:
            return f"Произошла ошибка: {e}"


def generate_image(prompt: str, user_id: Optional[int] = None) -> str:
    """
    Генерирует изображение по текстовому описанию с помощью DALL-E 3.

    Args:
        prompt: Описание изображения.
        user_id: ID пользователя (опционально).

    Returns:
        URL сгенерированного изображения.
    """
    data = {
        "model": "dall-e-3",
        "prompt": prompt,
        "n": 1,
        "size": "1024x1024",
        "quality": "standard"
    }
    
    try:
        response = requests.post(
            "https://api.openai.com/v1/images/generations",
            headers=headers,
            json=data,
            proxies=proxies
        )
        response.raise_for_status()
        return response.json()["data"][0]["url"]
    except requests.exceptions.RequestException as e:
        return f"Произошла ошибка: {e}"


def generate_voice(text: str, user_id: Optional[int] = None) -> str:
    """
    Генерирует голосовое сообщение из текста.

    Args:
        text: Текст для озвучивания.
        user_id: ID пользователя (опционально).

    Returns:
        Путь к файлу сгенерированного голосового сообщения.
    """
    from pathlib import Path

    speech_file_path = Path(__file__).parent / "speech.mp3"
    data = {
        "model": "tts-1",
        "voice": "alloy",
        "input": text
    }
    
    try:
        response = requests.post(
            "https://api.openai.com/v1/audio/speech",
            headers=headers,
            json=data,
            proxies=proxies,
            stream=True
        )
        response.raise_for_status()
        
        # Сохраняем поток в файл
        with open(speech_file_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
                
        return str(speech_file_path)
    except requests.exceptions.RequestException as e:
        return f"Произошла ошибка: {e}"


## Предыдущие сообщения пользователя:

После выбора активности ПРИ ВЫБОРЕ ЦЕЛИ бот перестает отвечать!Никаких ошибок в логах нет, вот логи: 2024-10-30 17:08:48,564 - main - INFO - Starting the bot. 2024-10-30 17:08:50,297 - telegram.ext.Application - INFO - Application started 2024-10-30 17:08:50,477 - main - INFO - Bot started polling. 2024-10-30 17:09:08,450 - user_reg_utils - INFO - Start command received 2024-10-30 17:09:13,102 - user_reg_utils - INFO - User entered name: Руслан 2024-10-30 17:09:23,312 - user_reg_utils - INFO - User provided location: Location(latitude=53.695228, longitude=91.679077) 2024-10-30 17:09:39,382 - user_reg_utils - INFO - User selected sex: male 2024-10-30 17:09:50,714 - user_reg_utils - INFO - User's age calculated as: 46 2024-10-30 17:09:56,308 - user_reg_utils - INFO - User's height set to: 185.0 2024-10-30 17:10:00,437 - user_reg_utils - INFO - User's weight set to: 100.0 2024-10-30 17:10:05,216 - user_reg_utils - INFO - User's activity level set to: SEDENTARY Разберись с этим! Вот текущие коды модулей: [Файл: 2d1597e4-0268-4587-8664-a38106f965a2_22___init__.py] [Файл: c2ed06d7-0f90-410d-8488-954991904bde_22_bot.py] [Файл: 3e94ce0f-6dcd-4427-869c-5aed92b130a5_22_config.py] [Файл: f8cf55e8-6308-4ec6-85b2-76728088e2e9_22_database.py] [Файл: 60caf44e-eab9-42f1-bd2e-d15f69d6c50b_22_llm.py] [Файл: f811671b-6a4e-4018-a3c5-4ea94b5800e9_22_memory.py] [Файл: 8477f897-9cd4-4baa-87bb-d6e9efefa5ee_22_user_reg_utils.py] [Файл: 4fbed2a2-995a-45a9-ac0a-2a6c855b5edb_22_utils.py]

## Релевантная информация из памяти:

После выбора активности ПРИ ВЫБОРЕ ЦЕЛИ бот перестает отвечать!Никаких ошибок в логах нет, вот логи: 2024-10-30 17:08:48,564 - main - INFO - Starting the bot. 2024-10-30 17:08:50,297 - telegram.ext.Application - INFO - Application started 2024-10-30 17:08:50,477 - main - INFO - Bot started polling. 2024-10-30 17:09:08,450 - user_reg_utils - INFO - Start command received 2024-10-30 17:09:13,102 - user_reg_utils - INFO - User entered name: Руслан 2024-10-30 17:09:23,312 - user_reg_utils - INFO - User provided location: Location(latitude=53.695228, longitude=91.679077) 2024-10-30 17:09:39,382 - user_reg_utils - INFO - User selected sex: male 2024-10-30 17:09:50,714 - user_reg_utils - INFO - User's age calculated as: 46 2024-10-30 17:09:56,308 - user_reg_utils - INFO - User's height set to: 185.0 2024-10-30 17:10:00,437 - user_reg_utils - INFO - User's weight set to: 100.0 2024-10-30 17:10:05,216 - user_reg_utils - INFO - User's activity level set to: SEDENTARY Разберись с этим! Вот текущие коды модулей:

[Файл: 2d1597e4-0268-4587-8664-a38106f965a2_22___init__.py]
[Файл: c2ed06d7-0f90-410d-8488-954991904bde_22_bot.py]
[Файл: 3e94ce0f-6dcd-4427-869c-5aed92b130a5_22_config.py]
[Файл: f8cf55e8-6308-4ec6-85b2-76728088e2e9_22_database.py]
[Файл: 60caf44e-eab9-42f1-bd2e-d15f69d6c50b_22_llm.py]
[Файл: f811671b-6a4e-4018-a3c5-4ea94b5800e9_22_memory.py]
[Файл: 8477f897-9cd4-4baa-87bb-d6e9efefa5ee_22_user_reg_utils.py]
[Файл: 4fbed2a2-995a-45a9-ac0a-2a6c855b5edb_22_utils.py]

## Текущий вопрос пользователя:
После выбора активности ПРИ ВЫБОРЕ ЦЕЛИ бот перестает отвечать!Никаких ошибок в логах нет, вот логи: 2024-10-30 17:08:48,564 - main - INFO - Starting the bot. 2024-10-30 17:08:50,297 - telegram.ext.Application - INFO - Application started 2024-10-30 17:08:50,477 - main - INFO - Bot started polling. 2024-10-30 17:09:08,450 - user_reg_utils - INFO - Start command received 2024-10-30 17:09:13,102 - user_reg_utils - INFO - User entered name: Руслан 2024-10-30 17:09:23,312 - user_reg_utils - INFO - User provided location: Location(latitude=53.695228, longitude=91.679077) 2024-10-30 17:09:39,382 - user_reg_utils - INFO - User selected sex: male 2024-10-30 17:09:50,714 - user_reg_utils - INFO - User's age calculated as: 46 2024-10-30 17:09:56,308 - user_reg_utils - INFO - User's height set to: 185.0 2024-10-30 17:10:00,437 - user_reg_utils - INFO - User's weight set to: 100.0 2024-10-30 17:10:05,216 - user_reg_utils - INFO - User's activity level set to: SEDENTARY Разберись с этим! Вот текущие коды модулей: [Файл: 2d1597e4-0268-4587-8664-a38106f965a2_22___init__.py] [Файл: c2ed06d7-0f90-410d-8488-954991904bde_22_bot.py] [Файл: 3e94ce0f-6dcd-4427-869c-5aed92b130a5_22_config.py] [Файл: f8cf55e8-6308-4ec6-85b2-76728088e2e9_22_database.py] [Файл: 60caf44e-eab9-42f1-bd2e-d15f69d6c50b_22_llm.py] [Файл: f811671b-6a4e-4018-a3c5-4ea94b5800e9_22_memory.py] [Файл: 8477f897-9cd4-4baa-87bb-d6e9efefa5ee_22_user_reg_utils.py] [Файл: 4fbed2a2-995a-45a9-ac0a-2a6c855b5edb_22_utils.py]