smart-bot-factory 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smart-bot-factory might be problematic. Click here for more details.
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/PKG-INFO +1 -1
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/best-valera.py +248 -248
- smart_bot_factory-0.2.7/publish.py +122 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/pyproject.toml +54 -49
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/creation/bot_builder.py +41 -4
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/handlers/handlers.py +4 -1
- smart_bot_factory-0.2.7/smart_bot_factory/utils/__init__.py +10 -0
- smart_bot_factory-0.2.7/smart_bot_factory/utils/user_prompt_loader.py +56 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/uv.lock +426 -1
- smart_bot_factory-0.2.5/smart_bot_factory/utils/__init__.py +0 -9
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.claude/settings.local.json +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.env.example +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.github/ISSUE_TEMPLATE//342/234/250-/320/267/320/260/320/277/321/200/320/276/321/201-/321/204/321/203/320/275/320/272/321/206/320/270/320/270.md" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.github/ISSUE_TEMPLATE//360/237/220/233-/320/261/320/260/320/263-/321/200/320/265/320/277/320/276/321/200/321/202.md" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.github/workflows/ci.yml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.github/workflows/publish-private.yml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.github/workflows/publish.yml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.gitignore +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/.python-version +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/CLIENTS_USAGE.md +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/Dockerfile +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/LICENSE +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/README.md +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/1sales_context.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/2product_info.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/3objection_handling.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/final_instructions.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/help_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/prompts/welcome_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/tests/quick_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/tests/realistic_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/tests/scenario_examples.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/welcome_files/welcome_file_msg.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/best-valera/welcome_files//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/prompts/2product_info.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/prompts/3objection_handling.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/prompts/final_instructions.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/prompts/help_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/prompts/welcome_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/tests/quick_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/tests/realistic_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/tests/scenario_examples.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/welcome_files/welcome_file_msg.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/bots/valera/welcome_files//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/cats//320/224/320/276/320/263/320/276/320/262/320/276/321/200.pdf" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/cats//320/272/320/276/320/275/320/270.jpg" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/cats//320/272/320/276/321/202.jpg" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/cats//320/273/320/265/321/201.jpg" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/tests/fixes_elina.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/configs/valera/welcome_file//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/create_tag.sh +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/docker-compose.yml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/env.example +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/requirements.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/save_backup.sh +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/save_fixes.sh +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/admin_logic.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/admin_manager.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/admin_migration.sql +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/admin_tester.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/admin/timeout_checker.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/analytics/analytics_manager.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/cli.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/config.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/configs/growthmed-october-24/welcome_file//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/bot_utils.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/conversation_manager.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/decorators.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/message_sender.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/router.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/router_manager.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/states.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/core/telegram_router.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/creation/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/creation/bot_testing.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/event/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/integrations/openai_client.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/integrations/supabase_client.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/message/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/router/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/setup_checker.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/supabase/__init__.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/supabase/client.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/table/database_structure.sql +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/table/schema.sql +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/utils/debug_routing.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/utils/prompt_loader.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/smart_bot_factory/utm_link_generator.py +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/system_prompt_example.txt +0 -0
- {smart_bot_factory-0.2.5 → smart_bot_factory-0.2.7}/valera.py +0 -0
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
from smart_bot_factory.router import EventRouter, TelegramRouter
|
|
5
|
-
from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage, send_message
|
|
6
|
-
from smart_bot_factory.supabase import SupabaseClient
|
|
7
|
-
from smart_bot_factory.creation import BotBuilder
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
# =============================================================================
|
|
12
|
-
# СОЗДАНИЕ РОУТЕРОВ
|
|
13
|
-
# =============================================================================
|
|
14
|
-
|
|
15
|
-
# Роутер для событий (бизнес-логика)
|
|
16
|
-
event_router = EventRouter("best-valera_events")
|
|
17
|
-
|
|
18
|
-
# Роутер для Telegram (команды и сообщения)
|
|
19
|
-
telegram_router_1 = TelegramRouter("best-valera_telegram")
|
|
20
|
-
telegram_router_2 = TelegramRouter("best-valera_telegram_2")
|
|
21
|
-
|
|
22
|
-
supabase_client = SupabaseClient("best-valera")
|
|
23
|
-
|
|
24
|
-
# =============================================================================
|
|
25
|
-
# ИНИЦИАЛИЗАЦИЯ BOT BUILDER (нужен для декоратора on_start)
|
|
26
|
-
# =============================================================================
|
|
27
|
-
|
|
28
|
-
bot_builder = BotBuilder("best-valera")
|
|
29
|
-
|
|
30
|
-
# =============================================================================
|
|
31
|
-
# TELEGRAM ОБРАБОТЧИКИ (используем прямой aiogram API)
|
|
32
|
-
# =============================================================================
|
|
33
|
-
|
|
34
|
-
from aiogram import F
|
|
35
|
-
from aiogram.filters import Command
|
|
36
|
-
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
|
37
|
-
from aiogram.fsm.context import FSMContext
|
|
38
|
-
|
|
39
|
-
# =============================================================================
|
|
40
|
-
# ОБРАБОТЧИК on_start (вызывается после стандартного /start)
|
|
41
|
-
# =============================================================================
|
|
42
|
-
|
|
43
|
-
@bot_builder.on_start
|
|
44
|
-
async def custom_start_handler(user_id: int, session_id: str, message: Message, state: FSMContext):
|
|
45
|
-
"""
|
|
46
|
-
Вызывается после стандартной логики /start
|
|
47
|
-
Здесь можно отправить дополнительные сообщения, кнопки и т.д.
|
|
48
|
-
"""
|
|
49
|
-
# Пример: отправляем сообщение с кнопками
|
|
50
|
-
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
|
51
|
-
[InlineKeyboardButton(text="📖 Каталог", callback_data="catalog")],
|
|
52
|
-
[InlineKeyboardButton(text="💰 Цены", callback_data="prices")],
|
|
53
|
-
[InlineKeyboardButton(text="📞 Связаться", callback_data="contact")]
|
|
54
|
-
])
|
|
55
|
-
|
|
56
|
-
await message.answer(
|
|
57
|
-
"🎯 Что вас интересует?",
|
|
58
|
-
reply_markup=keyboard
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
# =============================================================================
|
|
62
|
-
# ХУКИ ДЛЯ КАСТОМИЗАЦИИ ОБРАБОТКИ СООБЩЕНИЙ
|
|
63
|
-
# =============================================================================
|
|
64
|
-
|
|
65
|
-
# ХУК 1: Валидация сообщений
|
|
66
|
-
@bot_builder.validate_message
|
|
67
|
-
async def validate_service_names(message, supabase_client):
|
|
68
|
-
"""Проверяем корректность названий услуг"""
|
|
69
|
-
# Пример: блокируем неправильные названия
|
|
70
|
-
incorrect_names = ["массаш", "педекюр", "макияш"]
|
|
71
|
-
|
|
72
|
-
if message.text:
|
|
73
|
-
for incorrect in incorrect_names:
|
|
74
|
-
if incorrect in message.text.lower():
|
|
75
|
-
await message.answer(
|
|
76
|
-
f"❗ Возможно, вы имели в виду другую услугу?\n"
|
|
77
|
-
f"Пожалуйста, уточните название"
|
|
78
|
-
)
|
|
79
|
-
return False # Прерываем обработку AI
|
|
80
|
-
|
|
81
|
-
return True # Продолжаем обработку
|
|
82
|
-
|
|
83
|
-
# ХУК 2: Обогащение промпта
|
|
84
|
-
@bot_builder.enrich_prompt
|
|
85
|
-
async def add_client_info_to_prompt(system_prompt, user_id, session_id, supabase_client):
|
|
86
|
-
"""Добавляем информацию о клиенте в промпт"""
|
|
87
|
-
try:
|
|
88
|
-
session = await supabase_client.get_active_session(user_id)
|
|
89
|
-
|
|
90
|
-
if session and session.get('metadata'):
|
|
91
|
-
phone = session['metadata'].get('phone')
|
|
92
|
-
name = session['metadata'].get('confirmed_name') or session['metadata'].get('telegram_name')
|
|
93
|
-
|
|
94
|
-
if phone:
|
|
95
|
-
client_info = f"\n\nИНФОРМАЦИЯ О КЛИЕНТЕ:\n- Телефон: {phone}"
|
|
96
|
-
if name:
|
|
97
|
-
client_info += f"\n- Имя: {name}"
|
|
98
|
-
client_info += "\n\n⚠️ ВАЖНО: При создании записи используй ЭТОТ телефон и имя!"
|
|
99
|
-
|
|
100
|
-
return system_prompt + client_info
|
|
101
|
-
except Exception as e:
|
|
102
|
-
logger.error(f"Ошибка обогащения промпта: {e}")
|
|
103
|
-
|
|
104
|
-
return system_prompt
|
|
105
|
-
|
|
106
|
-
# ХУК 3: Обогащение контекста (например, данные из внешнего API)
|
|
107
|
-
@bot_builder.enrich_context
|
|
108
|
-
async def add_external_api_data(messages, user_id, session_id):
|
|
109
|
-
"""Добавляем данные из внешних систем"""
|
|
110
|
-
# Пример: можно добавить расписание из YClients, данные из CRM и т.д.
|
|
111
|
-
# messages.append({
|
|
112
|
-
# "role": "system",
|
|
113
|
-
# "content": "Реальное доступное время: ..."
|
|
114
|
-
# })
|
|
115
|
-
return messages
|
|
116
|
-
|
|
117
|
-
# ХУК 4: Обработка ответа AI
|
|
118
|
-
@bot_builder.process_response
|
|
119
|
-
async def add_promo_to_price_response(response_text, ai_metadata, user_id):
|
|
120
|
-
"""Добавляем промо-код к ответам о ценах"""
|
|
121
|
-
if "цен" in response_text.lower() or "стоимост" in response_text.lower():
|
|
122
|
-
response_text += "\n\n🎁 Промокод FIRST10 для скидки 10% на первый визит!"
|
|
123
|
-
|
|
124
|
-
return response_text, ai_metadata
|
|
125
|
-
|
|
126
|
-
# ХУК 5: Фильтр отправки
|
|
127
|
-
@bot_builder.filter_send
|
|
128
|
-
async def allow_all_messages(user_id, response_text):
|
|
129
|
-
"""Разрешаем все сообщения (можно добавить блокировку по условию)"""
|
|
130
|
-
# Пример: блокировка во время обработки
|
|
131
|
-
# if is_processing(user_id):
|
|
132
|
-
# return False
|
|
133
|
-
return True
|
|
134
|
-
|
|
135
|
-
@telegram_router_1.router.message(Command("price", "цена"))
|
|
136
|
-
async def handle_price_command(message: Message, state: FSMContext):
|
|
137
|
-
"""Обработчик команды /price"""
|
|
138
|
-
await message.answer(
|
|
139
|
-
"💰 Наши цены:\n\n"
|
|
140
|
-
"📦 Базовый пакет - 1000₽\n"
|
|
141
|
-
"📦 Стандартный - 2000₽\n"
|
|
142
|
-
"📦 Премиум - 5000₽"
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
@telegram_router_2.router.message(F.text & (F.text.lower().contains("цена") | F.text.lower().contains("стоимость")))
|
|
146
|
-
async def handle_price_question(message: Message, state: FSMContext):
|
|
147
|
-
"""Обработчик вопросов о цене"""
|
|
148
|
-
await message.answer("💡 Напишите /price чтобы увидеть актуальные цены")
|
|
149
|
-
|
|
150
|
-
# Обработчики callback'ов от кнопок в on_start
|
|
151
|
-
@telegram_router_1.router.callback_query(F.data == "catalog")
|
|
152
|
-
async def handle_catalog(callback: CallbackQuery, state: FSMContext):
|
|
153
|
-
"""Обработка кнопки Каталог"""
|
|
154
|
-
await callback.answer()
|
|
155
|
-
|
|
156
|
-
# Пример: используем send_message с файлами
|
|
157
|
-
await send_message(
|
|
158
|
-
message=callback.message,
|
|
159
|
-
text="📖 Вот наш каталог товаров:\n\n1. Товар 1\n2. Товар 2\n3. Товар 3",
|
|
160
|
-
supabase_client=supabase_client,
|
|
161
|
-
files_list=[], # Можно добавить файлы: ["catalog.pdf"]
|
|
162
|
-
parse_mode="Markdown"
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
@telegram_router_1.router.callback_query(F.data == "prices")
|
|
166
|
-
async def handle_prices(callback: CallbackQuery, state: FSMContext):
|
|
167
|
-
"""Обработка кнопки Цены"""
|
|
168
|
-
await callback.answer()
|
|
169
|
-
await callback.message.answer("💰 Наши цены:\n\n📦 Базовый - 1000₽\n📦 Премиум - 5000₽")
|
|
170
|
-
|
|
171
|
-
@telegram_router_1.router.callback_query(F.data == "contact")
|
|
172
|
-
async def handle_contact(callback: CallbackQuery, state: FSMContext):
|
|
173
|
-
"""Обработка кнопки Связаться"""
|
|
174
|
-
await callback.answer()
|
|
175
|
-
await callback.message.answer("📞 Свяжитесь с нами:\n\nТелефон: +7 (999) 123-45-67\nEmail: info@example.com")
|
|
176
|
-
|
|
177
|
-
# =============================================================================
|
|
178
|
-
# ОБРАБОТЧИКИ СОБЫТИЙ (бизнес-логика)
|
|
179
|
-
# =============================================================================
|
|
180
|
-
|
|
181
|
-
@event_router.event_handler("example_event")
|
|
182
|
-
async def handle_example_event(user_id: int, event_data: str):
|
|
183
|
-
"""Пример обработчика события"""
|
|
184
|
-
await send_message_by_human(
|
|
185
|
-
user_id=user_id,
|
|
186
|
-
message_text=f"✅ Событие обработано! Данные: {event_data}"
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
"status": "success",
|
|
191
|
-
"message": "Событие обработано"
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
# =============================================================================
|
|
195
|
-
# ВРЕМЕННЫЕ ЗАДАЧИ ДЛЯ ОДНОГО ПОЛЬЗОВАТЕЛЯ
|
|
196
|
-
# =============================================================================
|
|
197
|
-
|
|
198
|
-
@event_router.schedule_task("send_reminder", delay="1h")
|
|
199
|
-
async def send_user_reminder(user_id: int, reminder_text: str):
|
|
200
|
-
"""Отправляет напоминание пользователю"""
|
|
201
|
-
await send_message_by_human(
|
|
202
|
-
user_id=user_id,
|
|
203
|
-
message_text=f"🔔 Напоминание: {reminder_text}"
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
"status": "reminder_sent",
|
|
208
|
-
"message": f"Напоминание отправлено пользователю {user_id}"
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
# =============================================================================
|
|
212
|
-
# ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ (для всех пользователей)
|
|
213
|
-
# =============================================================================
|
|
214
|
-
|
|
215
|
-
@event_router.global_handler("mass_notification", delay="1h", notify=True)
|
|
216
|
-
async def send_global_announcement(announcement_text: str):
|
|
217
|
-
"""Отправляет анонс всем пользователям бота"""
|
|
218
|
-
|
|
219
|
-
await send_message_to_users_by_stage(
|
|
220
|
-
stage="introduction",
|
|
221
|
-
message_text=announcement_text,
|
|
222
|
-
bot_id="best-valera"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
# =============================================================================
|
|
226
|
-
# ОСНОВНАЯ ФУНКЦИЯ
|
|
227
|
-
# =============================================================================
|
|
228
|
-
|
|
229
|
-
async def main():
|
|
230
|
-
"""Основная функция запуска бота"""
|
|
231
|
-
try:
|
|
232
|
-
# bot_builder уже создан выше (для декоратора @bot_builder.on_start)
|
|
233
|
-
|
|
234
|
-
# Регистрируем роутеры ПЕРЕД сборкой (можно по одному или несколько сразу)
|
|
235
|
-
bot_builder.register_routers(event_router) # Роутеры событий
|
|
236
|
-
bot_builder.register_telegram_routers(telegram_router_1, telegram_router_2) # Telegram роутеры
|
|
237
|
-
|
|
238
|
-
await bot_builder.build()
|
|
239
|
-
|
|
240
|
-
# Запускаем бота
|
|
241
|
-
await bot_builder.start()
|
|
242
|
-
|
|
243
|
-
except Exception as e:
|
|
244
|
-
print(f"❌ Ошибка запуска бота: {e}")
|
|
245
|
-
raise
|
|
246
|
-
|
|
247
|
-
if __name__ == "__main__":
|
|
248
|
-
asyncio.run(main())
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from smart_bot_factory.router import EventRouter, TelegramRouter
|
|
5
|
+
from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage, send_message
|
|
6
|
+
from smart_bot_factory.supabase import SupabaseClient
|
|
7
|
+
from smart_bot_factory.creation import BotBuilder
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
# =============================================================================
|
|
12
|
+
# СОЗДАНИЕ РОУТЕРОВ
|
|
13
|
+
# =============================================================================
|
|
14
|
+
|
|
15
|
+
# Роутер для событий (бизнес-логика)
|
|
16
|
+
event_router = EventRouter("best-valera_events")
|
|
17
|
+
|
|
18
|
+
# Роутер для Telegram (команды и сообщения)
|
|
19
|
+
telegram_router_1 = TelegramRouter("best-valera_telegram")
|
|
20
|
+
telegram_router_2 = TelegramRouter("best-valera_telegram_2")
|
|
21
|
+
|
|
22
|
+
supabase_client = SupabaseClient("best-valera")
|
|
23
|
+
|
|
24
|
+
# =============================================================================
|
|
25
|
+
# ИНИЦИАЛИЗАЦИЯ BOT BUILDER (нужен для декоратора on_start)
|
|
26
|
+
# =============================================================================
|
|
27
|
+
|
|
28
|
+
bot_builder = BotBuilder("best-valera")
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# TELEGRAM ОБРАБОТЧИКИ (используем прямой aiogram API)
|
|
32
|
+
# =============================================================================
|
|
33
|
+
|
|
34
|
+
from aiogram import F
|
|
35
|
+
from aiogram.filters import Command
|
|
36
|
+
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
|
37
|
+
from aiogram.fsm.context import FSMContext
|
|
38
|
+
|
|
39
|
+
# =============================================================================
|
|
40
|
+
# ОБРАБОТЧИК on_start (вызывается после стандартного /start)
|
|
41
|
+
# =============================================================================
|
|
42
|
+
|
|
43
|
+
@bot_builder.on_start
|
|
44
|
+
async def custom_start_handler(user_id: int, session_id: str, message: Message, state: FSMContext):
|
|
45
|
+
"""
|
|
46
|
+
Вызывается после стандартной логики /start
|
|
47
|
+
Здесь можно отправить дополнительные сообщения, кнопки и т.д.
|
|
48
|
+
"""
|
|
49
|
+
# Пример: отправляем сообщение с кнопками
|
|
50
|
+
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
|
51
|
+
[InlineKeyboardButton(text="📖 Каталог", callback_data="catalog")],
|
|
52
|
+
[InlineKeyboardButton(text="💰 Цены", callback_data="prices")],
|
|
53
|
+
[InlineKeyboardButton(text="📞 Связаться", callback_data="contact")]
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
await message.answer(
|
|
57
|
+
"🎯 Что вас интересует?",
|
|
58
|
+
reply_markup=keyboard
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# =============================================================================
|
|
62
|
+
# ХУКИ ДЛЯ КАСТОМИЗАЦИИ ОБРАБОТКИ СООБЩЕНИЙ
|
|
63
|
+
# =============================================================================
|
|
64
|
+
|
|
65
|
+
# ХУК 1: Валидация сообщений
|
|
66
|
+
@bot_builder.validate_message
|
|
67
|
+
async def validate_service_names(message, supabase_client):
|
|
68
|
+
"""Проверяем корректность названий услуг"""
|
|
69
|
+
# Пример: блокируем неправильные названия
|
|
70
|
+
incorrect_names = ["массаш", "педекюр", "макияш"]
|
|
71
|
+
|
|
72
|
+
if message.text:
|
|
73
|
+
for incorrect in incorrect_names:
|
|
74
|
+
if incorrect in message.text.lower():
|
|
75
|
+
await message.answer(
|
|
76
|
+
f"❗ Возможно, вы имели в виду другую услугу?\n"
|
|
77
|
+
f"Пожалуйста, уточните название"
|
|
78
|
+
)
|
|
79
|
+
return False # Прерываем обработку AI
|
|
80
|
+
|
|
81
|
+
return True # Продолжаем обработку
|
|
82
|
+
|
|
83
|
+
# ХУК 2: Обогащение промпта
|
|
84
|
+
@bot_builder.enrich_prompt
|
|
85
|
+
async def add_client_info_to_prompt(system_prompt, user_id, session_id, supabase_client):
|
|
86
|
+
"""Добавляем информацию о клиенте в промпт"""
|
|
87
|
+
try:
|
|
88
|
+
session = await supabase_client.get_active_session(user_id)
|
|
89
|
+
|
|
90
|
+
if session and session.get('metadata'):
|
|
91
|
+
phone = session['metadata'].get('phone')
|
|
92
|
+
name = session['metadata'].get('confirmed_name') or session['metadata'].get('telegram_name')
|
|
93
|
+
|
|
94
|
+
if phone:
|
|
95
|
+
client_info = f"\n\nИНФОРМАЦИЯ О КЛИЕНТЕ:\n- Телефон: {phone}"
|
|
96
|
+
if name:
|
|
97
|
+
client_info += f"\n- Имя: {name}"
|
|
98
|
+
client_info += "\n\n⚠️ ВАЖНО: При создании записи используй ЭТОТ телефон и имя!"
|
|
99
|
+
|
|
100
|
+
return system_prompt + client_info
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error(f"Ошибка обогащения промпта: {e}")
|
|
103
|
+
|
|
104
|
+
return system_prompt
|
|
105
|
+
|
|
106
|
+
# ХУК 3: Обогащение контекста (например, данные из внешнего API)
|
|
107
|
+
@bot_builder.enrich_context
|
|
108
|
+
async def add_external_api_data(messages, user_id, session_id):
|
|
109
|
+
"""Добавляем данные из внешних систем"""
|
|
110
|
+
# Пример: можно добавить расписание из YClients, данные из CRM и т.д.
|
|
111
|
+
# messages.append({
|
|
112
|
+
# "role": "system",
|
|
113
|
+
# "content": "Реальное доступное время: ..."
|
|
114
|
+
# })
|
|
115
|
+
return messages
|
|
116
|
+
|
|
117
|
+
# ХУК 4: Обработка ответа AI
|
|
118
|
+
@bot_builder.process_response
|
|
119
|
+
async def add_promo_to_price_response(response_text, ai_metadata, user_id):
|
|
120
|
+
"""Добавляем промо-код к ответам о ценах"""
|
|
121
|
+
if "цен" in response_text.lower() or "стоимост" in response_text.lower():
|
|
122
|
+
response_text += "\n\n🎁 Промокод FIRST10 для скидки 10% на первый визит!"
|
|
123
|
+
|
|
124
|
+
return response_text, ai_metadata
|
|
125
|
+
|
|
126
|
+
# ХУК 5: Фильтр отправки
|
|
127
|
+
@bot_builder.filter_send
|
|
128
|
+
async def allow_all_messages(user_id, response_text):
|
|
129
|
+
"""Разрешаем все сообщения (можно добавить блокировку по условию)"""
|
|
130
|
+
# Пример: блокировка во время обработки
|
|
131
|
+
# if is_processing(user_id):
|
|
132
|
+
# return False
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
@telegram_router_1.router.message(Command("price", "цена"))
|
|
136
|
+
async def handle_price_command(message: Message, state: FSMContext):
|
|
137
|
+
"""Обработчик команды /price"""
|
|
138
|
+
await message.answer(
|
|
139
|
+
"💰 Наши цены:\n\n"
|
|
140
|
+
"📦 Базовый пакет - 1000₽\n"
|
|
141
|
+
"📦 Стандартный - 2000₽\n"
|
|
142
|
+
"📦 Премиум - 5000₽"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@telegram_router_2.router.message(F.text & (F.text.lower().contains("цена") | F.text.lower().contains("стоимость")))
|
|
146
|
+
async def handle_price_question(message: Message, state: FSMContext):
|
|
147
|
+
"""Обработчик вопросов о цене"""
|
|
148
|
+
await message.answer("💡 Напишите /price чтобы увидеть актуальные цены")
|
|
149
|
+
|
|
150
|
+
# Обработчики callback'ов от кнопок в on_start
|
|
151
|
+
@telegram_router_1.router.callback_query(F.data == "catalog")
|
|
152
|
+
async def handle_catalog(callback: CallbackQuery, state: FSMContext):
|
|
153
|
+
"""Обработка кнопки Каталог"""
|
|
154
|
+
await callback.answer()
|
|
155
|
+
|
|
156
|
+
# Пример: используем send_message с файлами
|
|
157
|
+
await send_message(
|
|
158
|
+
message=callback.message,
|
|
159
|
+
text="📖 Вот наш каталог товаров:\n\n1. Товар 1\n2. Товар 2\n3. Товар 3",
|
|
160
|
+
supabase_client=supabase_client,
|
|
161
|
+
files_list=[], # Можно добавить файлы: ["catalog.pdf"]
|
|
162
|
+
parse_mode="Markdown"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
@telegram_router_1.router.callback_query(F.data == "prices")
|
|
166
|
+
async def handle_prices(callback: CallbackQuery, state: FSMContext):
|
|
167
|
+
"""Обработка кнопки Цены"""
|
|
168
|
+
await callback.answer()
|
|
169
|
+
await callback.message.answer("💰 Наши цены:\n\n📦 Базовый - 1000₽\n📦 Премиум - 5000₽")
|
|
170
|
+
|
|
171
|
+
@telegram_router_1.router.callback_query(F.data == "contact")
|
|
172
|
+
async def handle_contact(callback: CallbackQuery, state: FSMContext):
|
|
173
|
+
"""Обработка кнопки Связаться"""
|
|
174
|
+
await callback.answer()
|
|
175
|
+
await callback.message.answer("📞 Свяжитесь с нами:\n\nТелефон: +7 (999) 123-45-67\nEmail: info@example.com")
|
|
176
|
+
|
|
177
|
+
# =============================================================================
|
|
178
|
+
# ОБРАБОТЧИКИ СОБЫТИЙ (бизнес-логика)
|
|
179
|
+
# =============================================================================
|
|
180
|
+
|
|
181
|
+
@event_router.event_handler("example_event")
|
|
182
|
+
async def handle_example_event(user_id: int, event_data: str):
|
|
183
|
+
"""Пример обработчика события"""
|
|
184
|
+
await send_message_by_human(
|
|
185
|
+
user_id=user_id,
|
|
186
|
+
message_text=f"✅ Событие обработано! Данные: {event_data}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
"status": "success",
|
|
191
|
+
"message": "Событие обработано"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# =============================================================================
|
|
195
|
+
# ВРЕМЕННЫЕ ЗАДАЧИ ДЛЯ ОДНОГО ПОЛЬЗОВАТЕЛЯ
|
|
196
|
+
# =============================================================================
|
|
197
|
+
|
|
198
|
+
@event_router.schedule_task("send_reminder", delay="1h")
|
|
199
|
+
async def send_user_reminder(user_id: int, reminder_text: str):
|
|
200
|
+
"""Отправляет напоминание пользователю"""
|
|
201
|
+
await send_message_by_human(
|
|
202
|
+
user_id=user_id,
|
|
203
|
+
message_text=f"🔔 Напоминание: {reminder_text}"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"status": "reminder_sent",
|
|
208
|
+
"message": f"Напоминание отправлено пользователю {user_id}"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# =============================================================================
|
|
212
|
+
# ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ (для всех пользователей)
|
|
213
|
+
# =============================================================================
|
|
214
|
+
|
|
215
|
+
@event_router.global_handler("mass_notification", delay="1h", notify=True)
|
|
216
|
+
async def send_global_announcement(announcement_text: str):
|
|
217
|
+
"""Отправляет анонс всем пользователям бота"""
|
|
218
|
+
|
|
219
|
+
await send_message_to_users_by_stage(
|
|
220
|
+
stage="introduction",
|
|
221
|
+
message_text=announcement_text,
|
|
222
|
+
bot_id="best-valera"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# =============================================================================
|
|
226
|
+
# ОСНОВНАЯ ФУНКЦИЯ
|
|
227
|
+
# =============================================================================
|
|
228
|
+
|
|
229
|
+
async def main():
|
|
230
|
+
"""Основная функция запуска бота"""
|
|
231
|
+
try:
|
|
232
|
+
# bot_builder уже создан выше (для декоратора @bot_builder.on_start)
|
|
233
|
+
|
|
234
|
+
# Регистрируем роутеры ПЕРЕД сборкой (можно по одному или несколько сразу)
|
|
235
|
+
bot_builder.register_routers(event_router) # Роутеры событий
|
|
236
|
+
bot_builder.register_telegram_routers(telegram_router_1, telegram_router_2) # Telegram роутеры
|
|
237
|
+
|
|
238
|
+
await bot_builder.build()
|
|
239
|
+
|
|
240
|
+
# Запускаем бота
|
|
241
|
+
await bot_builder.start()
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
print(f"❌ Ошибка запуска бота: {e}")
|
|
245
|
+
raise
|
|
246
|
+
|
|
247
|
+
if __name__ == "__main__":
|
|
248
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Скрипт автоматической публикации библиотеки в PyPI
|
|
4
|
+
- Увеличивает версию на 0.0.1
|
|
5
|
+
- Очищает dist/
|
|
6
|
+
- Собирает пакет через uv build
|
|
7
|
+
- Публикует через twine
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import shutil
|
|
12
|
+
import subprocess
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
def increment_version(version: str) -> str:
|
|
16
|
+
"""Увеличивает версию на 0.0.1"""
|
|
17
|
+
parts = version.split('.')
|
|
18
|
+
if len(parts) != 3:
|
|
19
|
+
raise ValueError(f"Неверный формат версии: {version}")
|
|
20
|
+
|
|
21
|
+
major, minor, patch = map(int, parts)
|
|
22
|
+
patch += 1
|
|
23
|
+
|
|
24
|
+
return f"{major}.{minor}.{patch}"
|
|
25
|
+
|
|
26
|
+
def update_version_in_toml():
|
|
27
|
+
"""Обновляет версию в pyproject.toml"""
|
|
28
|
+
toml_path = Path("pyproject.toml")
|
|
29
|
+
|
|
30
|
+
if not toml_path.exists():
|
|
31
|
+
raise FileNotFoundError("pyproject.toml не найден")
|
|
32
|
+
|
|
33
|
+
content = toml_path.read_text(encoding='utf-8')
|
|
34
|
+
|
|
35
|
+
# Находим текущую версию
|
|
36
|
+
version_match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
|
|
37
|
+
if not version_match:
|
|
38
|
+
raise ValueError("Версия не найдена в pyproject.toml")
|
|
39
|
+
|
|
40
|
+
old_version = version_match.group(1)
|
|
41
|
+
new_version = increment_version(old_version)
|
|
42
|
+
|
|
43
|
+
# Обновляем версию
|
|
44
|
+
new_content = re.sub(
|
|
45
|
+
r'^version\s*=\s*"[^"]+"',
|
|
46
|
+
f'version = "{new_version}"',
|
|
47
|
+
content,
|
|
48
|
+
flags=re.MULTILINE
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
toml_path.write_text(new_content, encoding='utf-8')
|
|
52
|
+
|
|
53
|
+
print(f"✅ Версия обновлена: {old_version} → {new_version}")
|
|
54
|
+
return new_version
|
|
55
|
+
|
|
56
|
+
def clean_dist():
|
|
57
|
+
"""Очищает папку dist/"""
|
|
58
|
+
dist_path = Path("dist")
|
|
59
|
+
|
|
60
|
+
if dist_path.exists():
|
|
61
|
+
shutil.rmtree(dist_path)
|
|
62
|
+
print("✅ Папка dist/ очищена")
|
|
63
|
+
|
|
64
|
+
dist_path.mkdir(exist_ok=True)
|
|
65
|
+
print("✅ Папка dist/ создана")
|
|
66
|
+
|
|
67
|
+
def build_package():
|
|
68
|
+
"""Собирает пакет через uv build"""
|
|
69
|
+
print("\n🔨 Сборка пакета...")
|
|
70
|
+
result = subprocess.run(["uv", "build"], check=True)
|
|
71
|
+
print("✅ Пакет собран")
|
|
72
|
+
return result.returncode == 0
|
|
73
|
+
|
|
74
|
+
def upload_to_pypi():
|
|
75
|
+
"""Загружает пакет в PyPI через twine"""
|
|
76
|
+
print("\n📤 Загрузка в PyPI...")
|
|
77
|
+
|
|
78
|
+
# Используем twine через uv run
|
|
79
|
+
result = subprocess.run(
|
|
80
|
+
["uv", "run", "twine", "upload", "dist/*"],
|
|
81
|
+
check=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
print("✅ Пакет загружен в PyPI")
|
|
85
|
+
return result.returncode == 0
|
|
86
|
+
|
|
87
|
+
def main():
|
|
88
|
+
"""Основная функция"""
|
|
89
|
+
try:
|
|
90
|
+
print("🚀 Начинаем публикацию в PyPI\n")
|
|
91
|
+
|
|
92
|
+
# 1. Обновляем версию
|
|
93
|
+
new_version = update_version_in_toml()
|
|
94
|
+
|
|
95
|
+
# 2. Очищаем dist/
|
|
96
|
+
clean_dist()
|
|
97
|
+
|
|
98
|
+
# 3. Собираем пакет
|
|
99
|
+
build_package()
|
|
100
|
+
|
|
101
|
+
# 4. Загружаем в PyPI
|
|
102
|
+
upload_to_pypi()
|
|
103
|
+
|
|
104
|
+
print(f"\n🎉 Успешно! Версия {new_version} опубликована в PyPI")
|
|
105
|
+
print(f"\n💡 Не забудьте закоммитить изменения:")
|
|
106
|
+
print(f" git add pyproject.toml")
|
|
107
|
+
print(f" git commit -m 'Bump version to {new_version}'")
|
|
108
|
+
print(f" git tag v{new_version}")
|
|
109
|
+
print(f" git push && git push --tags")
|
|
110
|
+
|
|
111
|
+
except subprocess.CalledProcessError as e:
|
|
112
|
+
print(f"\n❌ Ошибка выполнения команды: {e}")
|
|
113
|
+
return 1
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"\n❌ Ошибка: {e}")
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
exit(main())
|
|
122
|
+
|