import os import logging import threading import signal import time from flask import Flask, request, jsonify, send_from_directory from telegram import Update from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters from telegram.request import HTTPXRequest # Добавлено для управления таймаутами from supabase.client import create_client import google.generativeai as genai from groq import Groq # --- 0. КОНТРОЛЬ ВЕРСИИ --- VERSION = "20260417.01" # --- 1. НАСТРОЙКА ЛОГИРОВАНИЯ --- logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) # --- 2. ИНИЦИАЛИЗАЦИЯ КЛЮЧЕЙ --- TELEGRAM_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") SUPABASE_URL = os.environ.get("SUPABASE_URL") SUPABASE_KEY = os.environ.get("SUPABASE_SERVICE_ROLE_KEY") GEMINI_KEY = os.environ.get("GEMINI_API_KEY") GROQ_KEY = os.environ.get("GROQ_API_KEY") ADMIN_ID = os.environ.get("MY_TELEGRAM_ID") WEB_PASSWORD = os.environ.get("WEB_PASSWORD", "admin") # --- 3. ИНИЦИАЛИЗАЦИЯ СЕРВИСОВ --- supabase = create_client(SUPABASE_URL, SUPABASE_KEY) genai.configure(api_key=GEMINI_KEY) groq_client = Groq(api_key=GROQ_KEY) # --- 4. ВЕБ-СЕРВЕР (FLASK) --- app = Flask(__name__) api_usage_counter = 0 telegram_usage_counter = 0 @app.route('/static/') def send_static(path): """Раздача логотипа и других файлов.""" return send_from_directory('static', path) @app.route('/.well-known/ai-plugin.json') def ai_plugin(): """Манифест для внешних ИИ (ChatGPT и др.).""" return jsonify({ "schema_version": "v1", "name_for_human": "Evolution Prometheus", "name_for_model": "evolution_prometheus", "description_for_human": f"База знаний по книге 'Концепция развития'. Версия {VERSION}", "description_for_model": "Доступ к философским и научным концепциям книги 'Концепция развития'.", "auth": {"type": "none"}, "api": {"type": "openapi", "url": "https://wladlenvk-evolution-prometheus.hf.space/openapi.json"}, "logo_url": "https://i.ibb.co/C5Z95Jqs/logo.png", "contact_email": "wladlenvk@gmail.com" }) @app.route('/') def admin_panel(): """Главная страница и панель управления.""" global api_usage_counter, telegram_usage_counter pwd = request.args.get('pwd') if pwd != WEB_PASSWORD: return ( f"

Система 'Прометей'

" f"

Вер. {VERSION}

" f"

Telegram сообщений: {telegram_usage_counter}

" f"

API запросов: {api_usage_counter}

" f"

Для доступа к управлению введите пароль в URL.

" ) return f"""

Панель управления Прометей

Версия: {VERSION}


Статистика: ТГ: {telegram_usage_counter} | API: {api_usage_counter}

""" @app.route('/action') def handle_action(): pwd = request.args.get('pwd') do = request.args.get('do') if pwd != WEB_PASSWORD: return "Доступ запрещен", 403 if do == "restart": threading.Thread(target=lambda: (time.sleep(1), os.kill(os.getpid(), signal.SIGTERM))).start() return "

Перезагрузка...

" if do == "clear_history": try: supabase.table("chat_history").delete().neq("user_id", "0").execute() return "

История очищена!

Назад" except Exception as e: return f"Ошибка БД: {str(e)}" return "Неизвестное действие" @app.route('/openapi.json') def openapi_spec(): """Техпаспорт API.""" return jsonify({ "openapi": "3.0.0", "info": {"title": "Prometheus Knowledge Base", "version": VERSION}, "servers": [{"url": "https://wladlenvk-evolution-prometheus.hf.space"}], "paths": { "/api/knowledge": { "post": { "summary": "Поиск в книге", "operationId": "searchKnowledge", "requestBody": { "content": {"application/json": {"schema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}}} }, "responses": {"200": {"description": "OK"}} } } } }) @app.route('/api/knowledge', methods=['POST']) def external_ai_request(): global api_usage_counter try: data = request.json user_query = data.get("query", "") if not user_query: return jsonify({"error": "Empty query"}), 400 query_vector = get_embedding(user_query) relevant_docs = search_docs(query_vector) api_usage_counter += 1 return jsonify({ "source": "Концепция развития. Нетривиальный взгляд на эволюцию.", "content": [doc['content'] for doc in relevant_docs] if relevant_docs else [], "version": VERSION }) except Exception as e: return jsonify({"error": str(e)}), 500 def run_flask(): port = int(os.environ.get("PORT", 7860)) app.run(host='0.0.0.0', port=port) # --- 5. ФУНКЦИИ ПАМЯТИ И RAG (БЕЗ ИЗМЕНЕНИЙ) --- def save_chat_message(user_id, role, content): try: supabase.table("chat_history").insert({"user_id": user_id, "role": role, "content": content}).execute() except Exception as e: logging.error(f"Error saving history: {e}") def get_chat_history(user_id, limit=10): try: res = supabase.table("chat_history").select("role", "content").eq("user_id", user_id).order("created_at", desc=True).limit(limit).execute() return res.data[::-1] except Exception as e: logging.error(f"Error getting history: {e}") return [] def get_embedding(text): try: result = genai.embed_content(model="models/gemini-embedding-001", content=text, task_type="retrieval_query") return result['embedding'] except Exception as e: raise Exception(f"Gemini Error: {str(e)}") def search_docs(query_embedding): try: rpc_params = {'query_embedding': query_embedding, 'match_threshold': 0.5, 'match_count': 5} response = supabase.rpc('match_documents', rpc_params).execute() return response.data except Exception as e: raise Exception(f"Supabase Error: {str(e)}") def split_text(text, max_length=4000): return [text[i:i+max_length] for i in range(0, len(text), max_length)] # --- 7. ОБРАБОТЧИКИ ТЕЛЕГРАМ (БЕЗ ИЗМЕНЕНИЙ) --- async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text(f"Бот-консультант системы 'Прометей' (v{VERSION}) готов.") async def restart_bot(update: Update, context: ContextTypes.DEFAULT_TYPE): if str(update.message.from_user.id) == ADMIN_ID: await update.message.reply_text("♻️ Перезагрузка...") os.kill(os.getpid(), signal.SIGTERM) async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): global telegram_usage_counter telegram_usage_counter += 1 user_id = update.message.from_user.id user_text = update.message.text status_msg = await update.message.reply_text("🔍 Анализирую...") try: save_chat_message(user_id, "user", user_text) query_vector = get_embedding(user_text) relevant_docs = search_docs(query_vector) context_text = "\n\n".join([doc['content'] for doc in relevant_docs]) if relevant_docs else "Контекст не найден." history = get_chat_history(user_id) messages = [{"role": "system", "content": f"Ты тех-ассистент. Отвечай строго по контексту.\n\nКОНТЕКСТ:\n{context_text}"}] for msg in history: messages.append({"role": msg["role"], "content": msg["content"]}) completion = groq_client.chat.completions.create(model="llama-3.3-70b-versatile", messages=messages, temperature=0.1) final_answer = completion.choices[0].message.content save_chat_message(user_id, "assistant", final_answer) if len(final_answer) <= 4096: await status_msg.edit_text(final_answer) else: await status_msg.delete() for chunk in split_text(final_answer): await update.message.reply_text(chunk) except Exception as e: logging.error(f"Error: {e}") await update.message.reply_text(f"⚠️ Ошибка: {str(e)}") if __name__ == '__main__': if not TELEGRAM_TOKEN: print("CRITICAL ERROR: No Token") else: # Увеличенные таймауты оставляем — это полезная практика для облачных хостингов t_request = HTTPXRequest( connect_timeout=45, read_timeout=45 ) # Запуск Flask в отдельном потоке threading.Thread(target=run_flask, daemon=True).start() # Сборка и запуск бота application = ApplicationBuilder().token(TELEGRAM_TOKEN).request(t_request).build() application.add_handler(CommandHandler('start', start)) application.add_handler(CommandHandler('restart', restart_bot)) application.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), handle_message)) print(f"--- СИСТЕМА ПРОМЕТЕЙ {VERSION} ЗАПУЩЕНА ---") application.run_polling()