ysn-rfd/text-dataset-tiny-code-script-py-format
USED of tahamajs/medicine_ds_persian for .parquet file USED of Alijafarixcs2/persian-it-llama2-2k for .parquet file USED of Abirate/english_quotes for .jsonl file NEW FILES (05/12/2025) NEW FILES (12/26/2025) NEW FILES (02/15/2026)
31.7k
1# main.py2 3import os4import logging5import asyncio6import httpx7import time8from telegram import Update9from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes10from openai import AsyncOpenAI11from keep_alive import start_keep_alive12 13# وارد کردن مدیر دادهها و پنل ادمین14import data_manager15import admin_panel16 17# شروع سرویس نگه داشتن ربات فعال18start_keep_alive()19 20# --- بهبود لاگینگ ---21logging.basicConfig(22 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 23 level=logging.INFO,24 filename=data_manager.LOG_FILE, 25 filemode='a'26)27logger = logging.getLogger(__name__)28 29try:30 with open(data_manager.LOG_FILE, 'a') as f:31 f.write("")32except Exception as e:33 print(f"FATAL: Could not write to log file at {data_manager.LOG_FILE}. Error: {e}")34 logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")35 36# --- ایجاد یک کلاینت HTTP بهینهسازیشده ---37http_client = httpx.AsyncClient(38 http2=True,39 limits=httpx.Limits(max_keepalive_connections=20, max_connections=100, keepalive_expiry=30.0),40 timeout=httpx.Timeout(timeout=60.0, connect=10.0, read=45.0, write=10.0)41)42 43# کلاینت OpenAI (HuggingFace)44client = AsyncOpenAI(45 base_url="https://router.huggingface.co/v1",46 api_key=os.environ["HF_TOKEN"],47 http_client=http_client48)49 50# --- دیکشنری برای مدیریت وظایف پسزمینه هر کاربر ---51user_tasks = {}52group_tasks = {}53 54# --- توابع کمکی برای مدیریت ریپلای ---55def extract_reply_info(update: Update, context: ContextTypes.DEFAULT_TYPE) -> tuple:56 """استخراج اطلاعات از ریپلای"""57 message = update.message58 59 # اگر ریپلای وجود نداشت60 if not message or not message.reply_to_message:61 return None, None, None, None62 63 replied_message = message.reply_to_message64 65 # اطلاعات پیام ریپلای شده66 replied_user_id = replied_message.from_user.id if replied_message.from_user else None67 replied_user_name = replied_message.from_user.first_name if replied_message.from_user else "Unknown"68 69 # استخراج متن پیام ریپلای شده70 replied_text = ""71 if replied_message.text:72 replied_text = replied_message.text73 elif replied_message.caption:74 replied_text = replied_message.caption75 76 # بررسی اینکه آیا ریپلای به ربات است77 is_reply_to_bot = False78 if replied_message.from_user:79 # بررسی اینکه آیا پیام از خود ربات است80 if replied_message.from_user.is_bot:81 is_reply_to_bot = True82 # یا آیا پیام حاوی منشن ربات است83 elif context.bot and context.bot.username and replied_message.text:84 if f"@{context.bot.username}" in replied_message.text:85 is_reply_to_bot = True86 87 return replied_user_id, replied_user_name, replied_text, is_reply_to_bot88 89def format_reply_message(user_message: str, replied_user_name: str, replied_text: str, is_reply_to_bot: bool = False, current_user_name: str = None) -> str:90 """فرمتدهی پیام با در نظر گرفتن ریپلای"""91 if not replied_text:92 return user_message93 94 # محدود کردن طول متن ریپلای شده95 if len(replied_text) > 100:96 replied_preview = replied_text[:97] + "..."97 else:98 replied_preview = replied_text99 100 # حذف منشن ربات از متن ریپلای شده اگر وجود دارد101 replied_preview = replied_preview.replace("@", "(at)") # جایگزین موقت برای جلوگیری از منشن102 103 if is_reply_to_bot:104 # ریپلای به ربات105 if current_user_name:106 return f"📎 {current_user_name} در پاسخ به ربات: «{replied_preview}»\n\n{user_message}"107 else:108 return f"📎 ریپلای به ربات: «{replied_preview}»\n\n{user_message}"109 else:110 # ریپلای به کاربر دیگر111 if current_user_name:112 return f"📎 {current_user_name} در پاسخ به {replied_user_name}: «{replied_preview}»\n\n{user_message}"113 else:114 return f"📎 ریپلای به {replied_user_name}: «{replied_preview}»\n\n{user_message}"115 116def create_reply_info_dict(replied_user_id, replied_user_name, replied_text, is_reply_to_bot):117 """ایجاد دیکشنری اطلاعات ریپلای"""118 if not replied_text:119 return None120 121 return {122 'replied_user_id': replied_user_id,123 'replied_user_name': replied_user_name,124 'replied_text': replied_text[:500], # محدود کردن طول125 'is_reply_to_bot': is_reply_to_bot,126 'timestamp': time.time()127 }128 129# --- توابع کمکی برای مدیریت وظایف ---130def _cleanup_task(task: asyncio.Task, task_dict: dict, task_id: int):131 if task_id in task_dict and task_dict[task_id] == task:132 del task_dict[task_id]133 logger.info(f"Cleaned up finished task for ID {task_id}.")134 try:135 exception = task.exception()136 if exception:137 logger.error(f"Background task for ID {task_id} failed: {exception}")138 except asyncio.CancelledError:139 logger.info(f"Task for ID {task_id} was cancelled.")140 except asyncio.InvalidStateError:141 pass142 143async def _process_user_request(update: Update, context: ContextTypes.DEFAULT_TYPE, custom_message: str = None, reply_info: dict = None):144 """پردازش درخواست کاربر در چت خصوصی"""145 chat_id = update.effective_chat.id146 user_message = custom_message if custom_message is not None else (update.message.text or update.message.caption or "")147 user_id = update.effective_user.id148 149 if not user_message:150 logger.warning(f"No message content for user {user_id}")151 return152 153 # اگر reply_info از بیرون داده نشده، استخراج کن154 if reply_info is None:155 replied_user_id, replied_user_name, replied_text, is_reply_to_bot = extract_reply_info(update, context)156 reply_info = create_reply_info_dict(replied_user_id, replied_user_name, replied_text, is_reply_to_bot)157 158 # اگر ریپلای وجود داشت، متن را فرمت کن159 if reply_info and reply_info.get('replied_text'):160 formatted_message = format_reply_message(161 user_message, 162 reply_info.get('replied_user_name', 'Unknown'),163 reply_info.get('replied_text'),164 reply_info.get('is_reply_to_bot', False)165 )166 else:167 formatted_message = user_message168 169 start_time = time.time()170 171 try:172 await context.bot.send_chat_action(chat_id=chat_id, action="typing")173 174 # دریافت context کاربر175 user_context = data_manager.get_context_for_api(user_id)176 177 # اضافه کردن پیام کاربر به context (با فرمت ریپلای اگر وجود داشت)178 data_manager.add_to_user_context_with_reply(user_id, "user", formatted_message, reply_info)179 180 # ایجاد لیست پیامها برای API181 messages = user_context.copy()182 messages.append({"role": "user", "content": formatted_message})183 184 logger.info(f"User {user_id} sending {len(messages)} messages to AI")185 if reply_info and reply_info.get('replied_text'):186 logger.info(f"Reply info: replied_to={reply_info.get('replied_user_name')}, is_to_bot={reply_info.get('is_reply_to_bot')}")187 188 response = await client.chat.completions.create(189 model="mlabonne/gemma-3-27b-it-abliterated:featherless-ai",190 messages=messages,191 temperature=0.7,192 top_p=0.95,193 stream=False,194 )195 196 end_time = time.time()197 response_time = end_time - start_time198 data_manager.update_response_stats(response_time)199 200 ai_response = response.choices[0].message.content201 202 # اضافه کردن پاسخ هوش مصنوعی به context203 data_manager.add_to_user_context(user_id, "assistant", ai_response)204 205 await update.message.reply_text(ai_response)206 data_manager.update_user_stats(user_id, update.effective_user)207 208 except httpx.TimeoutException:209 logger.warning(f"Request timed out for user {user_id}.")210 await update.message.reply_text("⏱️ ارتباط با سرور هوش مصنوعی طولانی شد. لطفاً دوباره تلاش کنید.")211 except Exception as e:212 logger.error(f"Error while processing message for user {user_id}: {e}")213 await update.message.reply_text("❌ متاسفانه در پردازش درخواست شما مشکلی پیش آمد. لطفاً دوباره تلاش کنید.")214 215async def _process_group_request(update: Update, context: ContextTypes.DEFAULT_TYPE, custom_message: str = None, reply_info: dict = None):216 """پردازش درخواست در گروه"""217 chat_id = update.effective_chat.id218 user_message = custom_message if custom_message is not None else (update.message.text or update.message.caption or "")219 user_id = update.effective_user.id220 user_name = update.effective_user.first_name221 222 if not user_message:223 logger.warning(f"No message content for group {chat_id}, user {user_id}")224 return225 226 # اگر reply_info از بیرون داده نشده، استخراج کن227 if reply_info is None:228 replied_user_id, replied_user_name, replied_text, is_reply_to_bot = extract_reply_info(update, context)229 reply_info = create_reply_info_dict(replied_user_id, replied_user_name, replied_text, is_reply_to_bot)230 231 # اگر ریپلای وجود داشت، متن را فرمت کن232 if reply_info and reply_info.get('replied_text'):233 formatted_message = format_reply_message(234 user_message,235 reply_info.get('replied_user_name', 'Unknown'),236 reply_info.get('replied_text'),237 reply_info.get('is_reply_to_bot', False),238 user_name239 )240 else:241 formatted_message = user_message242 243 start_time = time.time()244 245 try:246 await context.bot.send_chat_action(chat_id=chat_id, action="typing")247 248 # بررسی حالت context249 context_mode = data_manager.get_context_mode()250 251 if context_mode == 'group_shared':252 # حالت مشترک گروه: فقط از context گروه استفاده میشود253 messages = data_manager.get_context_for_api_group(chat_id)254 data_manager.add_to_group_context_with_reply(chat_id, "user", formatted_message, user_name, reply_info)255 256 elif context_mode == 'hybrid':257 # حالت ترکیبی: از هر دو context استفاده میشود258 messages = data_manager.get_hybrid_context_for_api(user_id, chat_id)259 data_manager.add_to_hybrid_context_with_reply(user_id, chat_id, "user", formatted_message, user_name, reply_info)260 261 else: # separate (پیشفرض)262 # حالت جداگانه: هر کاربر context خودش را دارد263 messages = data_manager.get_context_for_api(user_id)264 data_manager.add_to_user_context_with_reply(user_id, "user", formatted_message, reply_info)265 266 # اضافه کردن پیام جدید267 if context_mode == 'group_shared':268 messages.append({"role": "user", "content": f"{user_name}: {formatted_message}"})269 else:270 messages.append({"role": "user", "content": formatted_message})271 272 logger.info(f"Group {chat_id} ({context_mode} mode) sending {len(messages)} messages to AI")273 if reply_info and reply_info.get('replied_text'):274 logger.info(f"Reply info: replied_to={reply_info.get('replied_user_name')}, is_to_bot={reply_info.get('is_reply_to_bot')}")275 276 response = await client.chat.completions.create(277 model="mlabonne/gemma-3-27b-it-abliterated:featherless-ai",278 messages=messages,279 temperature=0.7,280 top_p=0.95,281 stream=False,282 )283 284 end_time = time.time()285 response_time = end_time - start_time286 data_manager.update_response_stats(response_time)287 288 ai_response = response.choices[0].message.content289 290 # ذخیره پاسخ بر اساس حالت context291 if context_mode == 'group_shared':292 data_manager.add_to_group_context(chat_id, "assistant", ai_response)293 elif context_mode == 'hybrid':294 # برای پاسخهای هوش مصنوعی، reply_info را None میکنیم295 data_manager.add_to_hybrid_context_with_reply(user_id, chat_id, "assistant", ai_response, None, None)296 else:297 data_manager.add_to_user_context(user_id, "assistant", ai_response)298 299 # بهروزرسانی آمار گروه با user_id300 data_manager.update_group_stats(chat_id, update.effective_chat, user_id)301 302 await update.message.reply_text(ai_response)303 304 except httpx.TimeoutException:305 logger.warning(f"Request timed out for group {chat_id}.")306 await update.message.reply_text("⏱️ ارتباط با سرور هوش مصنوعی طولانی شد. لطفاً دوباره تلاش کنید.")307 except Exception as e:308 logger.error(f"Error while processing message for group {chat_id}: {e}")309 await update.message.reply_text("❌ متاسفانه در پردازش درخواست شما مشکلی پیش آمد. لطفاً دوباره تلاش کنید.")310 311# --- هندلرهای اصلی ربات ---312async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:313 user = update.effective_user314 user_id = user.id315 chat = update.effective_chat316 317 if chat.type in ['group', 'supergroup']:318 # در گروه319 data_manager.update_group_stats(chat.id, chat, user_id)320 321 welcome_msg = data_manager.DATA.get('group_welcome_message', 322 "سلام به همه! 🤖\n\nمن یک ربات هوشمند هستم. برای استفاده از من در گروه:\n1. مستقیم با من چت کنید\n2. یا با منشن کردن من سوال بپرسید\n3. یا روی پیامها ریپلای کنید")323 324 context_mode = data_manager.get_context_mode()325 mode_info = ""326 if context_mode == 'group_shared':327 mode_info = "\n\n📝 **نکته:** در این گروه از context مشترک استفاده میشود. همه کاربران تاریخچه مکالمه یکسانی دارند (تا 16384 توکن)."328 elif context_mode == 'hybrid':329 mode_info = "\n\n📝 **نکته:** در این گروه از context ترکیبی استفاده میشود."330 331 await update.message.reply_html(332 welcome_msg + mode_info,333 disable_web_page_preview=True334 )335 else:336 # در چت خصوصی337 data_manager.update_user_stats(user_id, user)338 339 welcome_msg = data_manager.DATA.get('welcome_message', 340 "سلام {user_mention}! 🤖\n\nمن یک ربات هوشمند هستم. هر سوالی دارید بپرسید.\n\n💡 **نکته:** میتوانید روی پیامهای من ریپلای کنید تا من ارتباط را بهتر بفهمم.")341 342 await update.message.reply_html(343 welcome_msg.format(user_mention=user.mention_html()),344 disable_web_page_preview=True345 )346 347async def clear_chat(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:348 """پاک کردن تاریخچه چت"""349 user_id = update.effective_user.id350 chat = update.effective_chat351 352 if chat.type in ['group', 'supergroup']:353 # پاک کردن context گروه354 data_manager.clear_group_context(chat.id)355 356 context_mode = data_manager.get_context_mode()357 if context_mode == 'group_shared':358 await update.message.reply_text(359 "🧹 تاریخچه مکالمه گروه پاک شد.\n"360 "از این لحظه مکالمه جدیدی برای همه اعضای گروه شروع خواهد شد."361 )362 else:363 await update.message.reply_text(364 "🧹 تاریخچه مکالمه شخصی شما در این گروه پاک شد.\n"365 "از این لحظه مکالمه جدیدی شروع خواهد شد."366 )367 else:368 # پاک کردن context کاربر369 data_manager.clear_user_context(user_id)370 371 await update.message.reply_text(372 "🧹 تاریخچه مکالمه شما پاک شد.\n"373 "از این لحظه مکالمه جدیدی شروع خواهد شد."374 )375 376async def context_info(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:377 """نمایش اطلاعات context"""378 user_id = update.effective_user.id379 chat = update.effective_chat380 381 if chat.type in ['group', 'supergroup']:382 context_mode = data_manager.get_context_mode()383 384 if context_mode == 'group_shared':385 context_summary = data_manager.get_group_context_summary(chat.id)386 await update.message.reply_text(387 f"📊 **اطلاعات تاریخچه مکالمه گروه:**\n\n"388 f"{context_summary}\n\n"389 f"**حالت:** مشترک بین همه اعضای گروه\n"390 f"**سقف توکن:** 16384 توکن\n"391 f"برای پاک کردن تاریخچه از دستور /clear استفاده کنید."392 )393 elif context_mode == 'hybrid':394 group_summary = data_manager.get_group_context_summary(chat.id)395 user_summary = data_manager.get_context_summary(user_id)396 await update.message.reply_text(397 f"📊 **اطلاعات تاریخچه مکالمه (حالت ترکیبی):**\n\n"398 f"**گروه:** {group_summary}\n"399 f"**شخصی:** {user_summary}\n\n"400 f"برای پاک کردن تاریخچه از دستور /clear استفاده کنید."401 )402 else:403 context_summary = data_manager.get_context_summary(user_id)404 await update.message.reply_text(405 f"📊 **اطلاعات تاریخچه مکالمه شخصی شما در این گروه:**\n\n"406 f"{context_summary}\n\n"407 f"برای پاک کردن تاریخچه از دستور /clear استفاده کنید."408 )409 else:410 context_summary = data_manager.get_context_summary(user_id)411 412 await update.message.reply_text(413 f"📊 **اطلاعات تاریخچه مکالمه شما:**\n\n"414 f"{context_summary}\n\n"415 f"برای پاک کردن تاریخچه از دستور /clear استفاده کنید."416 )417 418async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:419 """نمایش دستورات کمک"""420 chat = update.effective_chat421 422 if chat.type in ['group', 'supergroup']:423 help_text = (424 "🤖 **دستورات ربات در گروه:**\n\n"425 "🟢 `/start` - شروع کار با ربات در گروه\n"426 "🟢 `/clear` - پاک کردن تاریخچه مکالمه\n"427 "🟢 `/context` - نمایش اطلاعات تاریخچه مکالمه\n"428 "🟢 `/help` - نمایش این پیام راهنما\n\n"429 "📝 **نکته:** ربات به سه صورت کار میکند:\n"430 "1. پاسخ به پیامهای مستقیم\n"431 "2. پاسخ وقتی منشن میشوید\n"432 "3. پاسخ به ریپلایها (روی پیامهای من یا دیگران)\n\n"433 f"**حالت فعلی:** {data_manager.get_context_mode()}\n"434 "• separate: هر کاربر تاریخچه جداگانه (8192 توکن)\n"435 "• group_shared: تاریخچه مشترک برای همه (16384 توکن)\n"436 "• hybrid: ترکیب هر دو\n\n"437 "💡 **توجه:** در حالت group_shared، ربات میتواند تا 16384 توکن از تاریخچه مکالمه گروه را به خاطر بسپارد."438 )439 else:440 help_text = (441 "🤖 **دستورات ربات:**\n\n"442 "🟢 `/start` - شروع کار با ربات\n"443 "🟢 `/clear` - پاک کردن تاریخچه مکالمه\n"444 "🟢 `/context` - نمایش اطلاعات تاریخچه مکالمه\n"445 "🟢 `/help` - نمایش این پیام راهنما\n\n"446 "📝 **نکته:** ربات تاریخچه مکالمه شما را تا ۲۰۴۸ توکن ذخیره میکند. "447 "میتوانید روی پیامهای من ریپلای کنید تا من ارتباط را بهتر بفهمم.\n"448 "برای شروع مکالمه جدید از دستور /clear استفاده کنید."449 )450 451 await update.message.reply_text(help_text, parse_mode='Markdown')452 453async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:454 # بررسی وجود پیام455 if not update.message:456 return457 458 user_id = update.effective_user.id459 chat = update.effective_chat460 461 # بررسی مسدود بودن کاربر462 if data_manager.is_user_banned(user_id):463 logger.info(f"Banned user {user_id} tried to send a message.")464 return465 466 # بررسی حالت نگهداری467 if data_manager.DATA.get('maintenance_mode', False) and user_id not in admin_panel.ADMIN_IDS:468 await update.message.reply_text("🔧 ربات در حال حاضر در حالت نگهداری قرار دارد. لطفاً بعداً تلاش کنید.")469 return470 471 # بررسی کلمات مسدود شده472 message_text = update.message.text or update.message.caption or ""473 if not message_text:474 return475 476 if data_manager.contains_blocked_words(message_text):477 logger.info(f"User {user_id} sent a message with a blocked word.")478 return479 480 if chat.type in ['group', 'supergroup']:481 # پردازش در گروه482 if chat.id in group_tasks and not group_tasks[chat.id].done():483 group_tasks[chat.id].cancel()484 logger.info(f"Cancelled previous task for group {chat.id} to start a new one.")485 486 task = asyncio.create_task(_process_group_request(update, context))487 group_tasks[chat.id] = task488 task.add_done_callback(lambda t: _cleanup_task(t, group_tasks, chat.id))489 else:490 # پردازش در چت خصوصی491 if user_id in user_tasks and not user_tasks[user_id].done():492 user_tasks[user_id].cancel()493 logger.info(f"Cancelled previous task for user {user_id} to start a new one.")494 495 task = asyncio.create_task(_process_user_request(update, context))496 user_tasks[user_id] = task497 task.add_done_callback(lambda t: _cleanup_task(t, user_tasks, user_id))498 499async def mention_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:500 """هندلر برای زمانی که ربات در گروه منشن میشود"""501 if not update.message:502 return503 504 if update.effective_chat.type in ['group', 'supergroup']:505 # استخراج اطلاعات ریپلای506 replied_user_id, replied_user_name, replied_text, is_reply_to_bot = extract_reply_info(update, context)507 reply_info = create_reply_info_dict(replied_user_id, replied_user_name, replied_text, is_reply_to_bot)508 509 # حذف منشن از متن510 message_text = update.message.text or ""511 bot_username = context.bot.username512 513 # حذف منشن ربات از متن514 if bot_username and f"@{bot_username}" in message_text:515 message_text = message_text.replace(f"@{bot_username}", "").strip()516 517 if message_text:518 # پردازش پیام مانند حالت عادی اما با متن فرمتشده519 user_name = update.effective_user.first_name520 521 # اگر ریپلای وجود داشت، متن را فرمت کن522 if reply_info and reply_info.get('replied_text'):523 formatted_message = format_reply_message(524 message_text,525 reply_info.get('replied_user_name', 'Unknown'),526 reply_info.get('replied_text'),527 reply_info.get('is_reply_to_bot', False),528 user_name529 )530 else:531 formatted_message = message_text532 533 # پردازش درخواست534 if update.effective_chat.id in group_tasks and not group_tasks[update.effective_chat.id].done():535 group_tasks[update.effective_chat.id].cancel()536 logger.info(f"Cancelled previous task for group {update.effective_chat.id} to start a new one.")537 538 task = asyncio.create_task(_process_group_request(update, context, formatted_message, reply_info))539 group_tasks[update.effective_chat.id] = task540 task.add_done_callback(lambda t: _cleanup_task(t, group_tasks, update.effective_chat.id))541 else:542 await update.message.reply_text("بله؟ چگونه میتوانم کمک کنم؟")543 544async def reply_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:545 """هندلر مخصوص ریپلایها"""546 # بررسی وجود پیام547 if not update.message:548 return549 550 # فقط ریپلایهایی که متن دارند551 message_text = update.message.text or update.message.caption or ""552 if not message_text:553 return554 555 user_id = update.effective_user.id556 chat = update.effective_chat557 558 # بررسی مسدود بودن کاربر559 if data_manager.is_user_banned(user_id):560 return561 562 # بررسی حالت نگهداری563 if data_manager.DATA.get('maintenance_mode', False) and user_id not in admin_panel.ADMIN_IDS:564 return565 566 # بررسی کلمات مسدود شده567 if data_manager.contains_blocked_words(message_text):568 return569 570 if chat.type in ['group', 'supergroup']:571 # پردازش در گروه572 if chat.id in group_tasks and not group_tasks[chat.id].done():573 group_tasks[chat.id].cancel()574 logger.info(f"Cancelled previous task for group {chat.id} to start a new one.")575 576 task = asyncio.create_task(_process_group_request(update, context))577 group_tasks[chat.id] = task578 task.add_done_callback(lambda t: _cleanup_task(t, group_tasks, chat.id))579 else:580 # پردازش در چت خصوصی581 if user_id in user_tasks and not user_tasks[user_id].done():582 user_tasks[user_id].cancel()583 logger.info(f"Cancelled previous task for user {user_id} to start a new one.")584 585 task = asyncio.create_task(_process_user_request(update, context))586 user_tasks[user_id] = task587 task.add_done_callback(lambda t: _cleanup_task(t, user_tasks, user_id))588 589def main() -> None:590 token = os.environ.get("BOT_TOKEN")591 if not token:592 logger.error("BOT_TOKEN not set in environment variables!")593 return594 595 application = (596 Application.builder()597 .token(token)598 .concurrent_updates(True)599 .build()600 )601 602 # هندلرهای کاربران603 application.add_handler(CommandHandler("start", start))604 application.add_handler(CommandHandler("clear", clear_chat))605 application.add_handler(CommandHandler("context", context_info))606 application.add_handler(CommandHandler("help", help_command))607 608 # هندلر برای ریپلایها609 application.add_handler(MessageHandler(610 filters.TEXT & filters.REPLY, 611 reply_handler612 ))613 614 # هندلر برای منشن در گروه615 application.add_handler(MessageHandler(616 filters.TEXT & filters.Entity("mention") & filters.ChatType.GROUPS, 617 mention_handler618 ))619 620 # هندلر برای پیامهای متنی معمولی (بدون ریپلای و بدون منشن)621 application.add_handler(MessageHandler(622 filters.TEXT & ~filters.COMMAND & filters.ChatType.PRIVATE & ~filters.REPLY, 623 handle_message624 ))625 626 # هندلر برای پیامهای متنی در گروه (بدون منشن و بدون ریپلای)627 application.add_handler(MessageHandler(628 filters.TEXT & ~filters.COMMAND & filters.ChatType.GROUPS & ~filters.Entity("mention") & ~filters.REPLY, 629 handle_message630 ))631 632 # راهاندازی و ثبت هندلرهای پنل ادمین633 admin_panel.setup_admin_handlers(application)634 635 # تنظیم هندلر خطا636 async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:637 logger.error(f"Exception while handling an update: {context.error}")638 try:639 if update and update.message:640 await update.message.reply_text("❌ خطایی در پردازش درخواست شما رخ داد.")641 except:642 pass643 644 application.add_error_handler(error_handler)645 646 port = int(os.environ.get("PORT", 8443))647 webhook_url = os.environ.get("RENDER_EXTERNAL_URL") + "/webhook"648 649 if webhook_url:650 application.run_webhook(651 listen="0.0.0.0",652 port=port,653 webhook_url=webhook_url,654 url_path="webhook"655 )656 else:657 application.run_polling()658 659if __name__ == "__main__":660 main()