smart-bot-factory 0.2.10__py3-none-any.whl → 0.3.1__py3-none-any.whl

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/cli.py CHANGED
@@ -408,88 +408,59 @@ def list_bots_in_bots_folder() -> list:
408
408
 
409
409
  def create_bot_template(bot_id: str) -> str:
410
410
  """Создает шаблон основного файла бота"""
411
- return f'''import asyncio
411
+ return f'''"""
412
+ {bot_id.replace("-", " ").title()} Bot - Умный Telegram бот на Smart Bot Factory
413
+ """
412
414
 
413
- from smart_bot_factory.router import Router
414
- from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage
415
- from smart_bot_factory.supabase import SupabaseClient
415
+ import asyncio
416
+ from smart_bot_factory.router import EventRouter
417
+ from smart_bot_factory.message import send_message_by_human
416
418
  from smart_bot_factory.creation import BotBuilder
417
419
 
418
- # Создаем роутер для всех обработчиков
419
- router = Router("{bot_id}_handlers")
420
-
421
- supabase_client = SupabaseClient("{bot_id}")
420
+ # Инициализация
421
+ event_router = EventRouter("{bot_id}")
422
+ bot_builder = BotBuilder("{bot_id}")
422
423
 
423
424
  # =============================================================================
424
425
  # ОБРАБОТЧИКИ СОБЫТИЙ
425
426
  # =============================================================================
426
427
 
427
- @router.event_handler("example_event")
428
- async def handle_example_event(user_id: int, event_data: str):
429
- """Пример обработчика события"""
430
- # Отправляем подтверждение пользователю
431
- await send_message_by_human(
432
- user_id=user_id,
433
- message_text=f"✅ Событие обработано! Данные: {{event_data}}"
434
- )
428
+ @event_router.event_handler("collect_contact", notify=True, once_only=True)
429
+ async def handle_contact(user_id: int, contact_data: str):
430
+ """
431
+ Обрабатывает получение контактных данных
435
432
 
436
- return {{
437
- "status": "success",
438
- "message": "Событие обработано"
439
- }}
440
-
441
- # =============================================================================
442
- # ВРЕМЕННЫЕ ЗАДАЧИ ДЛЯ ОДНОГО ПОЛЬЗОВАТЕЛЯ
443
- # =============================================================================
444
-
445
- @router.schedule_task("send_reminder", delay="1h")
446
- async def send_user_reminder(user_id: int, reminder_text: str):
447
- """Отправляет напоминание пользователю"""
433
+ ИИ создает: {{"тип": "collect_contact", "инфо": "+79001234567"}}
434
+ """
448
435
  await send_message_by_human(
449
436
  user_id=user_id,
450
- message_text=f"🔔 Напоминание: {{reminder_text}}"
437
+ message_text=f" Спасибо! Ваши данные сохранены: {{contact_data}}"
451
438
  )
452
439
 
453
- return {{
454
- "status": "reminder_sent",
455
- "message": f"Напоминание отправлено пользователю {{user_id}}"
456
- }}
457
-
458
- # =============================================================================
459
- # ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ (для всех пользователей)
460
- # =============================================================================
461
-
462
- @router.global_handler("mass_notification", delay="1h", notify=True)
463
- async def send_global_announcement(announcement_text: str):
464
- """Отправляет анонс всем пользователям бота"""
465
-
466
- await send_message_to_users_by_stage(
467
- stage="introduction",
468
- message_text=announcement_text,
469
- bot_id="{bot_id}"
470
- )
440
+ return {{"status": "success", "contact": contact_data}}
471
441
 
472
442
  # =============================================================================
473
- # ОСНОВНАЯ ФУНКЦИЯ
443
+ # ЗАПУСК
474
444
  # =============================================================================
475
445
 
476
446
  async def main():
477
- """Основная функция запуска бота"""
478
- try:
479
- # Создаем и собираем бота
480
- bot_builder = BotBuilder("{bot_id}")
481
-
482
- # Регистрируем роутер ПЕРЕД сборкой, чтобы обработчики были доступны
483
- bot_builder.register_router(router)
484
-
485
- await bot_builder.build()
486
-
487
- # Запускаем бота
488
- await bot_builder.start()
489
-
490
- except Exception as e:
491
- print(f"❌ Ошибка запуска бота: {{e}}")
492
- raise
447
+ # ========== РЕГИСТРАЦИЯ РОУТЕРОВ ==========
448
+ bot_builder.register_routers(event_router)
449
+
450
+ # Можно добавить Telegram роутеры:
451
+ # from aiogram import Router
452
+ # telegram_router = Router(name="commands")
453
+ # bot_builder.register_telegram_router(telegram_router)
454
+
455
+ # ========== КАСТОМИЗАЦИЯ (до build) ==========
456
+ # Установить кастомный PromptLoader:
457
+ # from smart_bot_factory.utils import UserPromptLoader
458
+ # custom_loader = UserPromptLoader("{bot_id}")
459
+ # bot_builder.set_prompt_loader(custom_loader)
460
+
461
+ # ========== СБОРКА И ЗАПУСК ==========
462
+ await bot_builder.build()
463
+ await bot_builder.start()
493
464
 
494
465
  if __name__ == "__main__":
495
466
  asyncio.run(main())
@@ -229,17 +229,76 @@ async def process_events(session_id: str, events: list, user_id: int):
229
229
 
230
230
  # Сначала пробуем как обычное событие
231
231
  if event_type in event_handlers:
232
+ from ..core.decorators import execute_event_handler, update_event_result
233
+
234
+ event_handler_info = event_handlers.get(event_type, {})
235
+ once_only = event_handler_info.get('once_only', True)
236
+
237
+ logger.info(f" 🔍 Обработчик '{event_type}': once_only={once_only}")
238
+
239
+ # Если once_only=True - проверяем в БД наличие выполненных событий
240
+ if once_only:
241
+ check_query = supabase_client.client.table('scheduled_events')\
242
+ .select('id, status, session_id')\
243
+ .eq('event_type', event_type)\
244
+ .eq('user_id', user_id)\
245
+ .eq('status', 'completed')
246
+
247
+ # НЕ фильтруем по session_id - проверяем ВСЕ выполненные события пользователя
248
+ # if session_id:
249
+ # check_query = check_query.eq('session_id', session_id)
250
+
251
+ existing = check_query.execute()
252
+
253
+ logger.info(f" 🔍 Проверка БД: найдено {len(existing.data) if existing.data else 0} выполненных событий '{event_type}' для user_id={user_id}")
254
+
255
+ if existing.data:
256
+ logger.info(f" 🔄 Событие '{event_type}' уже выполнялось для пользователя {user_id}, пропускаем (once_only=True)")
257
+ logger.info(f" 📋 Найденные события: {existing.data}")
258
+ continue
259
+
260
+ # Немедленно выполняем событие
261
+ logger.info(f" 🎯 Немедленно выполняем user_event: '{event_type}'")
262
+
232
263
  try:
233
- logger.info(f" 🎯 Сохраняем как user_event: '{event_type}'")
234
- event_id = await save_immediate_event(event_type, user_id, event_info, session_id)
264
+ # Выполняем СНАЧАЛА
265
+ result = await execute_event_handler(event_type, user_id, event_info)
266
+
267
+ # Сохраняем в БД УЖЕ со статусом completed (избегаем дублирования)
268
+ event_record = {
269
+ 'event_type': event_type,
270
+ 'event_category': 'user_event',
271
+ 'user_id': user_id,
272
+ 'event_data': event_info,
273
+ 'scheduled_at': None,
274
+ 'status': 'completed', # Сразу completed!
275
+ 'session_id': session_id,
276
+ 'executed_at': __import__('datetime').datetime.now(__import__('datetime').timezone.utc).isoformat(),
277
+ 'result_data': __import__('json').dumps(result, ensure_ascii=False) if result else None
278
+ }
279
+ response = supabase_client.client.table('scheduled_events').insert(event_record).execute()
280
+ event_id = response.data[0]['id']
281
+
282
+ should_notify = event_handler_info.get('notify', False)
235
283
  should_execute_immediately = True
236
- logger.info(f" 💾 Событие сохранено в БД: {event_id}")
237
- except ValueError as e:
238
- if "once_only=True" in str(e):
239
- logger.info(f" 🔄 Событие '{event_type}' уже обрабатывалось, пропускаем")
240
- continue
241
- else:
242
- raise
284
+
285
+ logger.info(f" ✅ Событие {event_id} выполнено и сохранено как completed")
286
+
287
+ except Exception as e:
288
+ logger.error(f" ❌ Ошибка выполнения события: {e}")
289
+ # Сохраняем ошибку в БД
290
+ event_record = {
291
+ 'event_type': event_type,
292
+ 'event_category': 'user_event',
293
+ 'user_id': user_id,
294
+ 'event_data': event_info,
295
+ 'scheduled_at': None,
296
+ 'status': 'failed',
297
+ 'session_id': session_id,
298
+ 'last_error': str(e)
299
+ }
300
+ supabase_client.client.table('scheduled_events').insert(event_record).execute()
301
+ raise
243
302
 
244
303
  # Если не user_event, пробуем как запланированную задачу
245
304
  elif event_type in scheduled_tasks:
@@ -283,18 +342,7 @@ async def process_events(session_id: str, events: list, user_id: int):
283
342
  logger.debug(f" - event_handlers: {list(event_handlers.keys())}")
284
343
  logger.debug(f" - scheduled_tasks: {list(scheduled_tasks.keys())}")
285
344
  logger.debug(f" - global_handlers: {list(global_handlers.keys())}")
286
-
287
- # Выполняем немедленные события
288
- if should_execute_immediately and event_id:
289
- try:
290
- result = await execute_event_handler(event_type, user_id, event_info)
291
- should_notify = result.get('notify', False)
292
- await update_event_result(event_id, 'completed', result)
293
- logger.info(f" ✅ Событие выполнено: {result}")
294
- except Exception as e:
295
- await update_event_result(event_id, 'failed', None, str(e))
296
- logger.error(f" ❌ Ошибка выполнения события: {e}")
297
-
345
+
298
346
  except ValueError as e:
299
347
  logger.warning(f" ⚠️ Обработчик/задача не найдены: {e}")
300
348
  except Exception as e: