Really-amin commited on
Commit
943aa17
1 Parent(s): 56a6340

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +473 -182
app.py CHANGED
@@ -1,197 +1,488 @@
1
- from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
2
- from fastapi.responses import HTMLResponse
3
- from fastapi.templating import Jinja2Templates
4
- from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
5
- import asyncio
6
  import logging
7
- import httpx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  import torch
 
 
9
 
10
- # Initialize FastAPI
11
- app = FastAPI()
12
-
13
- # Logging setup
14
- logging.basicConfig(level=logging.INFO)
15
-
16
- # Telegram Token and Chat ID (Replace with your actual values)
17
- TELEGRAM_TOKEN = "7437859619:AAGeGG3ZkLM0OVaw-Exx1uMRE55JtBCZZCY"
18
- CHAT_ID = "-1002228627548"
19
-
20
- # Templating setup
21
- templates = Jinja2Templates(directory="templates")
22
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- # WebSocket Manager
25
- class WebSocketManager:
26
  def __init__(self):
27
- self.active_connection: WebSocket = None
28
-
29
- async def connect(self, websocket: WebSocket):
30
- """Connects the WebSocket"""
31
- await websocket.accept()
32
- self.active_connection = websocket
33
- logging.info("WebSocket connected.")
34
-
35
- async def disconnect(self):
36
- """Disconnects the WebSocket"""
37
- if self.active_connection:
38
- await self.active_connection.close()
39
- self.active_connection = None
40
- logging.info("WebSocket disconnected.")
41
-
42
- async def send_message(self, message: str):
43
- """Sends a message through WebSocket"""
44
- if self.active_connection:
45
- await self.active_connection.send_text(message)
46
- logging.info(f"Sent via WebSocket: {message}")
47
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- websocket_manager = WebSocketManager()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
 
 
 
 
51
 
52
- # BLOOM Model Manager
53
- class BloomAI:
54
- def __init__(self):
55
- self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
56
- self.pipeline = None
 
 
 
57
 
58
- def load_model(self):
59
- """Loads BLOOM AI Model"""
60
- logging.info("Loading BLOOM model...")
61
- tokenizer = AutoTokenizer.from_pretrained("bigscience/bloom-560m")
62
- model = AutoModelForCausalLM.from_pretrained("bigscience/bloom-560m")
63
- self.pipeline = pipeline(
64
- "text-generation",
65
- model=model,
66
- tokenizer=tokenizer,
67
- device=0 if torch.cuda.is_available() else -1
68
- )
69
- logging.info("BLOOM model loaded successfully.")
70
-
71
- async def generate_response(self, prompt: str) -> str:
72
- """Generates a response using BLOOM"""
73
- if not prompt.strip():
74
- return "⚠️ Please send a valid message."
75
- logging.info(f"Generating response for prompt: {prompt}")
76
- outputs = self.pipeline(
77
- prompt,
78
- max_length=100,
79
- do_sample=True,
80
- temperature=0.7,
81
- top_k=50,
82
- top_p=0.9,
83
- num_return_sequences=1,
84
- no_repeat_ngram_size=2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  )
86
- response = outputs[0]["generated_text"]
87
- return response.strip()
88
-
89
-
90
- # Initialize BLOOM
91
- bloom_ai = BloomAI()
92
- bloom_ai.load_model()
93
-
94
-
95
- # Telegram Message Handling
96
- async def send_telegram_message(text: str):
97
- """Sends a message to Telegram"""
98
- async with httpx.AsyncClient() as client:
99
- url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
100
- payload = {"chat_id": CHAT_ID, "text": text}
101
- response = await client.post(url, json=payload)
102
- if response.status_code == 200:
103
- logging.info(f"Sent to Telegram: {text}")
104
- else:
105
- logging.error(f"Failed to send message to Telegram: {response.text}")
106
-
107
-
108
- @app.post("/telegram")
109
- async def telegram_webhook(update: dict):
110
- """Handles Telegram Webhook messages"""
111
- if "message" in update:
112
- chat_id = str(update["message"]["chat"]["id"])
113
- if chat_id != CHAT_ID:
114
- return {"status": "Unauthorized"}
115
-
116
- user_message = update["message"]["text"]
117
- logging.info(f"Received from Telegram: {user_message}")
118
-
119
- # Process the message
120
- response = await bloom_ai.generate_response(user_message)
121
- await send_telegram_message(response)
122
- return {"status": "ok"}
123
-
124
-
125
- # WebSocket Endpoint
126
  @app.websocket("/ws")
127
  async def websocket_endpoint(websocket: WebSocket):
128
- """WebSocket communication for real-time interaction"""
129
- await websocket_manager.connect(websocket)
 
130
  try:
131
  while True:
132
- # Receive message from WebSocket
133
- data = await websocket.receive_text()
134
- logging.info(f"Received from WebSocket: {data}")
135
-
136
- # Process the message
137
- response = await bloom_ai.generate_response(data)
138
-
139
- # Send response back through WebSocket
140
- await websocket_manager.send_message(response)
141
- except WebSocketDisconnect:
142
- # Handle WebSocket disconnection
143
- await websocket_manager.disconnect()
144
-
145
-
146
- # HTML Test UI
147
- @app.get("/")
148
- async def get_ui(request: Request):
149
- """Displays the WebSocket HTML UI"""
150
- return templates.TemplateResponse("index.html", {"request": request})
151
-
152
-
153
- # Simple UI (fallback in case templates folder is not available)
154
- @app.get("/simple-ui")
155
- async def simple_ui():
156
- """Fallback HTML for WebSocket Test"""
157
- return HTMLResponse(content="""
158
- <!DOCTYPE html>
159
- <html>
160
- <head>
161
- <title>WebSocket Test</title>
162
- <script>
163
- let ws = new WebSocket("ws://localhost:8000/ws");
164
-
165
- ws.onopen = () => {
166
- console.log("WebSocket connection opened.");
167
- };
168
-
169
- ws.onmessage = (event) => {
170
- console.log("Message from server:", event.data);
171
- const msgContainer = document.getElementById("messages");
172
- const msg = document.createElement("div");
173
- msg.innerText = event.data;
174
- msgContainer.appendChild(msg);
175
- };
176
-
177
- ws.onclose = () => {
178
- console.log("WebSocket connection closed.");
179
- };
180
-
181
- function sendMessage() {
182
- const input = document.getElementById("messageInput");
183
- const message = input.value;
184
- ws.send(message);
185
- input.value = "";
186
- }
187
- </script>
188
- </head>
189
- <body>
190
- <h1>WebSocket Test</h1>
191
- <div id="messages" style="border: 1px solid black; height: 200px; overflow-y: scroll;"></div>
192
- <input id="messageInput" type="text" />
193
- <button onclick="sendMessage()">Send</button>
194
- </body>
195
- </html>
196
- """)
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gc
4
+ import json
 
5
  import logging
6
+ import asyncio
7
+ import traceback
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Dict, Optional, Set, List, Any
11
+ from collections import deque
12
+ from contextlib import asynccontextmanager
13
+ import tempfile
14
+
15
+ # تنظیمات اولیه و مسیرها
16
+ BASE_DIR = Path(__file__).parent
17
+ temp_cache_dir = tempfile.gettempdir()
18
+
19
+ # تنظیم متغیرهای محیطی قبل از import کردن کتابخانه‌های هوش مصنوعی
20
+ os.environ["HF_HOME"] = str(temp_cache_dir)
21
+ if "TRANSFORMERS_CACHE" in os.environ:
22
+ del os.environ["TRANSFORMERS_CACHE"]
23
+ os.environ["TRANSFORMERS_PARALLELISM"] = "false"
24
+ os.environ["TORCH_HOME"] = str(Path(temp_cache_dir) / "torch")
25
+
26
+ # کتابخانه‌های هوش مصنوعی
27
  import torch
28
+ import transformers
29
+ from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM, AutoModelForMaskedLM
30
 
31
+ # کتابخانه‌های وب و تلگرام
32
+ import aiofiles
33
+ import aiodns
34
+ import httpx
35
+ import uvicorn
36
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request, HTTPException, Response, Query
37
+ from fastapi.responses import HTMLResponse, FileResponse
38
+ from fastapi.staticfiles import StaticFiles
39
+ from fastapi.templating import Jinja2Templates
40
+ from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
41
+ from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler, ContextTypes, filters
42
+ from telegram.error import NetworkError
43
+
44
+ # کتابخانه‌های دیگر
45
+ import hazm
46
+ from logging.handlers import RotatingFileHandler
47
+ from cachetools import TTLCache
48
+
49
+ class AppConfig:
50
+ """تنظیمات اصلی برنامه"""
51
+
52
+ # تنظیمات مسیرها
53
+ BASE_DIR = Path("/app")
54
+ STATIC_DIR = BASE_DIR / "static"
55
+ TEMPLATES_DIR = BASE_DIR / "templates"
56
+ CACHE_DIR = Path(temp_cache_dir) / "cache"
57
+ LOG_DIR = Path(temp_cache_dir) / "logs"
58
+
59
+ # تنظیمات تلگرام
60
+ TELEGRAM_TOKEN = "7437859619:AAGeGG3ZkLM0OVaw-Exx1uMRE55JtBCZZCY"
61
+ CHAT_ID = "-1002228627548"
62
+ ADMIN_CHAT_ID = "1053836064"
63
+ ALLOWED_CHAT_IDS = {"-1002228627548", "1053836064"}
64
+
65
+ # تنظیمات مدل
66
+ MODEL_NAME = "HooshvareLab/bert-fa-base-uncased"
67
+ MAX_LENGTH = 128
68
+ MIN_LENGTH = 20
69
+ TEMPERATURE = 0.7
70
+ TOP_K = 50
71
+ TOP_P = 0.92
72
+
73
+ # تنظیمات سیستم
74
+ MAX_RETRIES = 3
75
+ CACHE_TTL = 300
76
+ MAX_CONNECTIONS = 5
77
+ MEMORY_THRESHOLD = 85
78
+ CLEANUP_THRESHOLD = 80
79
+ MAX_MESSAGE_LENGTH = 512
80
+ MAX_THREADS = 1
81
+
82
+ @classmethod
83
+ def setup_directories(cls) -> None:
84
+ """ایجاد دایرکتوری‌های مورد نیاز"""
85
+ try:
86
+ for path in [cls.CACHE_DIR, cls.LOG_DIR, cls.STATIC_DIR, cls.TEMPLATES_DIR]:
87
+ path.mkdir(exist_ok=True, parents=True)
88
+ logging.info("دایرکتوری‌ها با موفقیت ایجاد شدند")
89
+ except Exception as e:
90
+ logging.error(f"خطا در ایجاد دایرکتوری‌ها: {e}")
91
+ raise
92
+
93
+ def setup_logging() -> None:
94
+ """راه‌اندازی سیستم لاگ"""
95
+ logger = logging.getLogger()
96
+ logger.setLevel(logging.INFO)
97
+
98
+ formatter = logging.Formatter(
99
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
100
+ )
101
+
102
+ # لاگ در فایل
103
+ file_handler = RotatingFileHandler(
104
+ AppConfig.LOG_DIR / 'app.log',
105
+ maxBytes=10**6,
106
+ backupCount=5,
107
+ encoding='utf-8'
108
+ )
109
+ file_handler.setFormatter(formatter)
110
+
111
+ # لاگ در کنسول
112
+ console_handler = logging.StreamHandler(sys.stdout)
113
+ console_handler.setFormatter(formatter)
114
+
115
+ logger.handlers = []
116
+ logger.addHandler(file_handler)
117
+ logger.addHandler(console_handler)
118
+
119
+ class AIModel:
120
+ """کلاس مدیریت مدل هوش مصنوعی"""
121
 
 
 
122
  def __init__(self):
123
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
124
+ self.model = None
125
+ self.tokenizer = None
126
+ self.pipeline = None
127
+ self._initialize_model()
128
+
129
+ def _initialize_model(self):
130
+ try:
131
+ logging.info(f"در حال بارگذاری مدل {AppConfig.MODEL_NAME}...")
132
+
133
+ # بارگذاری توکنایزر
134
+ self.tokenizer = AutoTokenizer.from_pretrained(
135
+ AppConfig.MODEL_NAME,
136
+ cache_dir=AppConfig.CACHE_DIR,
137
+ use_fast=True
138
+ )
139
+
140
+ # بارگذاری مدل
141
+ self.model = AutoModelForMaskedLM.from_pretrained(
142
+ AppConfig.MODEL_NAME,
143
+ cache_dir=AppConfig.CACHE_DIR,
144
+ torch_dtype=torch.float32,
145
+ low_cpu_mem_usage=True,
146
+ device_map="auto" if torch.cuda.is_available() else None
147
+ )
148
+
149
+ # ایجاد پایپلاین
150
+ self.pipeline = pipeline(
151
+ "fill-mask",
152
+ model=self.model,
153
+ tokenizer=self.tokenizer,
154
+ device=0 if torch.cuda.is_available() else -1
155
+ )
156
+
157
+ logging.info("مدل با موفقیت بارگذاری شد")
158
+ return True
159
+ except Exception as e:
160
+ logging.error(f"خطا در بارگذاری مدل: {e}")
161
+ return False
162
+
163
+ async def generate_response(self, text: str) -> str:
164
+ try:
165
+ # اضافه کردن توکن ماسک اگر وجود نداشته باشد
166
+ if "[MASK]" not in text:
167
+ if len(text.split()) > 3:
168
+ words = text.split()
169
+ words.insert(3, "[MASK]")
170
+ text = " ".join(words)
171
+ else:
172
+ text = text + " [MASK]"
173
+
174
+ # تولید پاسخ
175
+ result = self.pipeline(text)
176
+ response = result[0]["sequence"]
177
+
178
+ # پاکسازی پاسخ
179
+ response = response.replace("[MASK]", "").strip()
180
+ if response.startswith(text):
181
+ response = response[len(text):].strip()
182
+
183
+ return response
184
+
185
+ except Exception as e:
186
+ logging.error(f"خطا در تولید پاسخ: {e}")
187
+ return "متأسفم، در تولید پاسخ مشکلی پیش آمد."
188
+
189
+ class TelegramUI:
190
+ """کلاس مدیریت رابط کاربری تلگرام"""
191
+
192
+ @staticmethod
193
+ def get_main_menu() -> InlineKeyboardMarkup:
194
+ """ایجاد منوی اصلی"""
195
+ keyboard = [
196
+ [
197
+ InlineKeyboardButton("💬 چت جدید", callback_data="new_chat"),
198
+ InlineKeyboardButton("🔍 جستجو", callback_data="search")
199
+ ],
200
+ [
201
+ InlineKeyboardButton("📚 آموزش", callback_data="learn"),
202
+ InlineKeyboardButton("🎯 تمرین", callback_data="practice")
203
+ ],
204
+ [
205
+ InlineKeyboardButton("⚙️ تنظیمات", callback_data="settings"),
206
+ InlineKeyboardButton("❓ راهنما", callback_data="help")
207
+ ]
208
+ ]
209
+ return InlineKeyboardMarkup(keyboard)
210
+
211
+ @staticmethod
212
+ def get_chat_keyboard() -> ReplyKeyboardMarkup:
213
+ """ایجاد صفحه کلید چت"""
214
+ keyboard = [
215
+ ["💭 چت عمومی", "❓ سوال و جواب"],
216
+ ["📚 آموزش", "🎯 تمرین"],
217
+ ["🏠 منوی اصلی"]
218
+ ]
219
+ return ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
220
+
221
+ class TelegramBot:
222
+ """کلاس اصلی ربات تلگرام"""
223
+
224
+ def __init__(self, ai_model: AIModel):
225
+ self.model = ai_model
226
+ self.ui = TelegramUI()
227
+ self.application = Application.builder().token(AppConfig.TELEGRAM_TOKEN).build()
228
+ self._setup_handlers()
229
+ self.normalizer = hazm.Normalizer()
230
+
231
+ def _setup_handlers(self):
232
+ """تنظیم هندلرهای ربات"""
233
+ self.application.add_handler(CommandHandler("start", self.start_command))
234
+ self.application.add_handler(CallbackQueryHandler(self.button_callback))
235
+ self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message))
236
+
237
+ async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
238
+ """پردازش دستور شروع"""
239
+ welcome_text = """
240
+ 🌟 به دستیار هوشمند خوش آمدید!
241
+
242
+ من می‌توانم در موارد زیر به شما کمک کنم:
243
+ • پاسخ به سؤالات
244
+ • آموزش مطالب
245
+ • حل تمرین
246
+ • جستجو در منابع
247
+
248
+ لطفاً یکی از گزینه‌های زیر را انتخاب کنید:
249
+ """
250
+ await update.message.reply_text(
251
+ welcome_text,
252
+ reply_markup=self.ui.get_main_menu()
253
+ )
254
 
255
+ async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
256
+ """پردازش دکمه‌های منو"""
257
+ query = update.callback_query
258
+ await query.answer()
259
+
260
+ if query.data == "new_chat":
261
+ await query.message.reply_text(
262
+ "💭 نوع چت را انتخاب کنید:",
263
+ reply_markup=self.ui.get_chat_keyboard()
264
+ )
265
+ elif query.data == "help":
266
+ help_text = """
267
+ 📚 راهنمای استفاده:
268
+
269
+ 1️⃣ برای شروع گفتگو، از دکمه "چت جدید" استفاده کنید
270
+ 2️⃣ نوع گفتگو را انتخاب کنید
271
+ 3️⃣ سؤال یا پیام خود را ارسال کنید
272
+ 4️⃣ منتظر پاسخ بمانید
273
+
274
+ ❗️ نکات مهم:
275
+ • سؤالات خود را واضح و دقیق مطرح کنید
276
+ • از منوی تنظیمات برای شخصی‌سازی استفاده کنید
277
+ • برای بازگشت به منوی اصلی از دکمه "🏠 منوی اصلی" استفاده کنید
278
+ """
279
+ await query.message.edit_text(help_text, reply_markup=self.ui.get_main_menu())
280
+
281
+ async def handle_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
282
+ """پردازش پیام‌های دریافتی"""
283
+ if update.message.text == "🏠 منوی اصلی":
284
+ await self.start_command(update, context)
285
+ return
286
+
287
+ try:
288
+ # نرمال‌سازی متن
289
+ normalized_text = self.normalizer.normalize(update.message.text)
290
+ # تولید پاسخ
291
+ response = await self.model.generate_response(normalized_text)
292
+ await update.message.reply_text(f"🤖 {response}")
293
+ except Exception as e:
294
+ logging.error(f"خطا در پردازش پیام: {e}")
295
+ await update.message.reply_text(
296
+ "متأسفانه خطایی رخ داد. لطفاً دوباره تلاش کنید."
297
+ )
298
+
299
+ async def run(self):
300
+ """اجرای ربات"""
301
+ try:
302
+ await self.application.run_polling()
303
+ except Exception as e:
304
+ logging.error(f"خطا در اجرای ربات: {e}")
305
+ raise
306
+
307
+ # ایجاد برنامه FastAPI
308
+ app = FastAPI(title="AI Assistant")
309
+
310
+ # پیوست کردن فایل‌های استاتیک
311
+ app.mount("/static", StaticFiles(directory=AppConfig.STATIC_DIR), name="static")
312
+ templates = Jinja2Templates(directory=AppConfig.TEMPLATES_DIR)
313
+
314
+ # HTML پیش‌فرض
315
+ DEFAULT_HTML = """
316
+ <!DOCTYPE html>
317
+ <html dir="rtl" lang="fa">
318
+ <head>
319
+ <meta charset="UTF-8">
320
+ <title>دستیار هوشمند</title>
321
+ <style>
322
+ body {
323
+ font-family: Tahoma, Arial;
324
+ background-color: #f5f5f5;
325
+ margin: 0;
326
+ padding: 20px;
327
+ }
328
+ .container {
329
+ max-width: 800px;
330
+ margin: 0 auto;
331
+ background: white;
332
+ padding: 20px;
333
+ border-radius: 10px;
334
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
335
+ }
336
+ h1 { color: #1a73e8; }
337
+ .status { margin: 20px 0; }
338
+ .chat-container {
339
+ margin-top: 20px;
340
+ border: 1px solid #e0e0e0;
341
+ border-radius: 8px;
342
+ padding: 10px;
343
+ min-height: 300px;
344
+ }
345
+ </style>
346
+ </head>
347
+ <body>
348
+ <div class="container">
349
+ <h1>دستیار هوشمند</h1>
350
+ <div class="status">
351
+ <p>برای استفاده از دستیار هوشمصنوعی، به ربات تلگرام مراجعه کنید یا از رابط وب استفاده نمایید.</p>
352
+ </div>
353
+ <div class="chat-container" id="chat-container">
354
+ <!-- Messages will appear here -->
355
+ </div>
356
+ <input type="text" id="message-input" placeholder="پیام خود را بنویسید..." style="width: 100%; padding: 10px; margin-top: 10px;">
357
+ <button onclick="sendMessage()" style="margin-top: 10px; padding: 10px 20px;">ارسال</button>
358
+ </div>
359
+ <script>
360
+ const ws = new WebSocket(`ws://${window.location.host}/ws`);
361
+ const chatContainer = document.getElementById('chat-container');
362
+ const messageInput = document.getElementById('message-input');
363
+
364
+ ws.onmessage = function(event) {
365
+ const response = JSON.parse(event.data);
366
+ appendMessage('bot', response.response);
367
+ };
368
+
369
+ function appendMessage(type, text) {
370
+ const messageDiv = document.createElement('div');
371
+ messageDiv.style.padding = '10px';
372
+ messageDiv.style.margin = '5px';
373
+ messageDiv.style.borderRadius = '5px';
374
+
375
+ if (type === 'user') {
376
+ messageDiv.style.backgroundColor = '#e3f2fd';
377
+ messageDiv.style.marginLeft = '20%';
378
+ } else {
379
+ messageDiv.style.backgroundColor = '#f5f5f5';
380
+ messageDiv.style.marginRight = '20%';
381
+ }
382
 
383
+ messageDiv.textContent = text;
384
+ chatContainer.appendChild(messageDiv);
385
+ chatContainer.scrollTop = chatContainer.scrollHeight;
386
+ }
387
 
388
+ function sendMessage() {
389
+ const message = messageInput.value.trim();
390
+ if (message) {
391
+ appendMessage('user', message);
392
+ ws.send(message);
393
+ messageInput.value = '';
394
+ }
395
+ }
396
 
397
+ messageInput.addEventListener('keypress', function(e) {
398
+ if (e.key === 'Enter') {
399
+ sendMessage();
400
+ }
401
+ });
402
+ </script>
403
+ </body>
404
+ </html>
405
+ """
406
+
407
+ @asynccontextmanager
408
+ async def lifespan(app: FastAPI):
409
+ """مدیریت چرخه حیات برنامه"""
410
+ # راه‌اندازی
411
+ AppConfig.setup_directories()
412
+ setup_logging()
413
+
414
+ # ایجاد نمونه از مدل هوش مصنوعی
415
+ app.state.model = AIModel()
416
+ app.state.is_ready = asyncio.Event()
417
+
418
+ # راه‌اندازی ربات تلگرام
419
+ app.state.bot = TelegramBot(app.state.model)
420
+ asyncio.create_task(app.state.bot.run())
421
+
422
+ yield
423
+
424
+ # خاتمه
425
+ tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
426
+ for task in tasks:
427
+ task.cancel()
428
+ await asyncio.gather(*tasks, return_exceptions=True)
429
+ logging.info("برنامه با موفقیت خاتمه یافت")
430
+
431
+ @app.get("/", response_class=HTMLResponse)
432
+ async def home(request: Request):
433
+ """صفحه اصلی"""
434
+ try:
435
+ return templates.TemplateResponse(
436
+ "index.html",
437
+ {
438
+ "request": request,
439
+ "bot_status": "فعال" if request.app.state.is_ready.is_set() else "در حال آماده‌سازی"
440
+ }
441
  )
442
+ except Exception as e:
443
+ logging.error(f"خطا در نمایش صفحه اصلی: {e}")
444
+ return HTMLResponse(content=DEFAULT_HTML)
445
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  @app.websocket("/ws")
447
  async def websocket_endpoint(websocket: WebSocket):
448
+ """اتصال WebSocket"""
449
+ await websocket.accept()
450
+
451
  try:
452
  while True:
453
+ # دریافت پیام
454
+ text = await websocket.receive_text()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
 
456
+ # تولید پاسخ
457
+ response = await app.state.model.generate_response(text)
458
+
459
+ # ارسال پاسخ
460
+ await websocket.send_json({
461
+ "response": response
462
+ })
463
+ except WebSocketDisconnect:
464
+ logging.info("اتصال WebSocket قطع شد")
465
+ except Exception as e:
466
+ logging.error(f"خطا در WebSocket: {e}")
467
+ await websocket.close()
468
+
469
+ @app.exception_handler(404)
470
+ async def not_found_handler(request: Request, exc: Exception):
471
+ """پردازش خطای 404"""
472
+ return HTMLResponse(content=DEFAULT_HTML, status_code=404)
473
+
474
+ @app.exception_handler(500)
475
+ async def server_error_handler(request: Request, exc: Exception):
476
+ """پردازش خطای 500"""
477
+ return HTMLResponse(content=DEFAULT_HTML, status_code=500)
478
+
479
+ if __name__ == "__main__":
480
+ # اجرای برنامه
481
+ uvicorn.run(
482
+ app,
483
+ host="0.0.0.0",
484
+ port=7860,
485
+ reload=False,
486
+ workers=1,
487
+ log_level="info"
488
+ )