Team Ai
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.7kdownloads
admin_panel.py1232 linesDownload Raw Back to 2
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 14import matplotlib15matplotlib.use('Agg')16import matplotlib.pyplot as plt17import pandas as pd18import tempfile19import psutil20import platform21import time22 23# --- تنظیمات ---24ADMIN_IDS = list(map(int, os.environ.get("ADMIN_IDS", "").split(','))) if os.environ.get("ADMIN_IDS") else []25 26import data_manager27 28logger = logging.getLogger(__name__)29 30# --- دکوراتور برای دسترسی ادمین ---31def admin_only(func):32    async def wrapped(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):33        if update.effective_user.id not in ADMIN_IDS:34            await update.message.reply_text("⛔️ شما دسترسی لازم برای اجرای این دستور را ندارید.")35            return36        return await func(update, context, *args, **kwargs)37    return wrapped38 39# --- هندلرهای دستورات ادمین ---40@admin_only41async def admin_commands(update: Update, context: ContextTypes.DEFAULT_TYPE):42    """نمایش تمام دستورات موجود ادمین."""43    commands_text = (44        "📋 **دستورات ادمین ربات:**\n\n"45        "📊 `/stats` - نمایش آمار ربات\n"46        "📢 `/broadcast [پیام]` - ارسال پیام به تمام کاربران\n"47        "🎯 `/targeted_broadcast [معیار] [مقدار] [پیام]` - ارسال پیام هدفمند\n"48        "📅 `/schedule_broadcast [YYYY-MM-DD] [HH:MM] [پیام]` - ارسال برنامه‌ریزی شده\n"49        "📋 `/list_scheduled` - نمایش لیست ارسال‌های برنامه‌ریزی شده\n"50        "🗑️ `/remove_scheduled [شماره]` - حذف ارسال برنامه‌ریزی شده\n"51        "🚫 `/ban [آیدی]` - مسدود کردن کاربر\n"52        "✅ `/unban [آیدی]` - رفع مسدودیت کاربر\n"53        "💌 `/direct_message [آیدی] [پیام]` - ارسال پیام مستقیم به کاربر\n"54        "ℹ️ `/user_info [آیدی]` - نمایش اطلاعات کاربر\n"55        "📝 `/logs` - نمایش آخرین لاگ‌ها\n"56        "📂 `/logs_file` - دانلود فایل کامل لاگ‌ها\n"57        "👥 `/users_list [صفحه]` - نمایش لیست کاربران\n"58        "🔍 `/user_search [نام]` - جستجوی کاربر بر اساس نام\n"59        "💾 `/backup` - ایجاد نسخه پشتیبان از داده‌ها\n"60        "📊 `/export_csv` - دانلود اطلاعات کاربران در فایل CSV\n"61        "🔧 `/maintenance [on/off]` - فعال/غیرفعال کردن حالت نگهداری\n"62        "👋 `/set_welcome [پیام]` - تنظیم پیام خوشامدگویی\n"63        "👋 `/set_goodbye [پیام]` - تنظیم پیام خداحافظی\n"64        "📈 `/activity_heatmap` - دریافت نمودار فعالیت کاربران\n"65        "⏱️ `/response_stats` - نمایش آمار زمان پاسخگویی\n"66        "🚫 `/add_blocked_word [کلمه]` - افزودن کلمه مسدود\n"67        "✅ `/remove_blocked_word [کلمه]` - حذف کلمه مسدود\n"68        "📜 `/list_blocked_words` - نمایش لیست کلمات مسدود\n"69        "💻 `/system_info` - نمایش اطلاعات سیستم\n"70        "🔄 `/reset_stats [messages/all]` - ریست کردن آمار\n"71        "🗑️ `/clear_context [آیدی]` - پاک کردن context کاربر\n"72        "📋 `/view_context [آیدی]` - مشاهده context کاربر\n"73        "📋 `/view_replies [user|group] [آیدی]` - مشاهده ریپلای‌ها\n"74        "🔄 `/set_context_mode [separate|group_shared|hybrid]` - تغییر حالت context\n"75        "📊 `/group_info [آیدی]` - نمایش اطلاعات گروه\n"76        "🗑️ `/clear_group_context [آیدی]` - پاک کردن context گروه\n"77        "💾 `/memory_status` - نمایش وضعیت حافظه\n"78        "📋 `/commands` - نمایش این لیست دستورات"79    )80    await update.message.reply_text(commands_text, parse_mode='Markdown')81 82@admin_only83async def admin_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):84    """آمار ربات را نمایش می‌دهد."""85    total_users = len(data_manager.DATA['users'])86    total_groups = len(data_manager.DATA['groups'])87    total_messages = data_manager.DATA['stats']['total_messages']88    banned_count = len(data_manager.DATA['banned_users'])89    90    now = datetime.now()91    active_24h = 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(hours=24))94    95    active_7d = sum(1 for user in data_manager.DATA['users'].values() 96                   if 'last_seen' in user and 97                   datetime.strptime(user['last_seen'], '%Y-%m-%d %H:%M:%S') > now - timedelta(days=7))98 99    active_users = sorted(100        data_manager.DATA['users'].items(),101        key=lambda item: item[1].get('last_seen', ''),102        reverse=True103    )[:5]104 105    active_users_text = "\n".join(106        [f"• {user_id}: {info.get('first_name', 'N/A')} (آخرین فعالیت: {info.get('last_seen', 'N/A')})"107         for user_id, info in active_users]108    )109 110    # آمار context111    users_with_context = sum(1 for user in data_manager.DATA['users'].values() 112                           if user.get('context') and len(user['context']) > 0)113    total_context_messages = sum(len(user.get('context', [])) for user in data_manager.DATA['users'].values())114    115    # آمار ریپلای‌ها116    total_replies = 0117    for user in data_manager.DATA['users'].values():118        if 'context' in user:119            total_replies += sum(1 for msg in user['context'] if msg.get('has_reply', False))120    121    # آمار گروه‌ها122    groups_with_context = sum(1 for group in data_manager.DATA['groups'].values() 123                            if group.get('context') and len(group['context']) > 0)124 125    text = (126        f"📊 **آمار ربات**\n\n"127        f"👥 **تعداد کل کاربران:** `{total_users}`\n"128        f"👥 **تعداد کل گروه‌ها:** `{total_groups}`\n"129        f"📝 **تعداد کل پیام‌ها:** `{total_messages}`\n"130        f"🚫 **کاربران مسدود شده:** `{banned_count}`\n"131        f"🟢 **کاربران فعال 24 ساعت گذشته:** `{active_24h}`\n"132        f"🟢 **کاربران فعال 7 روز گذشته:** `{active_7d}`\n"133        f"💭 **کاربران با context فعال:** `{users_with_context}`\n"134        f"💬 **گروه‌های با context فعال:** `{groups_with_context}`\n"135        f"📝 **کل پیام‌های context:** `{total_context_messages}`\n"136        f"📎 **کل ریپلای‌های ثبت شده:** `{total_replies}`\n"137        f"🔄 **حالت context فعلی:** `{data_manager.get_context_mode()}`\n\n"138        f"**۵ کاربر اخیر فعال:**\n{active_users_text}"139    )140    await update.message.reply_text(text, parse_mode='Markdown')141 142@admin_only143async def admin_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):144    """یک پیام را به تمام کاربران ارسال می‌کند."""145    if not context.args:146        await update.message.reply_text("⚠️ لطفاً پیامی برای ارسال بنویسید.\nمثال: `/broadcast سلام به همه!`")147        return148 149    message_text = " ".join(context.args)150    user_ids = list(data_manager.DATA['users'].keys())151    total_sent = 0152    total_failed = 0153 154    await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(user_ids)}` کاربر...")155 156    for user_id_str in user_ids:157        try:158            await context.bot.send_message(chat_id=int(user_id_str), text=message_text)159            total_sent += 1160            await asyncio.sleep(0.05)161        except TelegramError as e:162            logger.warning(f"Failed to send broadcast to {user_id_str}: {e}")163            total_failed += 1164 165    result_text = (166        f"✅ **ارسال همگانی تمام شد**\n\n"167        f"✅ موفق: `{total_sent}`\n"168        f"❌ ناموفق: `{total_failed}`"169    )170    await update.message.reply_text(result_text, parse_mode='Markdown')171 172@admin_only173async def admin_targeted_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):174    """ارسال پیام به گروه خاصی از کاربران بر اساس معیارهای مشخص."""175    if len(context.args) < 3:176        await update.message.reply_text("⚠️ فرمت صحیح: `/targeted_broadcast [معیار] [مقدار] [پیام]`\n"177                                       "معیارهای موجود: `active_days`, `message_count`, `banned`")178        return179    180    criteria = context.args[0].lower()181    value = context.args[1]182    message_text = " ".join(context.args[2:])183    184    target_users = []185    186    if criteria == "active_days":187        try:188            days = int(value)189            target_users = data_manager.get_active_users(days)190        except ValueError:191            await update.message.reply_text("⚠️ مقدار روز باید یک عدد صحیح باشد.")192            return193    194    elif criteria == "message_count":195        try:196            min_count = int(value)197            target_users = data_manager.get_users_by_message_count(min_count)198        except ValueError:199            await update.message.reply_text("⚠️ تعداد پیام باید یک عدد صحیح باشد.")200            return201    202    elif criteria == "banned":203        if value.lower() == "true":204            target_users = list(data_manager.DATA['banned_users'])205        elif value.lower() == "false":206            for user_id in data_manager.DATA['users']:207                if int(user_id) not in data_manager.DATA['banned_users']:208                    target_users.append(int(user_id))209        else:210            await update.message.reply_text("⚠️ مقدار برای معیار banned باید true یا false باشد.")211            return212    213    else:214        await update.message.reply_text("⚠️ معیار نامعتبر است. معیارهای موجود: active_days, message_count, banned")215        return216    217    if not target_users:218        await update.message.reply_text("هیچ کاربری با معیارهای مشخص شده یافت نشد.")219        return220    221    await update.message.reply_text(f"📣 در حال ارسال پیام به `{len(target_users)}` کاربر...")222    223    total_sent, total_failed = 0, 0224    for user_id in target_users:225        try:226            await context.bot.send_message(chat_id=user_id, text=message_text)227            total_sent += 1228            await asyncio.sleep(0.05)229        except TelegramError as e:230            logger.warning(f"Failed to send targeted broadcast to {user_id}: {e}")231            total_failed += 1232    233    result_text = f"✅ **ارسال هدفمند تمام شد**\n\n✅ موفق: `{total_sent}`\n❌ ناموفق: `{total_failed}`"234    await update.message.reply_text(result_text, parse_mode='Markdown')235 236@admin_only237async def admin_schedule_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):238    """تنظیم ارسال برنامه‌ریزی شده پیام به همه کاربران."""239    if len(context.args) < 3:240        await update.message.reply_text("⚠️ فرمت صحیح: `/schedule_broadcast [YYYY-MM-DD] [HH:MM] [پیام]`")241        return242    243    try:244        date_str, time_str = context.args[0], context.args[1]245        message_text = " ".join(context.args[2:])246        247        scheduled_time = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')248        249        if scheduled_time <= datetime.now():250            await update.message.reply_text("⚠️ زمان برنامه‌ریزی شده باید در آینده باشد.")251            return252        253        data_manager.DATA['scheduled_broadcasts'].append({254            'time': scheduled_time.strftime('%Y-%m-%d %H:%M:%S'),255            'message': message_text,256            'status': 'pending'257        })258        data_manager.save_data()259        260        await update.message.reply_text(f"✅ پیام برای زمان `{scheduled_time.strftime('%Y-%m-%d %H:%M')}` برنامه‌ریزی شد.")261        262    except ValueError:263        await update.message.reply_text("⚠️ فرمت زمان نامعتبر است. لطفاً از فرمت YYYY-MM-DD HH:MM استفاده کنید.")264 265@admin_only266async def admin_list_scheduled_broadcasts(update: Update, context: ContextTypes.DEFAULT_TYPE):267    """نمایش لیست ارسال‌های برنامه‌ریزی شده."""268    if not data_manager.DATA['scheduled_broadcasts']:269        await update.message.reply_text("هیچ ارسال برنامه‌ریزی شده‌ای وجود ندارد.")270        return271    272    broadcasts_text = "📅 **لیست ارسال‌های برنامه‌ریزی شده:**\n\n"273    for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts'], 1):274        status_emoji = "✅" if broadcast['status'] == 'sent' else "⏳"275        broadcasts_text += f"{i}. {status_emoji} `{broadcast['time']}` - {broadcast['message'][:50]}...\n"276    277    await update.message.reply_text(broadcasts_text, parse_mode='Markdown')278 279@admin_only280async def admin_remove_scheduled_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):281    """حذف یک ارسال برنامه‌ریزی شده."""282    if not context.args or not context.args[0].isdigit():283        await update.message.reply_text("⚠️ لطفاً شماره ارسال برنامه‌ریزی شده را وارد کنید.\nمثال: `/remove_scheduled 1`")284        return285    286    index = int(context.args[0]) - 1287    288    if not data_manager.DATA['scheduled_broadcasts'] or not (0 <= index < len(data_manager.DATA['scheduled_broadcasts'])):289        await update.message.reply_text("⚠️ شماره ارسال برنامه‌ریزی شده نامعتبر است.")290        return291    292    removed_broadcast = data_manager.DATA['scheduled_broadcasts'].pop(index)293    data_manager.save_data()294    295    await update.message.reply_text(f"✅ ارسال برنامه‌ریزی شده برای زمان `{removed_broadcast['time']}` حذف شد.")296 297@admin_only298async def admin_ban(update: Update, context: ContextTypes.DEFAULT_TYPE):299    """یک کاربر را با آیدی عددی مسدود کرده و به او اطلاع می‌دهد."""300    if not context.args or not context.args[0].isdigit():301        await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/ban 123456789`")302        return303 304    user_id_to_ban = int(context.args[0])305 306    if user_id_to_ban in ADMIN_IDS:307        await update.message.reply_text("🛡️ شما نمی‌توانید یک ادمین را مسدود کنید!")308        return309 310    if data_manager.is_user_banned(user_id_to_ban):311        await update.message.reply_text(f"کاربر `{user_id_to_ban}` از قبل مسدود شده است.")312        return313 314    data_manager.ban_user(user_id_to_ban)315    316    # ارسال پیام به کاربر مسدود شده317    try:318        await context.bot.send_message(319            chat_id=user_id_to_ban, 320            text="⛔️ شما توسط ادمین ربات مسدود شدید و دیگر نمی‌توانید از خدمات ربات استفاده کنید."321        )322    except TelegramError as e:323        logger.warning(f"Could not send ban notification to user {user_id_to_ban}: {e}")324 325    await update.message.reply_text(f"✅ کاربر `{user_id_to_ban}` با موفقیت مسدود شد.", parse_mode='Markdown')326 327@admin_only328async def admin_unban(update: Update, context: ContextTypes.DEFAULT_TYPE):329    """مسدودیت یک کاربر را برمی‌دارد و به او اطلاع می‌دهد."""330    if not context.args or not context.args[0].isdigit():331        await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/unban 123456789`")332        return333 334    user_id_to_unban = int(context.args[0])335 336    if not data_manager.is_user_banned(user_id_to_unban):337        await update.message.reply_text(f"کاربر `{user_id_to_unban}` در لیست مسدود شده‌ها وجود ندارد.")338        return339 340    data_manager.unban_user(user_id_to_unban)341 342    # ارسال پیام به کاربر برای رفع مسدودیت343    try:344        await context.bot.send_message(345            chat_id=user_id_to_unban, 346            text="✅ مسدودیت شما توسط ادمین ربات برداشته شد. می‌توانید دوباره از ربات استفاده کنید."347        )348    except TelegramError as e:349        logger.warning(f"Could not send unban notification to user {user_id_to_unban}: {e}")350 351    await update.message.reply_text(f"✅ مسدودیت کاربر `{user_id_to_unban}` با موفقیت برداشته شد.", parse_mode='Markdown')352 353@admin_only354async def admin_direct_message(update: Update, context: ContextTypes.DEFAULT_TYPE):355    """ارسال پیام مستقیم به یک کاربر خاص."""356    if len(context.args) < 2:357        await update.message.reply_text("⚠️ فرمت صحیح: `/direct_message [آیدی] [پیام]`")358        return359    360    user_id_str = context.args[0]361    if not user_id_str.isdigit():362        await update.message.reply_text("⚠️ لطفاً یک آیدی عددی معتبر وارد کنید.")363        return364    365    message_text = " ".join(context.args[1:])366    user_id = int(user_id_str)367    368    try:369        await context.bot.send_message(chat_id=user_id, text=message_text)370        await update.message.reply_text(f"✅ پیام با موفقیت به کاربر `{user_id}` ارسال شد.", parse_mode='Markdown')371    except TelegramError as e:372        await update.message.reply_text(f"❌ خطا در ارسال پیام: {e}")373 374@admin_only375async def admin_userinfo(update: Update, context: ContextTypes.DEFAULT_TYPE):376    """اطلاعات یک کاربر خاص را نمایش می‌دهد."""377    if not context.args or not context.args[0].isdigit():378        await update.message.reply_text("⚠️ لطفاً آیدی عددی کاربر را وارد کنید.\nمثال: `/user_info 123456789`")379        return380 381    user_id = int(context.args[0])382    user_info = data_manager.DATA['users'].get(str(user_id))383 384    if not user_info:385        await update.message.reply_text(f"کاربری با آیدی `{user_id}` در دیتابیس یافت نشد.")386        return387 388    is_banned = "بله" if data_manager.is_user_banned(user_id) else "خیر"389    390    if 'first_seen' in user_info and 'last_seen' in user_info:391        try:392            first_date = datetime.strptime(user_info['first_seen'], '%Y-%m-%d %H:%M:%S')393            last_date = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')394            days_active = max(1, (last_date - first_date).days)395            avg_messages = user_info.get('message_count', 0) / days_active396        except:397            avg_messages = user_info.get('message_count', 0)398    else:399        avg_messages = user_info.get('message_count', 0)400    401    # اطلاعات context402    context_messages = len(user_info.get('context', []))403    context_tokens = data_manager.get_context_token_count(user_id)404    405    # اطلاعات ریپلای‌ها406    reply_count = 0407    if 'context' in user_info:408        reply_count = sum(1 for msg in user_info['context'] if msg.get('has_reply', False))409    410    text = (411        f"ℹ️ **اطلاعات کاربر**\n\n"412        f"🆔 **آیدی:** `{user_id}`\n"413        f"👤 **نام:** {user_info.get('first_name', 'N/A')}\n"414        f"🔷 **نام کاربری:** @{user_info.get('username', 'N/A')}\n"415        f"📊 **تعداد پیام‌ها:** `{user_info.get('message_count', 0)}`\n"416        f"📈 **میانگین پیام در روز:** `{avg_messages:.2f}`\n"417        f"📎 **تعداد ریپلای‌ها:** `{reply_count}`\n"418        f"📅 **اولین پیام:** {user_info.get('first_seen', 'N/A')}\n"419        f"🕒 **آخرین فعالیت:** {user_info.get('last_seen', 'N/A')}\n"420        f"💭 **پیام‌های context:** `{context_messages}`\n"421        f"🔢 **توکن‌های context:** `{context_tokens}`\n"422        f"🚫 **وضعیت مسدودیت:** {is_banned}"423    )424    await update.message.reply_text(text, parse_mode='Markdown')425 426@admin_only427async def admin_logs(update: Update, context: ContextTypes.DEFAULT_TYPE):428    """آخرین خطوط لاگ ربات را ارسال می‌کند."""429    try:430        with open(data_manager.LOG_FILE, "r", encoding="utf-8") as f:431            lines = f.readlines()432            last_lines = lines[-30:]433            log_text = "".join(last_lines)434            if not log_text:435                await update.message.reply_text("فایل لاگ خالی است.")436                return437            438            if len(log_text) > 4096:439                for i in range(0, len(log_text), 4096):440                    await update.message.reply_text(f"```{log_text[i:i+4096]}```", parse_mode='Markdown')441            else:442                await update.message.reply_text(f"```{log_text}```", parse_mode='Markdown')443 444    except FileNotFoundError:445        await update.message.reply_text("فایل لاگ یافت نشد.")446    except Exception as e:447        await update.message.reply_text(f"خطایی در خواندن لاگ رخ داد: {e}")448 449@admin_only450async def admin_logs_file(update: Update, context: ContextTypes.DEFAULT_TYPE):451    """فایل کامل لاگ ربات را ارسال می‌کند."""452    try:453        await update.message.reply_document(454            document=open(data_manager.LOG_FILE, 'rb'),455            caption="📂 فایل کامل لاگ‌های ربات"456        )457    except FileNotFoundError:458        await update.message.reply_text("فایل لاگ یافت نشد.")459    except Exception as e:460        await update.message.reply_text(f"خطایی در ارسال فایل لاگ رخ داد: {e}")461 462@admin_only463async def admin_users_list(update: Update, context: ContextTypes.DEFAULT_TYPE):464    """نمایش لیست کامل کاربران با صفحه‌بندی."""465    users = data_manager.DATA['users']466    467    page = 1468    if context.args and context.args[0].isdigit():469        page = int(context.args[0])470        if page < 1: page = 1471    472    users_per_page = 20473    total_users = len(users)474    total_pages = (total_users + users_per_page - 1) // users_per_page475    476    if page > total_pages: page = total_pages477    478    start_idx = (page - 1) * users_per_page479    end_idx = min(start_idx + users_per_page, total_users)480    481    sorted_users = sorted(users.items(), key=lambda item: item[1].get('last_seen', ''), reverse=True)482    483    users_text = f"👥 **لیست کاربران (صفحه {page}/{total_pages})**\n\n"484    485    for i, (user_id, user_info) in enumerate(sorted_users[start_idx:end_idx], start=start_idx + 1):486        is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"487        username = user_info.get('username', 'N/A')488        first_name = user_info.get('first_name', 'N/A')489        last_seen = user_info.get('last_seen', 'N/A')490        message_count = user_info.get('message_count', 0)491        context_count = len(user_info.get('context', []))492        493        # تعداد ریپلای‌ها494        reply_count = 0495        if 'context' in user_info:496            reply_count = sum(1 for msg in user_info['context'] if msg.get('has_reply', False))497        498        users_text += f"{i}. {is_banned} `{user_id}` - {first_name} (@{username})\n"499        users_text += f"   📝 پیام‌ها: `{message_count}` | 💭 context: `{context_count}` | 📎 ریپلای‌ها: `{reply_count}`\n"500        users_text += f"   ⏰ آخرین فعالیت: `{last_seen}`\n\n"501    502    keyboard = []503    if page > 1: keyboard.append([InlineKeyboardButton("⬅️ صفحه قبل", callback_data=f"users_list:{page-1}")])504    if page < total_pages: keyboard.append([InlineKeyboardButton("➡️ صفحه بعد", callback_data=f"users_list:{page+1}")])505    506    reply_markup = InlineKeyboardMarkup(keyboard) if keyboard else None507    await update.message.reply_text(users_text, parse_mode='Markdown', reply_markup=reply_markup)508 509@admin_only510async def admin_user_search(update: Update, context: ContextTypes.DEFAULT_TYPE):511    """جستجوی کاربر بر اساس نام یا نام کاربری."""512    if not context.args:513        await update.message.reply_text("⚠️ لطفاً نام یا نام کاربری برای جستجو وارد کنید.\nمثال: `/user_search علی`")514        return515    516    search_term = " ".join(context.args).lower()517    users = data_manager.DATA['users']518    519    matching_users = []520    for user_id, user_info in users.items():521        first_name = (user_info.get('first_name') or '').lower()522        username = (user_info.get('username') or '').lower()523        524        if search_term in first_name or search_term in username:525            is_banned = "🚫" if int(user_id) in data_manager.DATA['banned_users'] else "✅"526            matching_users.append((user_id, user_info, is_banned))527    528    if not matching_users:529        await update.message.reply_text(f"هیچ کاربری با نام «{search_term}» یافت نشد.")530        return531    532    results_text = f"🔍 **نتایج جستجو برای «{search_term}»**\n\n"533    534    for user_id, user_info, is_banned in matching_users:535        username_display = user_info.get('username', 'N/A')536        first_name_display = user_info.get('first_name', 'N/A')537        last_seen = user_info.get('last_seen', 'N/A')538        message_count = user_info.get('message_count', 0)539        context_count = len(user_info.get('context', []))540        541        # تعداد ریپلای‌ها542        reply_count = 0543        if 'context' in user_info:544            reply_count = sum(1 for msg in user_info['context'] if msg.get('has_reply', False))545        546        results_text += f"{is_banned} `{user_id}` - {first_name_display} (@{username_display})\n"547        results_text += f"   📝 پیام‌ها: `{message_count}` | 💭 context: `{context_count}` | 📎 ریپلای‌ها: `{reply_count}`\n"548        results_text += f"   ⏰ آخرین فعالیت: `{last_seen}`\n\n"549    550    await update.message.reply_text(results_text, parse_mode='Markdown')551 552@admin_only553async def admin_backup(update: Update, context: ContextTypes.DEFAULT_TYPE):554    """ایجاد نسخه پشتیبان از داده‌های ربات."""555    try:556        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')557        backup_file = f"bot_backup_{timestamp}.json"558        559        data_to_backup = data_manager.DATA.copy()560        data_to_backup['banned_users'] = list(data_manager.DATA['banned_users'])561        562        with open(backup_file, 'w', encoding='utf-8') as f:563            json.dump(data_to_backup, f, indent=4, ensure_ascii=False)564        565        await update.message.reply_document(566            document=open(backup_file, 'rb'),567            caption=f"✅ نسخه پشتیبان با موفقیت ایجاد شد: {backup_file}"568        )569        570        logger.info(f"Backup created: {backup_file}")571        os.remove(backup_file)572    except Exception as e:573        await update.message.reply_text(f"❌ خطا در ایجاد نسخه پشتیبان: {e}")574        logger.error(f"Error creating backup: {e}")575 576@admin_only577async def admin_export_csv(update: Update, context: ContextTypes.DEFAULT_TYPE):578    """ایجاد و ارسال فایل CSV از اطلاعات کاربران."""579    users = data_manager.DATA['users']580    581    df_data = []582    for user_id, user_info in users.items():583        is_banned = "بله" if int(user_id) in data_manager.DATA['banned_users'] else "خیر"584        context_count = len(user_info.get('context', []))585        context_tokens = data_manager.get_context_token_count(int(user_id))586        587        # تعداد ریپلای‌ها588        reply_count = 0589        if 'context' in user_info:590            reply_count = sum(1 for msg in user_info['context'] if msg.get('has_reply', False))591        592        df_data.append({593            'User ID': user_id,594            'First Name': user_info.get('first_name', 'N/A'),595            'Username': user_info.get('username', 'N/A'),596            'Message Count': user_info.get('message_count', 0),597            'Context Messages': context_count,598            'Context Tokens': context_tokens,599            'Reply Count': reply_count,600            'First Seen': user_info.get('first_seen', 'N/A'),601            'Last Seen': user_info.get('last_seen', 'N/A'),602            'Banned': is_banned603        })604    605    df = pd.DataFrame(df_data)606    607    with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False, encoding='utf-8') as f:608        df.to_csv(f.name, index=False)609        temp_file_path = f.name610    611    await update.message.reply_document(612        document=open(temp_file_path, 'rb'),613        caption="📊 فایل CSV اطلاعات کاربران"614    )615    616    os.unlink(temp_file_path)617 618@admin_only619async def admin_maintenance(update: Update, context: ContextTypes.DEFAULT_TYPE):620    """حالت نگهداری ربات را فعال یا غیرفعال کرده و به کاربران اطلاع می‌دهد."""621    if not context.args or context.args[0].lower() not in ['on', 'off']:622        await update.message.reply_text("⚠️ فرمت صحیح: `/maintenance on` یا `/maintenance off`")623        return624 625    status = context.args[0].lower()626    627    if status == 'on':628        if data_manager.DATA.get('maintenance_mode', False):629            await update.message.reply_text("🔧 ربات از قبل در حالت نگهداری قرار دارد.")630            return631            632        data_manager.DATA['maintenance_mode'] = True633        data_manager.save_data()634        635        await update.message.reply_text("✅ حالت نگهداری ربات فعال شد. در حال اطلاع‌رسانی به کاربران...")636        637        user_ids = list(data_manager.DATA['users'].keys())638        for user_id_str in user_ids:639            try:640                if int(user_id_str) not in ADMIN_IDS:641                    await context.bot.send_message(642                        chat_id=int(user_id_str), 643                        text="🔧 ربات در حال حاضر در حالت به‌روزرسانی و نگهداری قرار دارد. لطفاً چند لحظه دیگر صبر کنید. از صبر شما سپاسگزاریم!"644                    )645                    await asyncio.sleep(0.05)646            except TelegramError:647                continue648 649    elif status == 'off':650        if not data_manager.DATA.get('maintenance_mode', False):651            await update.message.reply_text("✅ ربات از قبل در حالت عادی قرار دارد.")652            return653 654        data_manager.DATA['maintenance_mode'] = False655        data_manager.save_data()656 657        await update.message.reply_text("✅ حالت نگهداری ربات غیرفعال شد. در حال اطلاع‌رسانی به کاربران...")658 659        user_ids = list(data_manager.DATA['users'].keys())660        for user_id_str in user_ids:661            try:662                if int(user_id_str) not in ADMIN_IDS:663                    await context.bot.send_message(664                        chat_id=int(user_id_str), 665                        text="✅ به‌روزرسانی ربات به پایان رسید. از صبر شما سپاسگزاریم! می‌توانید دوباره از ربات استفاده کنید."666                    )667                    await asyncio.sleep(0.05)668            except TelegramError:669                continue670 671@admin_only672async def admin_set_welcome_message(update: Update, context: ContextTypes.DEFAULT_TYPE):673    """تنظیم پیام خوشامدگویی جدید."""674    if not context.args:675        await update.message.reply_text("⚠️ لطفاً پیام خوشامدگویی جدید را وارد کنید.\n"676                                       "مثال: `/set_welcome سلام {user_mention}! به ربات خوش آمدید.`")677        return678    679    new_message = " ".join(context.args)680    data_manager.DATA['welcome_message'] = new_message681    data_manager.save_data()682    683    await update.message.reply_text("✅ پیام خوشامدگویی با موفقیت به‌روزرسانی شد.")684 685@admin_only686async def admin_set_goodbye_message(update: Update, context: ContextTypes.DEFAULT_TYPE):687    """تنظیم پیام خداحافظی جدید."""688    if not context.args:689        await update.message.reply_text("⚠️ لطفاً پیام خداحافظی جدید را وارد کنید.\n"690                                       "مثال: `/set_goodbye {user_mention}، خداحافظ!`")691        return692    693    new_message = " ".join(context.args)694    data_manager.DATA['goodbye_message'] = new_message695    data_manager.save_data()696    697    await update.message.reply_text("✅ پیام خداحافظی با موفقیت به‌روزرسانی شد.")698 699@admin_only700async def admin_activity_heatmap(update: Update, context: ContextTypes.DEFAULT_TYPE):701    """ایجاد و ارسال نمودار فعالیت کاربران."""702    users = data_manager.DATA['users']703    activity_hours = [0] * 24704    705    for user_info in users.values():706        if 'last_seen' in user_info:707            try:708                last_seen = datetime.strptime(user_info['last_seen'], '%Y-%m-%d %H:%M:%S')709                activity_hours[last_seen.hour] += 1710            except ValueError:711                continue712    713    plt.figure(figsize=(12, 6))714    plt.bar(range(24), activity_hours, color='skyblue')715    plt.title('نمودار فعالیت کاربران بر اساس ساعت')716    plt.xlabel('ساعت')717    plt.ylabel('تعداد کاربران فعال')718    plt.xticks(range(24))719    plt.grid(axis='y', alpha=0.3)720    721    with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:722        plt.savefig(f.name, bbox_inches='tight')723        temp_file_path = f.name724    725    plt.close()726    727    await update.message.reply_photo(728        photo=open(temp_file_path, 'rb'),729        caption="📊 نمودار فعالیت کاربران بر اساس ساعت"730    )731    732    os.unlink(temp_file_path)733 734@admin_only735async def admin_response_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):736    """نمایش آمار زمان پاسخگویی ربات."""737    stats = data_manager.DATA['stats']738    await update.message.reply_text(739        "📈 **آمار زمان پاسخگویی ربات**\n\n"740        f"🟢 میانگین زمان پاسخگویی: `{stats.get('avg_response_time', 0):.2f}` ثانیه\n"741        f"🔴 بیشترین زمان پاسخگویی: `{stats.get('max_response_time', 0):.2f}` ثانیه\n"742        f"🟢 کمترین زمان پاسخگویی: `{stats.get('min_response_time', 0):.2f}` ثانیه\n"743        f"📊 کل پاسخ‌های ثبت شده: `{stats.get('total_responses', 0)}`"744    )745 746@admin_only747async def admin_add_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):748    """افزودن کلمه یا عبارت به لیست کلمات مسدود شده."""749    if not context.args:750        await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"751                                       "مثال: `/add_blocked_word کلمه_نامناسب`")752        return753    754    word = " ".join(context.args).lower()755    756    if word in data_manager.DATA['blocked_words']:757        await update.message.reply_text(f"⚠️ کلمه «{word}» از قبل در لیست کلمات مسدود شده وجود دارد.")758        return759    760    data_manager.DATA['blocked_words'].append(word)761    data_manager.save_data()762    763    await update.message.reply_text(f"✅ کلمه «{word}» به لیست کلمات مسدود شده اضافه شد.")764 765@admin_only766async def admin_remove_blocked_word(update: Update, context: ContextTypes.DEFAULT_TYPE):767    """حذف کلمه یا عبارت از لیست کلمات مسدود شده."""768    if not context.args:769        await update.message.reply_text("⚠️ لطفاً کلمه یا عبارت مورد نظر را وارد کنید.\n"770                                       "مثال: `/remove_blocked_word کلمه_نامناسب`")771        return772    773    word = " ".join(context.args).lower()774    775    if word not in data_manager.DATA['blocked_words']:776        await update.message.reply_text(f"⚠️ کلمه «{word}» در لیست کلمات مسدود شده وجود ندارد.")777        return778    779    data_manager.DATA['blocked_words'].remove(word)780    data_manager.save_data()781    782    await update.message.reply_text(f"✅ کلمه «{word}» از لیست کلمات مسدود شده حذف شد.")783 784@admin_only785async def admin_list_blocked_words(update: Update, context: ContextTypes.DEFAULT_TYPE):786    """نمایش لیست کلمات مسدود شده."""787    if not data_manager.DATA['blocked_words']:788        await update.message.reply_text("هیچ کلمه مسدود شده‌ای در لیست وجود ندارد.")789        return790    791    words_list = "\n".join([f"• {word}" for word in data_manager.DATA['blocked_words']])792    await update.message.reply_text(f"🚫 **لیست کلمات مسدود شده:**\n\n{words_list}")793 794@admin_only795async def admin_system_info(update: Update, context: ContextTypes.DEFAULT_TYPE):796    """نمایش اطلاعات سیستم و منابع."""797    bot_start_time_str = data_manager.DATA.get('bot_start_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))798    try:799        bot_start_time = datetime.strptime(bot_start_time_str, '%Y-%m-%d %H:%M:%S')800        uptime = datetime.now() - bot_start_time801    except:802        uptime = "نامشخص"803    804    total_context_messages = sum(len(user.get('context', [])) for user in data_manager.DATA['users'].values())805    users_with_context = sum(1 for user in data_manager.DATA['users'].values() if user.get('context'))806    807    # تعداد کل ریپلای‌ها808    total_replies = 0809    for user in data_manager.DATA['users'].values():810        if 'context' in user:811            total_replies += sum(1 for msg in user['context'] if msg.get('has_reply', False))812    813    system_info = (814        f"💻 **اطلاعات سیستم:**\n\n"815        f"🖥️ سیستم‌عامل: {platform.system()} {platform.release()}\n"816        f"🐍 نسخه پایتون: {platform.python_version()}\n"817        f"💾 حافظه RAM استفاده شده: {psutil.virtual_memory().percent}%\n"818        f"💾 حافظه RAM آزاد: {psutil.virtual_memory().available / (1024**3):.2f} GB\n"819        f"💾 فضای دیسک استفاده شده: {psutil.disk_usage('/').percent}%\n"820        f"💾 فضای دیسک آزاد: {psutil.disk_usage('/').free / (1024**3):.2f} GB\n"821        f"⏱️ زمان اجرای ربات: {uptime}\n"822        f"💭 کاربران با context: {users_with_context}\n"823        f"💬 کل پیام‌های context: {total_context_messages}\n"824        f"📎 کل ریپلای‌های ثبت شده: {total_replies}"825    )826    827    await update.message.reply_text(system_info, parse_mode='Markdown')828 829@admin_only830async def admin_reset_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):831    """ریست کردن آمار ربات."""832    if not context.args:833        await update.message.reply_text("⚠️ لطفاً نوع آماری که می‌خواهید ریست کنید را مشخص کنید.\n"834                                       "مثال: `/reset_stats messages` یا `/reset_stats all`")835        return836    837    stat_type = context.args[0].lower()838    839    if stat_type == "messages":840        data_manager.DATA['stats']['total_messages'] = 0841        for user_id in data_manager.DATA['users']:842            data_manager.DATA['users'][user_id]['message_count'] = 0843        await update.message.reply_text("✅ آمار پیام‌ها با موفقیت ریست شد.")844    845    elif stat_type == "all":846        data_manager.DATA['stats'] = {847            'total_messages': 0,848            'total_users': len(data_manager.DATA['users']),849            'total_groups': len(data_manager.DATA['groups']),850            'avg_response_time': 0,851            'max_response_time': 0,852            'min_response_time': 0,853            'total_responses': 0854        }855        for user_id in data_manager.DATA['users']:856            data_manager.DATA['users'][user_id]['message_count'] = 0857        await update.message.reply_text("✅ تمام آمارها با موفقیت ریست شد.")858    859    else:860        await update.message.reply_text("⚠️ نوع آمار نامعتبر است. گزینه‌های موجود: messages, all")861        return862    863    data_manager.save_data()864 865# --- دستورات مدیریت context ---866@admin_only867async def admin_clear_context(update: Update, context: ContextTypes.DEFAULT_TYPE):868    """پاک کردن context یک کاربر"""869    if not context.args or not context.args[0].isdigit():870        await update.message.reply_text("⚠️ لطفاً آیدی کاربر را وارد کنید.\nمثال: `/clear_context 123456789`")871        return872    873    user_id = int(context.args[0])874    data_manager.clear_user_context(user_id)875    876    await update.message.reply_text(f"✅ context کاربر `{user_id}` با موفقیت پاک شد.")877 878@admin_only879async def admin_view_context(update: Update, context: ContextTypes.DEFAULT_TYPE):880    """مشاهده context یک کاربر"""881    if not context.args or not context.args[0].isdigit():882        await update.message.reply_text("⚠️ لطفاً آیدی کاربر را وارد کنید.\nمثال: `/view_context 123456789`")883        return884    885    user_id = int(context.args[0])886    user_context = data_manager.get_user_context(user_id)887    888    if not user_context:889        await update.message.reply_text(f"کاربر `{user_id}` context ندارد.")890        return891    892    context_text = f"📋 **Context کاربر `{user_id}`**\n\n"893    total_tokens = 0894    reply_count = 0895    896    for i, msg in enumerate(user_context, 1):897        role_emoji = "👤" if msg['role'] == 'user' else "🤖"898        tokens = data_manager.count_tokens(msg['content'])899        total_tokens += tokens900        901        # بررسی ریپلای902        has_reply = msg.get('has_reply', False)903        if has_reply:904            reply_count += 1905            reply_emoji = "📎"906        else:907            reply_emoji = ""908        909        content_preview = msg['content']910        if len(content_preview) > 100:911            content_preview = content_preview[:100] + "..."912        913        context_text += f"{i}. {role_emoji} {reply_emoji} **{msg['role']}** ({tokens} توکن)\n"914        context_text += f"   ⏰ {msg.get('timestamp', 'N/A')}\n"915        916        if has_reply:917            context_text += f"   📎 ریپلای به: {msg.get('reply_to_user_name', 'Unknown')}\n"918        919        context_text += f"   📝 {content_preview}\n\n"920    921    context_text += f"📊 **مجموع:** {len(user_context)} پیام، {total_tokens} توکن، {reply_count} ریپلای"922    923    await update.message.reply_text(context_text, parse_mode='Markdown')924 925@admin_only926async def admin_view_replies(update: Update, context: ContextTypes.DEFAULT_TYPE):927    """مشاهده ریپلای‌های یک کاربر یا گروه"""928    if len(context.args) < 2:929        await update.message.reply_text(930            "⚠️ فرمت صحیح: `/view_replies [user|group] [آیدی]`\n"931            "مثال: `/view_replies user 123456789`\n"932            "مثال: `/view_replies group -1001234567890`"933        )934        return935    936    entity_type = context.args[0].lower()937    entity_id = context.args[1]938    939    if entity_type == 'user':940        try:941            user_id = int(entity_id)942            user_context = data_manager.get_user_context(user_id)943            if not user_context:944                await update.message.reply_text(f"کاربر `{entity_id}` context ندارد.")945                return946            947            replies = [msg for msg in user_context if msg.get('has_reply', False)]948            949            if not replies:950                await update.message.reply_text(f"کاربر `{entity_id}` هیچ ریپلای‌ای ثبت نکرده است.")951                return952            953            text = f"📋 **ریپلای‌های کاربر `{entity_id}`**\n\n"954            for i, msg in enumerate(replies, 1):955                text += f"{i}. **زمان:** {msg.get('timestamp', 'N/A')}\n"956                text += f"   **ریپلای به:** {msg.get('reply_to_user_name', 'Unknown')}\n"957                if msg.get('is_reply_to_bot', False):958                    text += f"   **نوع:** ریپلای به ربات 🤖\n"959                else:960                    text += f"   **نوع:** ریپلای به کاربر\n"961                text += f"   **متن ریپلای شده:** {msg.get('reply_to_content', '')}\n"962                text += f"   **پاسخ کاربر:** {msg.get('content', '')[:100]}...\n\n"963            964            text += f"📊 **مجموع:** {len(replies)} ریپلای"965            966            await update.message.reply_text(text, parse_mode='Markdown')967        except ValueError:968            await update.message.reply_text("⚠️ آیدی کاربر باید عددی باشد.")969    970    elif entity_type == 'group':971        try:972            chat_id = int(entity_id)973            group_context = data_manager.get_group_context(chat_id)974            if not group_context:975                await update.message.reply_text(f"گروه `{entity_id}` context ندارد.")976                return977            978            replies = [msg for msg in group_context if msg.get('has_reply', False)]979            980            if not replies:981                await update.message.reply_text(f"گروه `{entity_id}` هیچ ریپلای‌ای ثبت نکرده است.")982                return983            984            text = f"📋 **ریپلای‌های گروه `{entity_id}`**\n\n"985            for i, msg in enumerate(replies, 1):986                user_name = msg.get('user_name', 'Unknown')987                text += f"{i}. **کاربر:** {user_name}\n"988                text += f"   **زمان:** {msg.get('timestamp', 'N/A')}\n"989                text += f"   **ریپلای به:** {msg.get('reply_to_user_name', 'Unknown')}\n"990                if msg.get('is_reply_to_bot', False):991                    text += f"   **نوع:** ریپلای به ربات 🤖\n"992                else:993                    text += f"   **نوع:** ریپلای به کاربر\n"994                text += f"   **متن ریپلای شده:** {msg.get('reply_to_content', '')}\n"995                text += f"   **پاسخ کاربر:** {msg.get('content', '')[:100]}...\n\n"996            997            text += f"📊 **مجموع:** {len(replies)} ریپلای"998            999            await update.message.reply_text(text, parse_mode='Markdown')1000        except ValueError:1001            await update.message.reply_text("⚠️ آیدی گروه باید عددی باشد.")1002    1003    else:1004        await update.message.reply_text("⚠️ نوع نامعتبر. فقط 'user' یا 'group' مجاز است.")1005 1006# --- دستورات جدید برای مدیریت گروه‌ها ---1007@admin_only1008async def admin_set_context_mode(update: Update, context: ContextTypes.DEFAULT_TYPE):1009    """تنظیم حالت context ربات"""1010    if not context.args or context.args[0].lower() not in ['separate', 'group_shared', 'hybrid']:1011        await update.message.reply_text(1012            "⚠️ فرمت صحیح: `/set_context_mode [separate|group_shared|hybrid]`\n\n"1013            "• **separate**: هر کاربر context جداگانه دارد (8192 توکن)\n"1014            "• **group_shared**: همه کاربران در یک گروه context مشترک دارند (16384 توکن)\n"1015            "• **hybrid**: ترکیبی از context شخصی و گروهی\n\n"1016            "💡 **توجه:** حالت group_shared حافظه بیشتری برای گروه‌ها فراهم می‌کند."1017        )1018        return1019    1020    mode = context.args[0].lower()1021    if data_manager.set_context_mode(mode):1022        await update.message.reply_text(f"✅ حالت context به `{mode}` تغییر یافت.")1023    else:1024        await update.message.reply_text("❌ خطا در تغییر حالت context.")1025 1026@admin_only1027async def admin_group_info(update: Update, context: ContextTypes.DEFAULT_TYPE):1028    """نمایش اطلاعات یک گروه"""1029    if not context.args or not context.args[0].lstrip('-').isdigit():1030        await update.message.reply_text("⚠️ لطفاً آیدی عددی گروه را وارد کنید.\nمثال: `/group_info -1001234567890`")1031        return1032    1033    chat_id = int(context.args[0])1034    group_info = data_manager.DATA['groups'].get(str(chat_id))1035    1036    if not group_info:1037        await update.message.reply_text(f"گروهی با آیدی `{chat_id}` در دیتابیس یافت نشد.")1038        return1039    1040    # تبدیل set اعضا به لیست برای نمایش1041    members = list(group_info.get('members', set()))1042    members_count = len(members)1043    1044    # اطلاعات context گروه1045    context_messages = len(group_info.get('context', []))1046    total_tokens = sum(data_manager.count_tokens(msg['content']) for msg in group_info.get('context', []))1047    1048    # تعداد ریپلای‌ها در گروه1049    reply_count = 01050    if 'context' in group_info:1051        reply_count = sum(1 for msg in group_info['context'] if msg.get('has_reply', False))1052    1053    text = (1054        f"📊 **اطلاعات گروه**\n\n"1055        f"🆔 **آیدی:** `{chat_id}`\n"1056        f"🏷️ **عنوان:** {group_info.get('title', 'N/A')}\n"1057        f"📝 **تعداد پیام‌ها:** `{group_info.get('message_count', 0)}`\n"1058        f"👥 **تعداد اعضا:** `{members_count}`\n"1059        f"💭 **پیام‌های context:** `{context_messages}`\n"1060        f"📎 **تعداد ریپلای‌ها:** `{reply_count}`\n"1061        f"🔢 **توکن‌های context:** `{total_tokens}`\n"1062        f"📅 **اولین فعالیت:** {group_info.get('first_seen', 'N/A')}\n"1063        f"🕒 **آخرین فعالیت:** {group_info.get('last_seen', 'N/A')}"1064    )1065    1066    await update.message.reply_text(text, parse_mode='Markdown')1067 1068@admin_only1069async def admin_clear_group_context(update: Update, context: ContextTypes.DEFAULT_TYPE):1070    """پاک کردن context یک گروه"""1071    if not context.args or not context.args[0].lstrip('-').isdigit():1072        await update.message.reply_text("⚠️ لطفاً آیدی گروه را وارد کنید.\nمثال: `/clear_group_context -1001234567890`")1073        return1074    1075    chat_id = int(context.args[0])1076    data_manager.clear_group_context(chat_id)1077    1078    await update.message.reply_text(f"✅ context گروه `{chat_id}` با موفقیت پاک شد.")1079 1080@admin_only1081async def admin_memory_status(update: Update, context: ContextTypes.DEFAULT_TYPE):1082    """نمایش وضعیت حافظه و context گروه‌ها"""1083    groups = data_manager.DATA['groups']1084    1085    if not groups:1086        await update.message.reply_text("هیچ گروهی در دیتابیس وجود ندارد.")1087        return1088    1089    # محاسبه آمار1090    total_groups = len(groups)1091    groups_with_context = sum(1 for g in groups.values() if g.get('context'))1092    total_context_messages = sum(len(g.get('context', [])) for g in groups.values())1093    total_context_tokens = 01094    total_replies = 01095    1096    for group_id, group_info in groups.items():1097        if 'context' in group_info:1098            total_context_tokens += sum(data_manager.count_tokens(msg['content']) for msg in group_info['context'])1099            total_replies += sum(1 for msg in group_info['context'] if msg.get('has_reply', False))1100    1101    # لیست گروه‌هایی که بیشترین استفاده از حافظه را دارند1102    groups_by_tokens = []1103    for group_id, group_info in groups.items():1104        if 'context' in group_info:1105            tokens = sum(data_manager.count_tokens(msg['content']) for msg in group_info['context'])1106            messages = len(group_info['context'])1107            replies = sum(1 for msg in group_info['context'] if msg.get('has_reply', False))1108            groups_by_tokens.append((group_id, tokens, messages, replies))1109    1110    # مرتب‌سازی بر اساس تعداد توکن1111    groups_by_tokens.sort(key=lambda x: x[1], reverse=True)1112    1113    status_text = (1114        f"💾 **وضعیت حافظه گروه‌ها**\n\n"1115        f"📊 **آمار کلی:**\n"1116        f"• تعداد کل گروه‌ها: `{total_groups}`\n"1117        f"• گروه‌های دارای context: `{groups_with_context}`\n"1118        f"• کل پیام‌های context: `{total_context_messages}`\n"1119        f"• کل ریپلای‌ها: `{total_replies}`\n"1120        f"• کل توکن‌های استفاده شده: `{total_context_tokens}`\n"1121        f"• میانگین توکن per گروه: `{total_context_tokens/max(1, groups_with_context):.0f}`\n\n"1122        f"🏆 **گروه‌های با بیشترین استفاده از حافظه:**\n"1123    )1124    1125    for i, (group_id, tokens, messages, replies) in enumerate(groups_by_tokens[:10], 1):1126        usage_percent = (tokens / 16384) * 1001127        status_text += f"{i}. گروه `{group_id}`: {messages} پیام، {replies} ریپلای، {tokens} توکن ({usage_percent:.1f}%)\n"1128    1129    await update.message.reply_text(status_text, parse_mode='Markdown')1130 1131# --- هندلر برای دکمه‌های صفحه‌بندی ---1132async def users_list_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):1133    """پردازش دکمه‌های صفحه‌بندی لیست کاربران."""1134    query = update.callback_query1135    await query.answer()1136    1137    if query.data.startswith("users_list:"):1138        page = int(query.data.split(":")[1])1139        context.args = [str(page)]1140        await admin_users_list(update, context)1141 1142# --- تابع برای پردازش ارسال‌های برنامه‌ریزی شده ---1143async def process_scheduled_broadcasts(context: ContextTypes.DEFAULT_TYPE):1144    """پردازش ارسال‌های برنامه‌ریزی شده و ارسال پیام‌ها در زمان مقرر."""1145    now = datetime.now()1146    broadcasts_to_send_indices = []1147    1148    for i, broadcast in enumerate(data_manager.DATA['scheduled_broadcasts']):1149        if broadcast['status'] == 'pending':1150            broadcast_time = datetime.strptime(broadcast['time'], '%Y-%m-%d %H:%M:%S')1151            if broadcast_time <= now:1152                broadcasts_to_send_indices.append(i)1153 1154    if not broadcasts_to_send_indices:1155        return1156    1157    user_ids = list(data_manager.DATA['users'].keys())1158    1159    for index in broadcasts_to_send_indices:1160        broadcast = data_manager.DATA['scheduled_broadcasts'][index]1161        message_text = broadcast['message']1162        total_sent, total_failed = 0, 01163        1164        for user_id_str in user_ids:1165            try:1166                await context.bot.send_message(chat_id=int(user_id_str), text=message_text)1167                total_sent += 11168                await asyncio.sleep(0.05)1169            except TelegramError as e:1170                logger.warning(f"Failed to send scheduled broadcast to {user_id_str}: {e}")1171                total_failed += 11172        1173        # به‌روزرسانی وضعیت ارسال1174        data_manager.DATA['scheduled_broadcasts'][index]['status'] = 'sent'1175        data_manager.DATA['scheduled_broadcasts'][index]['sent_time'] = now.strftime('%Y-%m-%d %H:%M:%S')1176        data_manager.DATA['scheduled_broadcasts'][index]['sent_count'] = total_sent1177        data_manager.DATA['scheduled_broadcasts'][index]['failed_count'] = total_failed1178        1179        logger.info(f"Scheduled broadcast sent: {total_sent} successful, {total_failed} failed")1180    1181    data_manager.save_data()1182 1183# --- تابع راه‌اندازی هندلرها ---1184def setup_admin_handlers(application):1185    """هندلرهای پنل ادمین را به اپلیکیشن اضافه می‌کند."""1186    # هندلرهای اصلی1187    application.add_handler(CommandHandler("commands", admin_commands))1188    application.add_handler(CommandHandler("stats", admin_stats))1189    application.add_handler(CommandHandler("broadcast", admin_broadcast))1190    application.add_handler(CommandHandler("targeted_broadcast", admin_targeted_broadcast))1191    application.add_handler(CommandHandler("schedule_broadcast", admin_schedule_broadcast))1192    application.add_handler(CommandHandler("list_scheduled", admin_list_scheduled_broadcasts))1193    application.add_handler(CommandHandler("remove_scheduled", admin_remove_scheduled_broadcast))1194    application.add_handler(CommandHandler("ban", admin_ban))1195    application.add_handler(CommandHandler("unban", admin_unban))1196    application.add_handler(CommandHandler("direct_message", admin_direct_message))1197    application.add_handler(CommandHandler("user_info", admin_userinfo))1198    application.add_handler(CommandHandler("logs", admin_logs))1199    application.add_handler(CommandHandler("logs_file", admin_logs_file))1200    application.add_handler(CommandHandler("users_list", admin_users_list))

Showing the first 1,200 of 1232 lines. Download the file for the rest.