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.6k
1# admin_panel.py2 3import os4import json5import logging6import csv7import io8import asyncio9from datetime import datetime, timedelta10from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup11from telegram.ext import ContextTypes, CommandHandler, CallbackQueryHandler12from telegram.error import TelegramError13 14# --- کتابخانههای جدید برای ویژگیهای اضافه شده ---15import matplotlib16matplotlib.use('Agg') # تنظیم برای استفاده در محیط بدون رابط کاربری گرافیکی17import matplotlib.pyplot as plt18import pandas as pd19import tempfile20import psutil21import platform22 23# --- تنظیمات ---24ADMIN_IDS = list(map(int, os.environ.get("ADMIN_IDS", "").split(','))) if os.environ.get("ADMIN_IDS") else []25 26# وارد کردن مدیر دادهها27import data_manager28 29logger = logging.getLogger(__name__)30 31# --- دکوراتور برای دسترسی ادمین ---32 33def admin_only(func):34 """این دکوراتور تضمین میکند که فقط ادمینها بتوانند دستور را اجرا کنند."""35 async def wrapped(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):36 if update.effective_user.id not in ADMIN_IDS:37 await update.message.reply_text("⛔️ شما دسترسی لازم برای اجرای این دستور را ندارید.")38 return39 return await func(update, context, *args, **kwargs)40 return wrapped41 42# --- هندلرهای دستورات ادمین ---43 44@admin_only45async def admin_commands(update: Update, context: ContextTypes.DEFAULT_TYPE):46 """نمایش تمام دستورات موجود ادمین."""47 commands_text = (48 "📋 **دستورات ادمین ربات:**\n\n"49 "📊 `/stats` - نمایش آمار ربات\n"50 "📢 `/broadcast [پیام]` - ارسال پیام به تمام کاربران\n"51 "🎯 `/targeted_broadcast [معیار] [مقدار] [پیام]` - ارسال پیام هدفمند\n"52 "📅 `/schedule_broadcast [YYYY-MM-DD] [HH:MM] [پیام]` - ارسال برنامهریزی شده\n"53 "📋 `/list_scheduled` - نمایش لیست ارسالهای برنامهریزی شده\n"54 "🗑️ `/remove_scheduled [شماره]` - حذف ارسال برنامهریزی شده\n"55 "🚫 `/ban [آیدی]` - مسدود کردن کاربر\n"56 "✅ `/unban [آیدی]` - رفع مسدودیت کاربر\n"57 "💌 `/direct_message [آیدی] [پیام]` - ارسال پیام مستقیم به کاربر\n"58 "ℹ️ `/user_info [آیدی]` - نمایش اطلاعات کاربر\n"59 "📝 `/logs` - نمایش آخرین لاگها\n"60 "📂 `/logs_file` - دانلود فایل کامل لاگها\n"61 "👥 `/users_list [صفحه]` - نمایش لیست کاربران\n"62 "🔍 `/user_search [نام]` - جستجوی کاربر بر اساس نام\n"63 "💾 `/backup` - ایجاد نسخه پشتیبان از دادهها\n"64 "📊 `/export_csv` - دانلود اطلاعات کاربران در فایل CSV\n"65 "🔧 `/maintenance [on/off]` - فعال/غیرفعال کردن حالت نگهداری\n"66 "👋 `/set_welcome [پیام]` - تنظیم پیام خوشامدگویی\n"67 "👋 `/set_goodbye [پیام]` - تنظیم پیام خداحافظی\n"68 "📈 `/activity_heatmap` - دریافت نمودار فعالیت کاربران\n"69 "⏱️ `/response_stats` - نمایش آمار زمان پاسخگویی\n"70 "🚫 `/add_blocked_word [کلمه]` - افزودن کلمه مسدود\n"71 "✅ `/remove_blocked_word [کلمه]` - حذف کلمه مسدود\n"72 "📜 `/list_blocked_words` - نمایش لیست کلمات مسدود\n"73 "💻 `/system_info` - نمایش اطلاعات سیستم\n"74 "🔄 `/reset_stats [messages/all]` - ریست کردن آمار\n"75 "🗑️ `/clear_context [آیدی]` - پاک کردن context کاربر\n"76 "📋 `/view_context [آیدی]` - مشاهده context کاربر\n"77 "📋 `/commands` - نمایش این لیست دستورات"78 )79 await update.message.reply_text(commands_text, parse_mode='Markdown')80 81@admin_only82async def admin_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):83 """آمار ربات را نمایش میدهد."""84 total_users = len(data_manager.DATA['users'])85 total_messages = data_manager.DATA['stats']['total_messages']86 banned_count = len(data_manager.DATA['banned_users'])87 88 now = datetime.now()89 active_24h = sum(1 for user in data_manager.DATA['users'].values() 90 if 'last_seen' in user and 91 datetime.strptime(user['last_seen'], '%Y-%m-%d %H:%M:%S') > now - timedelta(hours=24))92 93 active_7d = sum(1 for user in data_manager.DATA['users'].values() 94 if 'last_seen' in user and 95 datetime.strptime(user['last_seen'], '%Y-%m-%d %H:%M:%S') > now - timedelta(days=7))96 97 active_users = sorted(98 data_manager.DATA['users'].items(),99 key=lambda item: item[1].get('last_seen', ''),100 reverse=True101 )[:5]102 103 active_users_text = "\n".join(104 [f"• {user_id}: {info.get('first_name', 'N/A')} (آخرین فعالیت: {info.get('last_seen', 'N/A')})"105 for user_id, info in active_users]106 )107 108 # آمار context109 users_with_context = sum(1 for user in data_manager.DATA['users'].values() 110 if user.get('context') and len(user['context']) > 0)111 total_context_messages = sum(len(user.get('context', [])) for user in data_manager.DATA['users'].values())112 113 text = (114 f"📊 **آمار ربات**\n\n"115 f"👥 **تعداد کل کاربران:** `{total_users}`\n"116 f"📝 **تعداد کل پیامها:** `{total_messages}`\n"117 f"🚫 **کاربران مسدود شده:** `{banned_count}`\n"118 f"🟢 **کاربران فعال 24 ساعت گذشته:** `{active_24h}`\n"119 f"🟢 **کاربران فعال 7 روز گذشته:** `{active_7d}`\n"120 f"💭 **کاربران با context فعال:** `{users_with_context}`\n"121 f"💬 **کل پیامهای context:** `{total_context_messages}`\n\n"122 f"**۵ کاربر اخیر فعال:**\n{active_users_text}"123 )124 await update.message.reply_text(text, parse_mode='Markdown')125 126@admin_only127async def admin_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):128 """یک پیام را به تمام کاربران ارسال میکند."""129 if not context.args:130 await update.message.reply_text("⚠️ لطفاً پیامی برای ارسال بنویسید.\nمثال: `/broadcast سلام به همه!`")131 return132 133 message_text = " ".join(context.args)134 user_ids = list(data_manager.DATA['users'].keys())135 total_sent = 0136 total_failed = 0137 138 await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(user_ids)}` کاربر...")139 140 for user_id_str in user_ids:141 try:142 await context.bot.send_message(chat_id=int(user_id_str), text=message_text)143 total_sent += 1144 await asyncio.sleep(0.05)145 except TelegramError as e:146 logger.warning(f"Failed to send broadcast to {user_id_str}: {e}")147 total_failed += 1148 149 result_text = (150 f"✅ **ارسال همگانی تمام شد**\n\n"151 f"✅ موفق: `{total_sent}`\n"152 f"❌ ناموفق: `{total_failed}`"153 )154 await update.message.reply_text(result_text, parse_mode='Markdown')155 156@admin_only157async def admin_targeted_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):158 """ارسال پیام به گروه خاصی از کاربران بر اساس معیارهای مشخص."""159 if len(context.args) < 3:160 await update.message.reply_text("⚠️ فرمت صحیح: `/targeted_broadcast [معیار] [مقدار] [پیام]`\n"161 "معیارهای موجود: `active_days`, `message_count`, `banned`")162 return163 164 criteria = context.args[0].lower()165 value = context.args[1]166 message_text = " ".join(context.args[2:])167 168 target_users = []169 170 if criteria == "active_days":171 try:172 days = int(value)173 target_users = data_manager.get_active_users(days)174 except ValueError:175 await update.message.reply_text("⚠️ مقدار روز باید یک عدد صحیح باشد.")176 return177 178 elif criteria == "message_count":179 try:180 min_count = int(value)181 target_users = data_manager.get_users_by_message_count(min_count)182 except ValueError:183 await update.message.reply_text("⚠️ تعداد پیام باید یک عدد صحیح باشد.")184 return185 186 elif criteria == "banned":187 if value.lower() == "true":188 target_users = list(data_manager.DATA['banned_users'])189 elif value.lower() == "false":190 for user_id in data_manager.DATA['users']:191 if int(user_id) not in data_manager.DATA['banned_users']:192 target_users.append(int(user_id))193 else:194 await update.message.reply_text("⚠️ مقدار برای معیار banned باید true یا false باشد.")195 return196 197 else:198 await update.message.reply_text("⚠️ معیار نامعتبر است. معیارهای موجود: active_days, message_count, banned")199 return200 201 if not target_users:202 await update.message.reply_text("هیچ کاربری با معیارهای مشخص شده یافت نشد.")203 return204 205 await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(target_users)}` کاربر...")206 207 total_sent, total_failed = 0, 0208 for user_id in target_users:209 try:210 await context.bot.send_message(chat_id=user_id, text=message_text)211 total_sent += 1212 await asyncio.sleep(0.05)213 except TelegramError as e:214 logger.warning(f"Failed to send targeted broadcast to {user_id}: {e}")215 total_failed += 1216 217 result_text = f"✅ **ارسال هدفمند تمام شد**\n\n✅ موفق: `{total_sent}`\n❌ ناموفق: `{total_failed}`"218 await update.message.reply_text(result_text, parse_mode='Markdown')219 220@admin_only221async def admin_schedule_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):222 """تنظیم ارسال برنامهریزی شده پیام به همه کاربران."""223 if len(context.args) < 3:224 await update.message.reply_text("⚠️ فرمت صحیح: `/schedule_broadcast [YYYY-MM-DD] [HH:MM] [پیام]`")225 return226 227 try:228 date_str, time_str = context.args[0], context.args[1]229 message_text = " ".join(context.args[2:])230 231 scheduled_time = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')232 233 if scheduled_time <= datetime.now():234 await update.message.reply_text("⚠️ زمان برنامهریزی شده باید در آینده باشد.")235 return236 237 data_manager.DATA['scheduled_broadcasts'].append({238 'time': scheduled_time.strftime('%Y-%m-%d %H:%M:%S'),239 'message': message_text,240 'status': 'pending'241 })242 data_manager.save_data()243 244 await update.message.reply_text(f"✅ پیام برای زمان `{scheduled_time.strftime('%Y-%m-%d %H:%M')}` برنامهریزی شد.")245 246 except ValueError:247 await update.message.reply_text("⚠️ فرمت زمان نامعتبر است. لطفاً از فرمت YYYY-MM-DD HH:MM استفاده کنید.")248 249@admin_only250async def admin_list_scheduled_broadcasts(update: Update, context: ContextTypes.DEFAULT_TYPE):251 """نمایش لیست ارسالهای برنامهریزی شده."""252 if not data_manager.DATA['scheduled_broadcasts']:253 await update.message.reply_text("هیچ ارسال برنامهریزی شدهای وجود ندارد.")254 return255 256 broadcasts_text = "📅 **لیست ارسالهای برنامهریزی شده:**\n\n"257 for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts'], 1):258 status_emoji = "✅" if broadcast['status'] == 'sent' else "⏳"259 broadcasts_text += f"{i}. {status_emoji} `{broadcast['time']}` - {broadcast['message'][:50]}...\n"260 261 await update.message.reply_text(broadcasts_text, parse_mode='Markdown')262 263@admin_only264async def admin_remove_scheduled_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):265 """حذف یک ارسال برنامهریزی شده."""266 if not context.args or not context.args[0].isdigit():267 await update.message.reply_text("⚠️ لطفاً شماره ارسال برنامهریزی شده را وارد کنید.\nمثال: `/remove_scheduled 1`")268 return269 270 index = int(context.args[0]) - 1271 272 if not data_manager.DATA['scheduled_broadcasts'] or not (0 <= index < len(data_manager.DATA['scheduled_broadcasts'])):273 await update.message.reply_text("⚠️ شماره ارسال برنامهریزی شده نامعتبر است.")274 return275 276 removed_broadcast = data_manager.DATA['scheduled_broadcasts'].pop(index)277 data_manager.save_data()278 279 await update.message.reply_text(f"✅ ارسال برنامهریزی شده برای زمان `{removed_broadcast['time']}` حذف شد.")280 281@admin_only282async def admin_ban(update: Update, context: ContextTypes.DEFAULT_TYPE):283 """یک کاربر را با آیدی عددی مسدود کرده و به او اطلاع میدهد."""284 if not context.args or not context.args[0].isdigit():285 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/ban 123456789`")286 return287 288 user_id_to_ban = int(context.args[0])289 290 if user_id_to_ban in ADMIN_IDS:291 await update.message.reply_text("🛡️ شما نمیتوانید یک ادمین را مسدود کنید!")292 return293 294 if data_manager.is_user_banned(user_id_to_ban):295 await update.message.reply_text(f"کاربر `{user_id_to_ban}` از قبل مسدود شده است.")296 return297 298 data_manager.ban_user(user_id_to_ban)299 300 # ارسال پیام به کاربر مسدود شده301 try:302 await context.bot.send_message(303 chat_id=user_id_to_ban, 304 text="⛔️ شما توسط ادمین ربات مسدود شدید و دیگر نمیتوانید از خدمات ربات استفاده کنید."305 )306 except TelegramError as e:307 logger.warning(f"Could not send ban notification to user {user_id_to_ban}: {e}")308 309 await update.message.reply_text(f"✅ کاربر `{user_id_to_ban}` با موفقیت مسدود شد.", parse_mode='Markdown')310 311@admin_only312async def admin_unban(update: Update, context: ContextTypes.DEFAULT_TYPE):313 """مسدودیت یک کاربر را برمیدارد و به او اطلاع میدهد."""314 if not context.args or not context.args[0].isdigit():315 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/unban 123456789`")316 return317 318 user_id_to_unban = int(context.args[0])319 320 if not data_manager.is_user_banned(user_id_to_unban):321 await update.message.reply_text(f"کاربر `{user_id_to_unban}` در لیست مسدود شدهها وجود ندارد.")322 return323 324 data_manager.unban_user(user_id_to_unban)325 326 # ارسال پیام به کاربر برای رفع مسدودیت327 try:328 await context.bot.send_message(329 chat_id=user_id_to_unban, 330 text="✅ مسدودیت شما توسط ادمین ربات برداشته شد. میتوانید دوباره از ربات استفاده کنید."331 )332 except TelegramError as e:333 logger.warning(f"Could not send unban notification to user {user_id_to_unban}: {e}")334 335 await update.message.reply_text(f"✅ مسدودیت کاربر `{user_id_to_unban}` با موفقیت برداشته شد.", parse_mode='Markdown')336 337@admin_only338async def admin_direct_message(update: Update, context: ContextTypes.DEFAULT_TYPE):339 """ارسال پیام مستقیم به یک کاربر خاص."""340 if len(context.args) < 2:341 await update.message.reply_text("⚠️ فرمت صحیح: `/direct_message [آیدی] [پیام]`")342 return343 344 user_id_str = context.args[0]345 if not user_id_str.isdigit():346 await update.message.reply_text("⚠️ لطفاً یک آیدی عددی معتبر وارد کنید.")347 return348 349 message_text = " ".join(context.args[1:])350 user_id = int(user_id_str)351 352 try:353 await context.bot.send_message(chat_id=user_id, text=message_text)354 await update.message.reply_text(f"✅ پیام با موفقیت به کاربر `{user_id}` ارسال شد.", parse_mode='Markdown')355 except TelegramError as e:356 await update.message.reply_text(f"❌ خطا در ارسال پیام: {e}")357 358@admin_only359async def admin_userinfo(update: Update, context: ContextTypes.DEFAULT_TYPE):360 """اطلاعات یک کاربر خاص را نمایش میدهد."""361 if not context.args or not context.args[0].isdigit():362 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/user_info 123456789`")363 return364 365 user_id = int(context.args[0])366 user_info = data_manager.DATA['users'].get(str(user_id))367 368 if not user_info:369 await update.message.reply_text(f"کاربری با آیدی `{user_id}` در دیتابیس یافت نشد.")370 return371 372 is_banned = "بله" if data_manager.is_user_banned(user_id) else "خیر"373 374 if 'first_seen' in user_info and 'last_seen' in user_info:375 first_date = datetime.strptime(user_info['first_seen'], '%Y-%m-%d %H:%M:%S')376 last_date = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')377 days_active = max(1, (last_date - first_date).days)378 avg_messages = user_info.get('message_count', 0) / days_active379 else:380 avg_messages = user_info.get('message_count', 0)381 382 # اطلاعات context383 context_messages = len(user_info.get('context', []))384 context_tokens = data_manager.get_context_token_count(user_id)385 386 text = (387 f"ℹ️ **اطلاعات کاربر**\n\n"388 f"🆔 **آیدی:** `{user_id}`\n"389 f"👤 **نام:** {user_info.get('first_name', 'N/A')}\n"390 f"🔷 **نام کاربری:** @{user_info.get('username', 'N/A')}\n"391 f"📊 **تعداد پیامها:** `{user_info.get('message_count', 0)}`\n"392 f"📈 **میانگین پیام در روز:** `{avg_messages:.2f}`\n"393 f"📅 **اولین پیام:** {user_info.get('first_seen', 'N/A')}\n"394 f"🕒 **آخرین فعالیت:** {user_info.get('last_seen', 'N/A')}\n"395 f"💭 **پیامهای context:** `{context_messages}`\n"396 f"🔢 **توکنهای context:** `{context_tokens}`\n"397 f"🚫 **وضعیت مسدودیت:** {is_banned}"398 )399 await update.message.reply_text(text, parse_mode='Markdown')400 401@admin_only402async def admin_logs(update: Update, context: ContextTypes.DEFAULT_TYPE):403 """آخرین خطوط لاگ ربات را ارسال میکند."""404 try:405 with open(data_manager.LOG_FILE, "r", encoding="utf-8") as f:406 lines = f.readlines()407 last_lines = lines[-30:]408 log_text = "".join(last_lines)409 if not log_text:410 await update.message.reply_text("فایل لاگ خالی است.")411 return412 413 if len(log_text) > 4096:414 for i in range(0, len(log_text), 4096):415 await update.message.reply_text(f"```{log_text[i:i+4096]}```", parse_mode='Markdown')416 else:417 await update.message.reply_text(f"```{log_text}```", parse_mode='Markdown')418 419 except FileNotFoundError:420 await update.message.reply_text("فایل لاگ یافت نشد.")421 except Exception as e:422 await update.message.reply_text(f"خطایی در خواندن لاگ رخ داد: {e}")423 424@admin_only425async def admin_logs_file(update: Update, context: ContextTypes.DEFAULT_TYPE):426 """فایل کامل لاگ ربات را ارسال میکند."""427 try:428 await update.message.reply_document(429 document=open(data_manager.LOG_FILE, 'rb'),430 caption="📂 فایل کامل لاگهای ربات"431 )432 except FileNotFoundError:433 await update.message.reply_text("فایل لاگ یافت نشد.")434 except Exception as e:435 await update.message.reply_text(f"خطایی در ارسال فایل لاگ رخ داد: {e}")436 437@admin_only438async def admin_users_list(update: Update, context: ContextTypes.DEFAULT_TYPE):439 """نمایش لیست کامل کاربران با صفحهبندی."""440 users = data_manager.DATA['users']441 442 page = 1443 if context.args and context.args[0].isdigit():444 page = int(context.args[0])445 if page < 1: page = 1446 447 users_per_page = 20448 total_users = len(users)449 total_pages = (total_users + users_per_page - 1) // users_per_page450 451 if page > total_pages: page = total_pages452 453 start_idx = (page - 1) * users_per_page454 end_idx = min(start_idx + users_per_page, total_users)455 456 sorted_users = sorted(users.items(), key=lambda item: item[1].get('last_seen', ''), reverse=True)457 458 users_text = f"👥 **لیست کاربران (صفحه {page}/{total_pages})**\n\n"459 460 for i, (user_id, user_info) in enumerate(sorted_users[start_idx:end_idx], start=start_idx + 1):461 is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"462 username = user_info.get('username', 'N/A')463 first_name = user_info.get('first_name', 'N/A')464 last_seen = user_info.get('last_seen', 'N/A')465 message_count = user_info.get('message_count', 0)466 context_count = len(user_info.get('context', []))467 468 users_text += f"{i}. {is_banned} `{user_id}` - {first_name} (@{username})\n"469 users_text += f" 📝 پیامها: `{message_count}` | 💭 context: `{context_count}`\n"470 users_text += f" ⏰ آخرین فعالیت: `{last_seen}`\n\n"471 472 keyboard = []473 if page > 1: keyboard.append([InlineKeyboardButton("⬅️ صفحه قبل", callback_data=f"users_list:{page-1}")])474 if page < total_pages: keyboard.append([InlineKeyboardButton("➡️ صفحه بعد", callback_data=f"users_list:{page+1}")])475 476 reply_markup = InlineKeyboardMarkup(keyboard) if keyboard else None477 await update.message.reply_text(users_text, parse_mode='Markdown', reply_markup=reply_markup)478 479@admin_only480async def admin_user_search(update: Update, context: ContextTypes.DEFAULT_TYPE):481 """جستجوی کاربر بر اساس نام یا نام کاربری."""482 if not context.args:483 await update.message.reply_text("⚠️ لطفاً نام یا نام کاربری برای جستجو وارد کنید.\nمثال: `/user_search علی`")484 return485 486 search_term = " ".join(context.args).lower()487 users = data_manager.DATA['users']488 489 matching_users = []490 for user_id, user_info in users.items():491 # استفاده از (value or '') برای جلوگیری از خطا در صورت وجود None492 first_name = (user_info.get('first_name') or '').lower()493 username = (user_info.get('username') or '').lower()494 495 if search_term in first_name or search_term in username:496 is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"497 matching_users.append((user_id, user_info, is_banned))498 499 if not matching_users:500 await update.message.reply_text(f"هیچ کاربری با نام «{search_term}» یافت نشد.")501 return502 503 results_text = f"🔍 **نتایج جستجو برای «{search_term}»**\n\n"504 505 for user_id, user_info, is_banned in matching_users:506 username_display = user_info.get('username', 'N/A')507 first_name_display = user_info.get('first_name', 'N/A')508 last_seen = user_info.get('last_seen', 'N/A')509 message_count = user_info.get('message_count', 0)510 context_count = len(user_info.get('context', []))511 512 results_text += f"{is_banned} `{user_id}` - {first_name_display} (@{username_display})\n"513 results_text += f" 📝 پیامها: `{message_count}` | 💭 context: `{context_count}`\n"514 results_text += f" ⏰ آخرین فعالیت: `{last_seen}`\n\n"515 516 await update.message.reply_text(results_text, parse_mode='Markdown')517 518@admin_only519async def admin_backup(update: Update, context: ContextTypes.DEFAULT_TYPE):520 """ایجاد نسخه پشتیبان از دادههای ربات."""521 try:522 timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')523 backup_file = f"bot_backup_{timestamp}.json"524 525 data_to_backup = data_manager.DATA.copy()526 data_to_backup['banned_users'] = list(data_manager.DATA['banned_users'])527 528 with open(backup_file, 'w', encoding='utf-8') as f:529 json.dump(data_to_backup, f, indent=4, ensure_ascii=False)530 531 await update.message.reply_document(532 document=open(backup_file, 'rb'),533 caption=f"✅ نسخه پشتیبان با موفقیت ایجاد شد: {backup_file}"534 )535 536 logger.info(f"Backup created: {backup_file}")537 os.remove(backup_file) # حذف فایل پس از ارسال538 except Exception as e:539 await update.message.reply_text(f"❌ خطا در ایجاد نسخه پشتیبان: {e}")540 logger.error(f"Error creating backup: {e}")541 542@admin_only543async def admin_export_csv(update: Update, context: ContextTypes.DEFAULT_TYPE):544 """ایجاد و ارسال فایل CSV از اطلاعات کاربران."""545 users = data_manager.DATA['users']546 547 df_data = []548 for user_id, user_info in users.items():549 is_banned = "بله" if int(user_id) in data_manager.DATA['banned_users'] else "خیر"550 context_count = len(user_info.get('context', []))551 context_tokens = data_manager.get_context_token_count(int(user_id))552 553 df_data.append({554 'User ID': user_id,555 'First Name': user_info.get('first_name', 'N/A'),556 'Username': user_info.get('username', 'N/A'),557 'Message Count': user_info.get('message_count', 0),558 'Context Messages': context_count,559 'Context Tokens': context_tokens,560 'First Seen': user_info.get('first_seen', 'N/A'),561 'Last Seen': user_info.get('last_seen', 'N/A'),562 'Banned': is_banned563 })564 565 df = pd.DataFrame(df_data)566 567 with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False, encoding='utf-8') as f:568 df.to_csv(f.name, index=False)569 temp_file_path = f.name570 571 await update.message.reply_document(572 document=open(temp_file_path, 'rb'),573 caption="📊 فایل CSV اطلاعات کاربران"574 )575 576 os.unlink(temp_file_path)577 578@admin_only579async def admin_maintenance(update: Update, context: ContextTypes.DEFAULT_TYPE):580 """حالت نگهداری ربات را فعال یا غیرفعال کرده و به کاربران اطلاع میدهد."""581 if not context.args or context.args[0].lower() not in ['on', 'off']:582 await update.message.reply_text("⚠️ فرمت صحیح: `/maintenance on` یا `/maintenance off`")583 return584 585 status = context.args[0].lower()586 587 if status == 'on':588 if data_manager.DATA.get('maintenance_mode', False):589 await update.message.reply_text("🔧 ربات از قبل در حالت نگهداری قرار دارد.")590 return591 592 data_manager.DATA['maintenance_mode'] = True593 data_manager.save_data()594 595 await update.message.reply_text("✅ حالت نگهداری ربات فعال شد. در حال اطلاعرسانی به کاربران...")596 597 user_ids = list(data_manager.DATA['users'].keys())598 for user_id_str in user_ids:599 try:600 # به ادمینها پیام ارسال نشود601 if int(user_id_str) not in ADMIN_IDS:602 await context.bot.send_message(603 chat_id=int(user_id_str), 604 text="🔧 ربات در حال حاضر در حالت بهروزرسانی و نگهداری قرار دارد. لطفاً چند لحظه دیگر صبر کنید. از صبر شما سپاسگزاریم!"605 )606 await asyncio.sleep(0.05) # جلوگیری از محدودیت تلگرام607 except TelegramError:608 continue # نادیده گرفتن کاربرانی که ربات را مسدود کردهاند609 610 elif status == 'off':611 if not data_manager.DATA.get('maintenance_mode', False):612 await update.message.reply_text("✅ ربات از قبل در حالت عادی قرار دارد.")613 return614 615 data_manager.DATA['maintenance_mode'] = False616 data_manager.save_data()617 618 await update.message.reply_text("✅ حالت نگهداری ربات غیرفعال شد. در حال اطلاعرسانی به کاربران...")619 620 user_ids = list(data_manager.DATA['users'].keys())621 for user_id_str in user_ids:622 try:623 if int(user_id_str) not in ADMIN_IDS:624 await context.bot.send_message(625 chat_id=int(user_id_str), 626 text="✅ بهروزرسانی ربات به پایان رسید. از صبر شما سپاسگزاریم! میتوانید دوباره از ربات استفاده کنید."627 )628 await asyncio.sleep(0.05)629 except TelegramError:630 continue631 632@admin_only633async def admin_set_welcome_message(update: Update, context: ContextTypes.DEFAULT_TYPE):634 """تنظیم پیام خوشامدگویی جدید."""635 if not context.args:636 await update.message.reply_text("⚠️ لطفاً پیام خوشامدگویی جدید را وارد کنید.\n"637 "مثال: `/set_welcome سلام {user_mention}! به ربات خوش آمدید.`")638 return639 640 new_message = " ".join(context.args)641 data_manager.DATA['welcome_message'] = new_message642 data_manager.save_data()643 644 await update.message.reply_text("✅ پیام خوشامدگویی با موفقیت بهروزرسانی شد.")645 646@admin_only647async def admin_set_goodbye_message(update: Update, context: ContextTypes.DEFAULT_TYPE):648 """تنظیم پیام خداحافظی جدید."""649 if not context.args:650 await update.message.reply_text("⚠️ لطفاً پیام خداحافظی جدید را وارد کنید.\n"651 "مثال: `/set_goodbye {user_mention}، خداحافظ!`")652 return653 654 new_message = " ".join(context.args)655 data_manager.DATA['goodbye_message'] = new_message656 data_manager.save_data()657 658 await update.message.reply_text("✅ پیام خداحافظی با موفقیت بهروزرسانی شد.")659 660@admin_only661async def admin_activity_heatmap(update: Update, context: ContextTypes.DEFAULT_TYPE):662 """ایجاد و ارسال نمودار فعالیت کاربران."""663 users = data_manager.DATA['users']664 activity_hours = [0] * 24665 666 for user_info in users.values():667 if 'last_seen' in user_info:668 try:669 last_seen = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')670 activity_hours[last_seen.hour] += 1671 except ValueError:672 continue673 674 plt.figure(figsize=(12, 6))675 plt.bar(range(24), activity_hours, color='skyblue')676 plt.title('نمودار فعالیت کاربران بر اساس ساعت')677 plt.xlabel('ساعت')678 plt.ylabel('تعداد کاربران فعال')679 plt.xticks(range(24))680 plt.grid(axis='y', alpha=0.3)681 682 with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:683 plt.savefig(f.name, bbox_inches='tight')684 temp_file_path = f.name685 686 plt.close()687 688 await update.message.reply_photo(689 photo=open(temp_file_path, 'rb'),690 caption="📊 نمودار فعالیت کاربران بر اساس ساعت"691 )692 693 os.unlink(temp_file_path)694 695@admin_only696async def admin_response_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):697 """نمایش آمار زمان پاسخگویی ربات."""698 stats = data_manager.DATA['stats']699 await update.message.reply_text(700 "📈 **آمار زمان پاسخگویی ربات**\n\n"701 f"🟢 میانگین زمان پاسخگویی: `{stats.get('avg_response_time', 0):.2f}` ثانیه\n"702 f"🔴 بیشترین زمان پاسخگویی: `{stats.get('max_response_time', 0):.2f}` ثانیه\n"703 f"🟢 کمترین زمان پاسخگویی: `{stats.get('min_response_time', 0):.2f}` ثانیه\n"704 f"📊 کل پاسخهای ثبت شده: `{stats.get('total_responses', 0)}`"705 )706 707@admin_only708async def admin_add_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):709 """افزودن کلمه یا عبارت به لیست کلمات مسدود شده."""710 if not context.args:711 await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"712 "مثال: `/add_blocked_word کلمه_نامناسب`")713 return714 715 word = " ".join(context.args).lower()716 717 if word in data_manager.DATA['blocked_words']:718 await update.message.reply_text(f"⚠️ کلمه «{word}» از قبل در لیست کلمات مسدود شده وجود دارد.")719 return720 721 data_manager.DATA['blocked_words'].append(word)722 data_manager.save_data()723 724 await update.message.reply_text(f"✅ کلمه «{word}» به لیست کلمات مسدود شده اضافه شد.")725 726@admin_only727async def admin_remove_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):728 """حذف کلمه یا عبارت از لیست کلمات مسدود شده."""729 if not context.args:730 await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"731 "مثال: `/remove_blocked_word کلمه_نامناسب`")732 return733 734 word = " ".join(context.args).lower()735 736 if word not in data_manager.DATA['blocked_words']:737 await update.message.reply_text(f"⚠️ کلمه «{word}» در لیست کلمات مسدود شده وجود ندارد.")738 return739 740 data_manager.DATA['blocked_words'].remove(word)741 data_manager.save_data()742 743 await update.message.reply_text(f"✅ کلمه «{word}» از لیست کلمات مسدود شده حذف شد.")744 745@admin_only746async def admin_list_blocked_words(update: Update, context: ContextTypes.DEFAULT_TYPE):747 """نمایش لیست کلمات مسدود شده."""748 if not data_manager.DATA['blocked_words']:749 await update.message.reply_text("هیچ کلمه مسدود شدهای در لیست وجود ندارد.")750 return751 752 words_list = "\n".join([f"• {word}" for word in data_manager.DATA['blocked_words']])753 await update.message.reply_text(f"🚫 **لیست کلمات مسدود شده:**\n\n{words_list}")754 755@admin_only756async def admin_system_info(update: Update, context: ContextTypes.DEFAULT_TYPE):757 """نمایش اطلاعات سیستم و منابع."""758 bot_start_time_str = data_manager.DATA.get('bot_start_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))759 bot_start_time = datetime.strptime(bot_start_time_str, '%Y-%m-%d %H:%M:%S')760 uptime = datetime.now() - bot_start_time761 762 # محاسبه آمار context763 total_context_messages = sum(len(user.get('context', [])) for user in data_manager.DATA['users'].values())764 users_with_context = sum(1 for user in data_manager.DATA['users'].values() if user.get('context'))765 766 system_info = (767 f"💻 **اطلاعات سیستم:**\n\n"768 f"🖥️ سیستمعامل: {platform.system()} {platform.release()}\n"769 f"🐍 نسخه پایتون: {platform.python_version()}\n"770 f"💾 حافظه RAM استفاده شده: {psutil.virtual_memory().percent}%\n"771 f"💾 حافظه RAM آزاد: {psutil.virtual_memory().available / (1024**3):.2f} GB\n"772 f"💾 فضای دیسک استفاده شده: {psutil.disk_usage('/').percent}%\n"773 f"💾 فضای دیسک آزاد: {psutil.disk_usage('/').free / (1024**3):.2f} GB\n"774 f"⏱️ زمان اجرای ربات: {uptime}\n"775 f"💭 کاربران با context: {users_with_context}\n"776 f"💬 کل پیامهای context: {total_context_messages}"777 )778 779 await update.message.reply_text(system_info, parse_mode='Markdown')780 781@admin_only782async def admin_reset_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):783 """ریست کردن آمار ربات."""784 if not context.args:785 await update.message.reply_text("⚠️ لطفاً نوع آماری که میخواهید ریست کنید را مشخص کنید.\n"786 "مثال: `/reset_stats messages` یا `/reset_stats all`")787 return788 789 stat_type = context.args[0].lower()790 791 if stat_type == "messages":792 data_manager.DATA['stats']['total_messages'] = 0793 for user_id in data_manager.DATA['users']:794 data_manager.DATA['users'][user_id]['message_count'] = 0795 await update.message.reply_text("✅ آمار پیامها با موفقیت ریست شد.")796 797 elif stat_type == "all":798 data_manager.DATA['stats'] = {799 'total_messages': 0,800 'total_users': len(data_manager.DATA['users']),801 'avg_response_time': 0,802 'max_response_time': 0,803 'min_response_time': 0,804 'total_responses': 0805 }806 for user_id in data_manager.DATA['users']:807 data_manager.DATA['users'][user_id]['message_count'] = 0808 await update.message.reply_text("✅ تمام آمارها با موفقیت ریست شد.")809 810 else:811 await update.message.reply_text("⚠️ نوع آمار نامعتبر است. گزینههای موجود: messages, all")812 return813 814 data_manager.save_data()815 816# --- دستورات جدید برای مدیریت context ---817 818@admin_only819async def admin_clear_context(update: Update, context: ContextTypes.DEFAULT_TYPE):820 """پاک کردن context یک کاربر"""821 if not context.args or not context.args[0].isdigit():822 await update.message.reply_text("⚠️ لطفاً آیدی کاربر را وارد کنید.\nمثال: `/clear_context 123456789`")823 return824 825 user_id = int(context.args[0])826 data_manager.clear_user_context(user_id)827 828 await update.message.reply_text(f"✅ context کاربر `{user_id}` با موفقیت پاک شد.")829 830@admin_only831async def admin_view_context(update: Update, context: ContextTypes.DEFAULT_TYPE):832 """مشاهده context یک کاربر"""833 if not context.args or not context.args[0].isdigit():834 await update.message.reply_text("⚠️ لطفاً آیدی کاربر را وارد کنید.\nمثال: `/view_context 123456789`")835 return836 837 user_id = int(context.args[0])838 user_context = data_manager.get_user_context(user_id)839 840 if not user_context:841 await update.message.reply_text(f"کاربر `{user_id}` context ندارد.")842 return843 844 context_text = f"📋 **Context کاربر `{user_id}`**\n\n"845 total_tokens = 0846 847 for i, msg in enumerate(user_context, 1):848 role_emoji = "👤" if msg['role'] == 'user' else "🤖"849 tokens = data_manager.count_tokens(msg['content'])850 total_tokens += tokens851 852 # کوتاه کردن متن طولانی853 content_preview = msg['content']854 if len(content_preview) > 100:855 content_preview = content_preview[:100] + "..."856 857 context_text += f"{i}. {role_emoji} **{msg['role']}** ({tokens} توکن)\n"858 context_text += f" ⏰ {msg.get('timestamp', 'N/A')}\n"859 context_text += f" 📝 {content_preview}\n\n"860 861 context_text += f"📊 **مجموع:** {len(user_context)} پیام، {total_tokens} توکن"862 863 await update.message.reply_text(context_text, parse_mode='Markdown')864 865# --- هندلر برای دکمههای صفحهبندی ---866async def users_list_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):867 """پردازش دکمههای صفحهبندی لیست کاربران."""868 query = update.callback_query869 await query.answer()870 871 if query.data.startswith("users_list:"):872 page = int(query.data.split(":")[1])873 context.args = [str(page)]874 await admin_users_list(update, context)875 876# --- تابع برای پردازش ارسالهای برنامهریزی شده ---877async def process_scheduled_broadcasts(context: ContextTypes.DEFAULT_TYPE):878 """پردازش ارسالهای برنامهریزی شده و ارسال پیامها در زمان مقرر."""879 now = datetime.now()880 broadcasts_to_send_indices = []881 882 for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts']):883 if broadcast['status'] == 'pending':884 broadcast_time = datetime.strptime(broadcast['time'], '%Y-%m-%d %H:%M:%S')885 if broadcast_time <= now:886 broadcasts_to_send_indices.append(i)887 888 if not broadcasts_to_send_indices:889 return890 891 user_ids = list(data_manager.DATA['users'].keys())892 893 for index in broadcasts_to_send_indices:894 broadcast = data_manager.DATA['scheduled_broadcasts'][index]895 message_text = broadcast['message']896 total_sent, total_failed = 0, 0897 898 for user_id_str in user_ids:899 try:900 await context.bot.send_message(chat_id=int(user_id_str), text=message_text)901 total_sent += 1902 await asyncio.sleep(0.05)903 except TelegramError as e:904 logger.warning(f"Failed to send scheduled broadcast to {user_id_str}: {e}")905 total_failed += 1906 907 # بهروزرسانی وضعیت ارسال908 data_manager.DATA['scheduled_broadcasts'][index]['status'] = 'sent'909 data_manager.DATA['scheduled_broadcasts'][index]['sent_time'] = now.strftime('%Y-%m-%d %H:%M:%S')910 data_manager.DATA['scheduled_broadcasts'][index]['sent_count'] = total_sent911 data_manager.DATA['scheduled_broadcasts'][index]['failed_count'] = total_failed912 913 logger.info(f"Scheduled broadcast sent: {total_sent} successful, {total_failed} failed")914 915 data_manager.save_data()916 917# --- تابع راهاندازی هندلرها ---918def setup_admin_handlers(application):919 """هندلرهای پنل ادمین را به اپلیکیشن اضافه میکند."""920 # هندلرهای اصلی921 application.add_handler(CommandHandler("commands", admin_commands))922 application.add_handler(CommandHandler("stats", admin_stats))923 application.add_handler(CommandHandler("broadcast", admin_broadcast))924 application.add_handler(CommandHandler("ban", admin_ban))925 application.add_handler(CommandHandler("unban", admin_unban))926 application.add_handler(CommandHandler("user_info", admin_userinfo))927 application.add_handler(CommandHandler("logs", admin_logs))928 application.add_handler(CommandHandler("logs_file", admin_logs_file))929 application.add_handler(CommandHandler("users_list", admin_users_list))930 application.add_handler(CommandHandler("user_search", admin_user_search))931 application.add_handler(CommandHandler("backup", admin_backup))932 933 # هندلرهای جدید934 application.add_handler(CommandHandler("targeted_broadcast", admin_targeted_broadcast))935 application.add_handler(CommandHandler("schedule_broadcast", admin_schedule_broadcast))936 application.add_handler(CommandHandler("list_scheduled", admin_list_scheduled_broadcasts))937 application.add_handler(CommandHandler("remove_scheduled", admin_remove_scheduled_broadcast))938 application.add_handler(CommandHandler("direct_message", admin_direct_message))939 application.add_handler(CommandHandler("export_csv", admin_export_csv))940 application.add_handler(CommandHandler("maintenance", admin_maintenance))941 application.add_handler(CommandHandler("set_welcome", admin_set_welcome_message))942 application.add_handler(CommandHandler("set_goodbye", admin_set_goodbye_message))943 application.add_handler(CommandHandler("activity_heatmap", admin_activity_heatmap))944 application.add_handler(CommandHandler("response_stats", admin_response_stats))945 application.add_handler(CommandHandler("add_blocked_word", admin_add_blocked_word))946 application.add_handler(CommandHandler("remove_blocked_word", admin_remove_blocked_word))947 application.add_handler(CommandHandler("list_blocked_words", admin_list_blocked_words))948 application.add_handler(CommandHandler("system_info", admin_system_info))949 application.add_handler(CommandHandler("reset_stats", admin_reset_stats))950 951 # هندلرهای مدیریت context952 application.add_handler(CommandHandler("clear_context", admin_clear_context))953 application.add_handler(CommandHandler("view_context", admin_view_context))954 955 # هندلر برای دکمههای صفحهبندی956 application.add_handler(CallbackQueryHandler(users_list_callback, pattern="^users_list:"))957 958 # شروع وظیفه دورهای برای بررسی ارسالهای برنامهریزی شده959 application.job_queue.run_repeating(process_scheduled_broadcasts, interval=60, first=0)960 961 logger.info("Admin panel handlers have been set up.")