CoolFace
Datasetpublic

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)

sourceHugging Faceapache-2.0updated 4mo agoView on Hugging Face
3likes1.6kdownloads
admin_panel.py881 linesDownload Raw Back to render-main
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