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 "📋 `/commands` - نمایش این لیست دستورات"76 )77 await update.message.reply_text(commands_text, parse_mode='Markdown')78 79@admin_only80async def admin_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):81 """آمار ربات را نمایش میدهد."""82 total_users = len(data_manager.DATA['users'])83 total_messages = data_manager.DATA['stats']['total_messages']84 banned_count = len(data_manager.DATA['banned_users'])85 86 now = datetime.now()87 active_24h = sum(1 for user in data_manager.DATA['users'].values() 88 if 'last_seen' in user and 89 datetime.strptime(user['last_seen'], '%Y-%m-%d %H:%M:%S') > now - timedelta(hours=24))90 91 active_7d = sum(1 for user in data_manager.DATA['users'].values() 92 if 'last_seen' in user and 93 datetime.strptime(user['last_seen'], '%Y-%m-%d %H:%M:%S') > now - timedelta(days=7))94 95 active_users = sorted(96 data_manager.DATA['users'].items(),97 key=lambda item: item[1].get('last_seen', ''),98 reverse=True99 )[:5]100 101 active_users_text = "\n".join(102 [f"• {user_id}: {info.get('first_name', 'N/A')} (آخرین فعالیت: {info.get('last_seen', 'N/A')})"103 for user_id, info in active_users]104 )105 106 text = (107 f"📊 **آمار ربات**\n\n"108 f"👥 **تعداد کل کاربران:** `{total_users}`\n"109 f"📝 **تعداد کل پیامها:** `{total_messages}`\n"110 f"🚫 **کاربران مسدود شده:** `{banned_count}`\n"111 f"🟢 **کاربران فعال 24 ساعت گذشته:** `{active_24h}`\n"112 f"🟢 **کاربران فعال 7 روز گذشته:** `{active_7d}`\n\n"113 f"**۵ کاربر اخیر فعال:**\n{active_users_text}"114 )115 await update.message.reply_text(text, parse_mode='Markdown')116 117@admin_only118async def admin_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):119 """یک پیام را به تمام کاربران ارسال میکند."""120 if not context.args:121 await update.message.reply_text("⚠️ لطفاً پیامی برای ارسال بنویسید.\nمثال: `/broadcast سلام به همه!`")122 return123 124 message_text = " ".join(context.args)125 user_ids = list(data_manager.DATA['users'].keys())126 total_sent = 0127 total_failed = 0128 129 await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(user_ids)}` کاربر...")130 131 for user_id_str in user_ids:132 try:133 await context.bot.send_message(chat_id=int(user_id_str), text=message_text)134 total_sent += 1135 await asyncio.sleep(0.05)136 except TelegramError as e:137 logger.warning(f"Failed to send broadcast to {user_id_str}: {e}")138 total_failed += 1139 140 result_text = (141 f"✅ **ارسال همگانی تمام شد**\n\n"142 f"✅ موفق: `{total_sent}`\n"143 f"❌ ناموفق: `{total_failed}`"144 )145 await update.message.reply_text(result_text, parse_mode='Markdown')146 147@admin_only148async def admin_targeted_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):149 """ارسال پیام به گروه خاصی از کاربران بر اساس معیارهای مشخص."""150 if len(context.args) < 3:151 await update.message.reply_text("⚠️ فرمت صحیح: `/targeted_broadcast [معیار] [مقدار] [پیام]`\n"152 "معیارهای موجود: `active_days`, `message_count`, `banned`")153 return154 155 criteria = context.args[0].lower()156 value = context.args[1]157 message_text = " ".join(context.args[2:])158 159 target_users = []160 161 if criteria == "active_days":162 try:163 days = int(value)164 target_users = data_manager.get_active_users(days)165 except ValueError:166 await update.message.reply_text("⚠️ مقدار روز باید یک عدد صحیح باشد.")167 return168 169 elif criteria == "message_count":170 try:171 min_count = int(value)172 target_users = data_manager.get_users_by_message_count(min_count)173 except ValueError:174 await update.message.reply_text("⚠️ تعداد پیام باید یک عدد صحیح باشد.")175 return176 177 elif criteria == "banned":178 if value.lower() == "true":179 target_users = list(data_manager.DATA['banned_users'])180 elif value.lower() == "false":181 for user_id in data_manager.DATA['users']:182 if int(user_id) not in data_manager.DATA['banned_users']:183 target_users.append(int(user_id))184 else:185 await update.message.reply_text("⚠️ مقدار برای معیار banned باید true یا false باشد.")186 return187 188 else:189 await update.message.reply_text("⚠️ معیار نامعتبر است. معیارهای موجود: active_days, message_count, banned")190 return191 192 if not target_users:193 await update.message.reply_text("هیچ کاربری با معیارهای مشخص شده یافت نشد.")194 return195 196 await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(target_users)}` کاربر...")197 198 total_sent, total_failed = 0, 0199 for user_id in target_users:200 try:201 await context.bot.send_message(chat_id=user_id, text=message_text)202 total_sent += 1203 await asyncio.sleep(0.05)204 except TelegramError as e:205 logger.warning(f"Failed to send targeted broadcast to {user_id}: {e}")206 total_failed += 1207 208 result_text = f"✅ **ارسال هدفمند تمام شد**\n\n✅ موفق: `{total_sent}`\n❌ ناموفق: `{total_failed}`"209 await update.message.reply_text(result_text, parse_mode='Markdown')210 211@admin_only212async def admin_schedule_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):213 """تنظیم ارسال برنامهریزی شده پیام به همه کاربران."""214 if len(context.args) < 3:215 await update.message.reply_text("⚠️ فرمت صحیح: `/schedule_broadcast [YYYY-MM-DD] [HH:MM] [پیام]`")216 return217 218 try:219 date_str, time_str = context.args[0], context.args[1]220 message_text = " ".join(context.args[2:])221 222 scheduled_time = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')223 224 if scheduled_time <= datetime.now():225 await update.message.reply_text("⚠️ زمان برنامهریزی شده باید در آینده باشد.")226 return227 228 data_manager.DATA['scheduled_broadcasts'].append({229 'time': scheduled_time.strftime('%Y-%m-%d %H:%M:%S'),230 'message': message_text,231 'status': 'pending'232 })233 data_manager.save_data()234 235 await update.message.reply_text(f"✅ پیام برای زمان `{scheduled_time.strftime('%Y-%m-%d %H:%M')}` برنامهریزی شد.")236 237 except ValueError:238 await update.message.reply_text("⚠️ فرمت زمان نامعتبر است. لطفاً از فرمت YYYY-MM-DD HH:MM استفاده کنید.")239 240@admin_only241async def admin_list_scheduled_broadcasts(update: Update, context: ContextTypes.DEFAULT_TYPE):242 """نمایش لیست ارسالهای برنامهریزی شده."""243 if not data_manager.DATA['scheduled_broadcasts']:244 await update.message.reply_text("هیچ ارسال برنامهریزی شدهای وجود ندارد.")245 return246 247 broadcasts_text = "📅 **لیست ارسالهای برنامهریزی شده:**\n\n"248 for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts'], 1):249 status_emoji = "✅" if broadcast['status'] == 'sent' else "⏳"250 broadcasts_text += f"{i}. {status_emoji} `{broadcast['time']}` - {broadcast['message'][:50]}...\n"251 252 await update.message.reply_text(broadcasts_text, parse_mode='Markdown')253 254@admin_only255async def admin_remove_scheduled_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):256 """حذف یک ارسال برنامهریزی شده."""257 if not context.args or not context.args[0].isdigit():258 await update.message.reply_text("⚠️ لطفاً شماره ارسال برنامهریزی شده را وارد کنید.\nمثال: `/remove_scheduled 1`")259 return260 261 index = int(context.args[0]) - 1262 263 if not data_manager.DATA['scheduled_broadcasts'] or not (0 <= index < len(data_manager.DATA['scheduled_broadcasts'])):264 await update.message.reply_text("⚠️ شماره ارسال برنامهریزی شده نامعتبر است.")265 return266 267 removed_broadcast = data_manager.DATA['scheduled_broadcasts'].pop(index)268 data_manager.save_data()269 270 await update.message.reply_text(f"✅ ارسال برنامهریزی شده برای زمان `{removed_broadcast['time']}` حذف شد.")271 272@admin_only273async def admin_ban(update: Update, context: ContextTypes.DEFAULT_TYPE):274 """یک کاربر را با آیدی عددی مسدود کرده و به او اطلاع میدهد."""275 if not context.args or not context.args[0].isdigit():276 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/ban 123456789`")277 return278 279 user_id_to_ban = int(context.args[0])280 281 if user_id_to_ban in ADMIN_IDS:282 await update.message.reply_text("🛡️ شما نمیتوانید یک ادمین را مسدود کنید!")283 return284 285 if data_manager.is_user_banned(user_id_to_ban):286 await update.message.reply_text(f"کاربر `{user_id_to_ban}` از قبل مسدود شده است.")287 return288 289 data_manager.ban_user(user_id_to_ban)290 291 # ارسال پیام به کاربر مسدود شده292 try:293 await context.bot.send_message(294 chat_id=user_id_to_ban, 295 text="⛔️ شما توسط ادمین ربات مسدود شدید و دیگر نمیتوانید از خدمات ربات استفاده کنید."296 )297 except TelegramError as e:298 logger.warning(f"Could not send ban notification to user {user_id_to_ban}: {e}")299 300 await update.message.reply_text(f"✅ کاربر `{user_id_to_ban}` با موفقیت مسدود شد.", parse_mode='Markdown')301 302@admin_only303async def admin_unban(update: Update, context: ContextTypes.DEFAULT_TYPE):304 """مسدودیت یک کاربر را برمیدارد و به او اطلاع میدهد."""305 if not context.args or not context.args[0].isdigit():306 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/unban 123456789`")307 return308 309 user_id_to_unban = int(context.args[0])310 311 if not data_manager.is_user_banned(user_id_to_unban):312 await update.message.reply_text(f"کاربر `{user_id_to_unban}` در لیست مسدود شدهها وجود ندارد.")313 return314 315 data_manager.unban_user(user_id_to_unban)316 317 # ارسال پیام به کاربر برای رفع مسدودیت318 try:319 await context.bot.send_message(320 chat_id=user_id_to_unban, 321 text="✅ مسدودیت شما توسط ادمین ربات برداشته شد. میتوانید دوباره از ربات استفاده کنید."322 )323 except TelegramError as e:324 logger.warning(f"Could not send unban notification to user {user_id_to_unban}: {e}")325 326 await update.message.reply_text(f"✅ مسدودیت کاربر `{user_id_to_unban}` با موفقیت برداشته شد.", parse_mode='Markdown')327 328@admin_only329async def admin_direct_message(update: Update, context: ContextTypes.DEFAULT_TYPE):330 """ارسال پیام مستقیم به یک کاربر خاص."""331 if len(context.args) < 2:332 await update.message.reply_text("⚠️ فرمت صحیح: `/direct_message [آیدی] [پیام]`")333 return334 335 user_id_str = context.args[0]336 if not user_id_str.isdigit():337 await update.message.reply_text("⚠️ لطفاً یک آیدی عددی معتبر وارد کنید.")338 return339 340 message_text = " ".join(context.args[1:])341 user_id = int(user_id_str)342 343 try:344 await context.bot.send_message(chat_id=user_id, text=message_text)345 await update.message.reply_text(f"✅ پیام با موفقیت به کاربر `{user_id}` ارسال شد.", parse_mode='Markdown')346 except TelegramError as e:347 await update.message.reply_text(f"❌ خطا در ارسال پیام: {e}")348 349@admin_only350async def admin_userinfo(update: Update, context: ContextTypes.DEFAULT_TYPE):351 """اطلاعات یک کاربر خاص را نمایش میدهد."""352 if not context.args or not context.args[0].isdigit():353 await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/user_info 123456789`")354 return355 356 user_id = int(context.args[0])357 user_info = data_manager.DATA['users'].get(str(user_id))358 359 if not user_info:360 await update.message.reply_text(f"کاربری با آیدی `{user_id}` در دیتابیس یافت نشد.")361 return362 363 is_banned = "بله" if data_manager.is_user_banned(user_id) else "خیر"364 365 if 'first_seen' in user_info and 'last_seen' in user_info:366 first_date = datetime.strptime(user_info['first_seen'], '%Y-%m-%d %H:%M:%S')367 last_date = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')368 days_active = max(1, (last_date - first_date).days)369 avg_messages = user_info.get('message_count', 0) / days_active370 else:371 avg_messages = user_info.get('message_count', 0)372 373 text = (374 f"ℹ️ **اطلاعات کاربر**\n\n"375 f"🆔 **آیدی:** `{user_id}`\n"376 f"👤 **نام:** {user_info.get('first_name', 'N/A')}\n"377 f"🔷 **نام کاربری:** @{user_info.get('username', 'N/A')}\n"378 f"📊 **تعداد پیامها:** `{user_info.get('message_count', 0)}`\n"379 f"📈 **میانگین پیام در روز:** `{avg_messages:.2f}`\n"380 f"📅 **اولین پیام:** {user_info.get('first_seen', 'N/A')}\n"381 f"🕒 **آخرین فعالیت:** {user_info.get('last_seen', 'N/A')}\n"382 f"🚫 **وضعیت مسدودیت:** {is_banned}"383 )384 await update.message.reply_text(text, parse_mode='Markdown')385 386@admin_only387async def admin_logs(update: Update, context: ContextTypes.DEFAULT_TYPE):388 """آخرین خطوط لاگ ربات را ارسال میکند."""389 try:390 with open(data_manager.LOG_FILE, "r", encoding="utf-8") as f:391 lines = f.readlines()392 last_lines = lines[-30:]393 log_text = "".join(last_lines)394 if not log_text:395 await update.message.reply_text("فایل لاگ خالی است.")396 return397 398 if len(log_text) > 4096:399 for i in range(0, len(log_text), 4096):400 await update.message.reply_text(f"```{log_text[i:i+4096]}```", parse_mode='Markdown')401 else:402 await update.message.reply_text(f"```{log_text}```", parse_mode='Markdown')403 404 except FileNotFoundError:405 await update.message.reply_text("فایل لاگ یافت نشد.")406 except Exception as e:407 await update.message.reply_text(f"خطایی در خواندن لاگ رخ داد: {e}")408 409@admin_only410async def admin_logs_file(update: Update, context: ContextTypes.DEFAULT_TYPE):411 """فایل کامل لاگ ربات را ارسال میکند."""412 try:413 await update.message.reply_document(414 document=open(data_manager.LOG_FILE, 'rb'),415 caption="📂 فایل کامل لاگهای ربات"416 )417 except FileNotFoundError:418 await update.message.reply_text("فایل لاگ یافت نشد.")419 except Exception as e:420 await update.message.reply_text(f"خطایی در ارسال فایل لاگ رخ داد: {e}")421 422@admin_only423async def admin_users_list(update: Update, context: ContextTypes.DEFAULT_TYPE):424 """نمایش لیست کامل کاربران با صفحهبندی."""425 users = data_manager.DATA['users']426 427 page = 1428 if context.args and context.args[0].isdigit():429 page = int(context.args[0])430 if page < 1: page = 1431 432 users_per_page = 20433 total_users = len(users)434 total_pages = (total_users + users_per_page - 1) // users_per_page435 436 if page > total_pages: page = total_pages437 438 start_idx = (page - 1) * users_per_page439 end_idx = min(start_idx + users_per_page, total_users)440 441 sorted_users = sorted(users.items(), key=lambda item: item[1].get('last_seen', ''), reverse=True)442 443 users_text = f"👥 **لیست کاربران (صفحه {page}/{total_pages})**\n\n"444 445 for i, (user_id, user_info) in enumerate(sorted_users[start_idx:end_idx], start=start_idx + 1):446 is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"447 username = user_info.get('username', 'N/A')448 first_name = user_info.get('first_name', 'N/A')449 last_seen = user_info.get('last_seen', 'N/A')450 message_count = user_info.get('message_count', 0)451 452 users_text += f"{i}. {is_banned} `{user_id}` - {first_name} (@{username})\n"453 users_text += f" پیامها: `{message_count}` | آخرین فعالیت: `{last_seen}`\n\n"454 455 keyboard = []456 if page > 1: keyboard.append([InlineKeyboardButton("⬅️ صفحه قبل", callback_data=f"users_list:{page-1}")])457 if page < total_pages: keyboard.append([InlineKeyboardButton("➡️ صفحه بعد", callback_data=f"users_list:{page+1}")])458 459 reply_markup = InlineKeyboardMarkup(keyboard) if keyboard else None460 await update.message.reply_text(users_text, parse_mode='Markdown', reply_markup=reply_markup)461 462@admin_only463async def admin_user_search(update: Update, context: ContextTypes.DEFAULT_TYPE):464 """جستجوی کاربر بر اساس نام یا نام کاربری."""465 if not context.args:466 await update.message.reply_text("⚠️ لطفاً نام یا نام کاربری برای جستجو وارد کنید.\nمثال: `/user_search علی`")467 return468 469 search_term = " ".join(context.args).lower()470 users = data_manager.DATA['users']471 472 matching_users = []473 for user_id, user_info in users.items():474 # --- کد اصلاح شده ---475 # استفاده از (value or '') برای جلوگیری از خطا در صورت وجود None476 first_name = (user_info.get('first_name') or '').lower()477 username = (user_info.get('username') or '').lower()478 # --- پایان کد اصلاح شده ---479 480 if search_term in first_name or search_term in username:481 is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"482 matching_users.append((user_id, user_info, is_banned))483 484 if not matching_users:485 await update.message.reply_text(f"هیچ کاربری با نام «{search_term}» یافت نشد.")486 return487 488 results_text = f"🔍 **نتایج جستجو برای «{search_term}»**\n\n"489 490 for user_id, user_info, is_banned in matching_users:491 username_display = user_info.get('username', 'N/A') # برای نمایش نیازی به lower نیست492 first_name_display = user_info.get('first_name', 'N/A') # برای نمایش نیازی به lower نیست493 last_seen = user_info.get('last_seen', 'N/A')494 message_count = user_info.get('message_count', 0)495 496 results_text += f"{is_banned} `{user_id}` - {first_name_display} (@{username_display})\n"497 results_text += f" پیامها: `{message_count}` | آخرین فعالیت: `{last_seen}`\n\n"498 499 await update.message.reply_text(results_text, parse_mode='Markdown')500 501@admin_only502async def admin_backup(update: Update, context: ContextTypes.DEFAULT_TYPE):503 """ایجاد نسخه پشتیبان از دادههای ربات."""504 try:505 timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')506 backup_file = f"bot_backup_{timestamp}.json"507 508 data_to_backup = data_manager.DATA.copy()509 data_to_backup['banned_users'] = list(data_manager.DATA['banned_users'])510 511 with open(backup_file, 'w', encoding='utf-8') as f:512 json.dump(data_to_backup, f, indent=4, ensure_ascii=False)513 514 await update.message.reply_document(515 document=open(backup_file, 'rb'),516 caption=f"✅ نسخه پشتیبان با موفقیت ایجاد شد: {backup_file}"517 )518 519 logger.info(f"Backup created: {backup_file}")520 os.remove(backup_file) # حذف فایل پس از ارسال521 except Exception as e:522 await update.message.reply_text(f"❌ خطا در ایجاد نسخه پشتیبان: {e}")523 logger.error(f"Error creating backup: {e}")524 525@admin_only526async def admin_export_csv(update: Update, context: ContextTypes.DEFAULT_TYPE):527 """ایجاد و ارسال فایل CSV از اطلاعات کاربران."""528 users = data_manager.DATA['users']529 530 df_data = []531 for user_id, user_info in users.items():532 is_banned = "بله" if int(user_id) in data_manager.DATA['banned_users'] else "خیر"533 df_data.append({534 'User ID': user_id,535 'First Name': user_info.get('first_name', 'N/A'),536 'Username': user_info.get('username', 'N/A'),537 'Message Count': user_info.get('message_count', 0),538 'First Seen': user_info.get('first_seen', 'N/A'),539 'Last Seen': user_info.get('last_seen', 'N/A'),540 'Banned': is_banned541 })542 543 df = pd.DataFrame(df_data)544 545 with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False, encoding='utf-8') as f:546 df.to_csv(f.name, index=False)547 temp_file_path = f.name548 549 await update.message.reply_document(550 document=open(temp_file_path, 'rb'),551 caption="📊 فایل CSV اطلاعات کاربران"552 )553 554 os.unlink(temp_file_path)555 556@admin_only557async def admin_maintenance(update: Update, context: ContextTypes.DEFAULT_TYPE):558 """حالت نگهداری ربات را فعال یا غیرفعال کرده و به کاربران اطلاع میدهد."""559 if not context.args or context.args[0].lower() not in ['on', 'off']:560 await update.message.reply_text("⚠️ فرمت صحیح: `/maintenance on` یا `/maintenance off`")561 return562 563 status = context.args[0].lower()564 565 if status == 'on':566 if data_manager.DATA.get('maintenance_mode', False):567 await update.message.reply_text("🔧 ربات از قبل در حالت نگهداری قرار دارد.")568 return569 570 data_manager.DATA['maintenance_mode'] = True571 data_manager.save_data()572 573 await update.message.reply_text("✅ حالت نگهداری ربات فعال شد. در حال اطلاعرسانی به کاربران...")574 575 user_ids = list(data_manager.DATA['users'].keys())576 for user_id_str in user_ids:577 try:578 # به ادمینها پیام ارسال نشود579 if int(user_id_str) not in ADMIN_IDS:580 await context.bot.send_message(581 chat_id=int(user_id_str), 582 text="🔧 ربات در حال حاضر در حالت بهروزرسانی و نگهداری قرار دارد. لطفاً چند لحظه دیگر صبر کنید. از صبر شما سپاسگزاریم!"583 )584 await asyncio.sleep(0.05) # جلوگیری از محدودیت تلگرام585 except TelegramError:586 continue # نادیده گرفتن کاربرانی که ربات را مسدود کردهاند587 588 elif status == 'off':589 if not data_manager.DATA.get('maintenance_mode', False):590 await update.message.reply_text("✅ ربات از قبل در حالت عادی قرار دارد.")591 return592 593 data_manager.DATA['maintenance_mode'] = False594 data_manager.save_data()595 596 await update.message.reply_text("✅ حالت نگهداری ربات غیرفعال شد. در حال اطلاعرسانی به کاربران...")597 598 user_ids = list(data_manager.DATA['users'].keys())599 for user_id_str in user_ids:600 try: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 continue609 610@admin_only611async def admin_set_welcome_message(update: Update, context: ContextTypes.DEFAULT_TYPE):612 """تنظیم پیام خوشامدگویی جدید."""613 if not context.args:614 await update.message.reply_text("⚠️ لطفاً پیام خوشامدگویی جدید را وارد کنید.\n"615 "مثال: `/set_welcome سلام {user_mention}! به ربات خوش آمدید.`")616 return617 618 new_message = " ".join(context.args)619 data_manager.DATA['welcome_message'] = new_message620 data_manager.save_data()621 622 await update.message.reply_text("✅ پیام خوشامدگویی با موفقیت بهروزرسانی شد.")623 624@admin_only625async def admin_set_goodbye_message(update: Update, context: ContextTypes.DEFAULT_TYPE):626 """تنظیم پیام خداحافظی جدید."""627 if not context.args:628 await update.message.reply_text("⚠️ لطفاً پیام خداحافظی جدید را وارد کنید.\n"629 "مثال: `/set_goodbye {user_mention}، خداحافظ!`")630 return631 632 new_message = " ".join(context.args)633 data_manager.DATA['goodbye_message'] = new_message634 data_manager.save_data()635 636 await update.message.reply_text("✅ پیام خداحافظی با موفقیت بهروزرسانی شد.")637 638@admin_only639async def admin_activity_heatmap(update: Update, context: ContextTypes.DEFAULT_TYPE):640 """ایجاد و ارسال نمودار فعالیت کاربران."""641 users = data_manager.DATA['users']642 activity_hours = [0] * 24643 644 for user_info in users.values():645 if 'last_seen' in user_info:646 try:647 last_seen = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')648 activity_hours[last_seen.hour] += 1649 except ValueError:650 continue651 652 plt.figure(figsize=(12, 6))653 plt.bar(range(24), activity_hours, color='skyblue')654 plt.title('نمودار فعالیت کاربران بر اساس ساعت')655 plt.xlabel('ساعت')656 plt.ylabel('تعداد کاربران فعال')657 plt.xticks(range(24))658 plt.grid(axis='y', alpha=0.3)659 660 with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:661 plt.savefig(f.name, bbox_inches='tight')662 temp_file_path = f.name663 664 plt.close()665 666 await update.message.reply_photo(667 photo=open(temp_file_path, 'rb'),668 caption="📊 نمودار فعالیت کاربران بر اساس ساعت"669 )670 671 os.unlink(temp_file_path)672 673@admin_only674async def admin_response_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):675 """نمایش آمار زمان پاسخگویی ربات."""676 stats = data_manager.DATA['stats']677 await update.message.reply_text(678 "📈 **آمار زمان پاسخگویی ربات**\n\n"679 f"🟢 میانگین زمان پاسخگویی: `{stats.get('avg_response_time', 'N/A'):.2f}` ثانیه\n"680 f"🔴 بیشترین زمان پاسخگویی: `{stats.get('max_response_time', 'N/A'):.2f}` ثانیه\n"681 f"🟢 کمترین زمان پاسخگویی: `{stats.get('min_response_time', 'N/A'):.2f}` ثانیه\n"682 f"📊 کل پاسخهای ثبت شده: `{stats.get('total_responses', 'N/A')}`"683 )684 685@admin_only686async def admin_add_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):687 """افزودن کلمه یا عبارت به لیست کلمات مسدود شده."""688 if not context.args:689 await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"690 "مثال: `/add_blocked_word کلمه_نامناسب`")691 return692 693 word = " ".join(context.args).lower()694 695 if word in data_manager.DATA['blocked_words']:696 await update.message.reply_text(f"⚠️ کلمه «{word}» از قبل در لیست کلمات مسدود شده وجود دارد.")697 return698 699 data_manager.DATA['blocked_words'].append(word)700 data_manager.save_data()701 702 await update.message.reply_text(f"✅ کلمه «{word}» به لیست کلمات مسدود شده اضافه شد.")703 704@admin_only705async def admin_remove_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):706 """حذف کلمه یا عبارت از لیست کلمات مسدود شده."""707 if not context.args:708 await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"709 "مثال: `/remove_blocked_word کلمه_نامناسب`")710 return711 712 word = " ".join(context.args).lower()713 714 if word not in data_manager.DATA['blocked_words']:715 await update.message.reply_text(f"⚠️ کلمه «{word}» در لیست کلمات مسدود شده وجود ندارد.")716 return717 718 data_manager.DATA['blocked_words'].remove(word)719 data_manager.save_data()720 721 await update.message.reply_text(f"✅ کلمه «{word}» از لیست کلمات مسدود شده حذف شد.")722 723@admin_only724async def admin_list_blocked_words(update: Update, context: ContextTypes.DEFAULT_TYPE):725 """نمایش لیست کلمات مسدود شده."""726 if not data_manager.DATA['blocked_words']:727 await update.message.reply_text("هیچ کلمه مسدود شدهای در لیست وجود ندارد.")728 return729 730 words_list = "\n".join([f"• {word}" for word in data_manager.DATA['blocked_words']])731 await update.message.reply_text(f"🚫 **لیست کلمات مسدود شده:**\n\n{words_list}")732 733@admin_only734async def admin_system_info(update: Update, context: ContextTypes.DEFAULT_TYPE):735 """نمایش اطلاعات سیستم و منابع."""736 bot_start_time_str = data_manager.DATA.get('bot_start_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))737 bot_start_time = datetime.strptime(bot_start_time_str, '%Y-%m-%d %H:%M:%S')738 uptime = datetime.now() - bot_start_time739 740 system_info = (741 f"💻 **اطلاعات سیستم:**\n\n"742 f"🖥️ سیستمعامل: {platform.system()} {platform.release()}\n"743 f"🐍 نسخه پایتون: {platform.python_version()}\n"744 f"💾 حافظه RAM استفاده شده: {psutil.virtual_memory().percent}%\n"745 f"💾 حافظه RAM آزاد: {psutil.virtual_memory().available / (1024**3):.2f} GB\n"746 f"💾 فضای دیسک استفاده شده: {psutil.disk_usage('/').percent}%\n"747 f"💾 فضای دیسک آزاد: {psutil.disk_usage('/').free / (1024**3):.2f} GB\n"748 f"⏱️ زمان اجرای ربات: {uptime}"749 )750 751 await update.message.reply_text(system_info, parse_mode='Markdown')752 753@admin_only754async def admin_reset_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):755 """ریست کردن آمار ربات."""756 if not context.args:757 await update.message.reply_text("⚠️ لطفاً نوع آماری که میخواهید ریست کنید را مشخص کنید.\n"758 "مثال: `/reset_stats messages` یا `/reset_stats all`")759 return760 761 stat_type = context.args[0].lower()762 763 if stat_type == "messages":764 data_manager.DATA['stats']['total_messages'] = 0765 for user_id in data_manager.DATA['users']:766 data_manager.DATA['users'][user_id]['message_count'] = 0767 await update.message.reply_text("✅ آمار پیامها با موفقیت ریست شد.")768 769 elif stat_type == "all":770 data_manager.DATA['stats'] = {771 'total_messages': 0,772 'total_users': len(data_manager.DATA['users']),773 'avg_response_time': 0,774 'max_response_time': 0,775 'min_response_time': 0,776 'total_responses': 0777 }778 for user_id in data_manager.DATA['users']:779 data_manager.DATA['users'][user_id]['message_count'] = 0780 await update.message.reply_text("✅ تمام آمارها با موفقیت ریست شد.")781 782 else:783 await update.message.reply_text("⚠️ نوع آمار نامعتبر است. گزینههای موجود: messages, all")784 return785 786 data_manager.save_data()787 788# --- هندلر برای دکمههای صفحهبندی ---789async def users_list_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):790 """پردازش دکمههای صفحهبندی لیست کاربران."""791 query = update.callback_query792 await query.answer()793 794 if query.data.startswith("users_list:"):795 page = int(query.data.split(":")[1])796 context.args = [str(page)]797 await admin_users_list(update, context)798 799# --- تابع برای پردازش ارسالهای برنامهریزی شده ---800async def process_scheduled_broadcasts(context: ContextTypes.DEFAULT_TYPE):801 """پردازش ارسالهای برنامهریزی شده و ارسال پیامها در زمان مقرر."""802 now = datetime.now()803 broadcasts_to_send_indices = []804 805 for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts']):806 if broadcast['status'] == 'pending':807 broadcast_time = datetime.strptime(broadcast['time'], '%Y-%m-%d %H:%M:%S')808 if broadcast_time <= now:809 broadcasts_to_send_indices.append(i)810 811 if not broadcasts_to_send_indices:812 return813 814 user_ids = list(data_manager.DATA['users'].keys())815 816 for index in broadcasts_to_send_indices:817 broadcast = data_manager.DATA['scheduled_broadcasts'][index]818 message_text = broadcast['message']819 total_sent, total_failed = 0, 0820 821 for user_id_str in user_ids:822 try:823 await context.bot.send_message(chat_id=int(user_id_str), text=message_text)824 total_sent += 1825 await asyncio.sleep(0.05)826 except TelegramError as e:827 logger.warning(f"Failed to send scheduled broadcast to {user_id_str}: {e}")828 total_failed += 1829 830 # بهروزرسانی وضعیت ارسال831 data_manager.DATA['scheduled_broadcasts'][index]['status'] = 'sent'832 data_manager.DATA['scheduled_broadcasts'][index]['sent_time'] = now.strftime('%Y-%m-%d %H:%M:%S')833 data_manager.DATA['scheduled_broadcasts'][index]['sent_count'] = total_sent834 data_manager.DATA['scheduled_broadcasts'][index]['failed_count'] = total_failed835 836 logger.info(f"Scheduled broadcast sent: {total_sent} successful, {total_failed} failed")837 838 data_manager.save_data()839 840# --- تابع راهاندازی هندلرها ---841def setup_admin_handlers(application):842 """هندلرهای پنل ادمین را به اپلیکیشن اضافه میکند."""843 # هندلرهای اصلی844 application.add_handler(CommandHandler("commands", admin_commands))845 application.add_handler(CommandHandler("stats", admin_stats))846 application.add_handler(CommandHandler("broadcast", admin_broadcast))847 application.add_handler(CommandHandler("ban", admin_ban))848 application.add_handler(CommandHandler("unban", admin_unban))849 application.add_handler(CommandHandler("user_info", admin_userinfo))850 application.add_handler(CommandHandler("logs", admin_logs))851 application.add_handler(CommandHandler("logs_file", admin_logs_file)) # <--- هندلر جدید852 application.add_handler(CommandHandler("users_list", admin_users_list))853 application.add_handler(CommandHandler("user_search", admin_user_search))854 application.add_handler(CommandHandler("backup", admin_backup))855 856 # هندلرهای جدید857 application.add_handler(CommandHandler("targeted_broadcast", admin_targeted_broadcast))858 application.add_handler(CommandHandler("schedule_broadcast", admin_schedule_broadcast))859 application.add_handler(CommandHandler("list_scheduled", admin_list_scheduled_broadcasts))860 application.add_handler(CommandHandler("remove_scheduled", admin_remove_scheduled_broadcast))861 application.add_handler(CommandHandler("direct_message", admin_direct_message))862 application.add_handler(CommandHandler("export_csv", admin_export_csv))863 application.add_handler(CommandHandler("maintenance", admin_maintenance)) # <--- هندلر جایگزین شده864 application.add_handler(CommandHandler("set_welcome", admin_set_welcome_message))865 application.add_handler(CommandHandler("set_goodbye", admin_set_goodbye_message))866 application.add_handler(CommandHandler("activity_heatmap", admin_activity_heatmap))867 application.add_handler(CommandHandler("response_stats", admin_response_stats))868 application.add_handler(CommandHandler("add_blocked_word", admin_add_blocked_word))869 application.add_handler(CommandHandler("remove_blocked_word", admin_remove_blocked_word))870 application.add_handler(CommandHandler("list_blocked_words", admin_list_blocked_words))871 application.add_handler(CommandHandler("system_info", admin_system_info))872 application.add_handler(CommandHandler("reset_stats", admin_reset_stats))873 874 # هندلر برای دکمههای صفحهبندی875 application.add_handler(CallbackQueryHandler(users_list_callback, pattern="^users_list:"))876 877 # شروع وظیفه دورهای برای بررسی ارسالهای برنامهریزی شده878 application.job_queue.run_repeating(process_scheduled_broadcasts, interval=60, first=0)879 880 logger.info("Admin panel handlers have been set up.")881 