# آمار یکتای ادمین‌ها

## فایل و محل تغییر

فقط کد `bot.php` تغییر کرده است. `bot2.php` و مسیر Trailer دست‌نخورده‌اند. این راهنما جایگزین توضیح قبلی آمار است.
آمار اصلی همچنان در Handler موجود `panel_(...)`، شاخهٔ `stats` قرار دارد. دکمهٔ «👨‍💼 آمار ادمین‌ها» با callback `admin_stats_day_1` به همان منو متصل است.

## منوی دو سطحی

۱. `admin_stats_day_{page}` فهرست ادمین‌های مجاز را با پنج ادمین در هر صفحه نمایش می‌دهد. ادمین بدون محتوا نیز نمایش داده می‌شود. ترتیب شناسه‌ها ثابت است.
۲. هر دکمه `admin_stat_{admin_id}_{period}_{page}` آمار اختصاصی همان ادمین را باز می‌کند. period یکی از day/week/month است.
۳. کل فیلم‌های یکتا، کل سریال‌های یکتا، آمار امروز/هفته/ماه و تعداد عنوان‌های فاقد تاریخ اولیه نمایش داده می‌شوند. انتخاب بازه، ردیف مربوطه را برجسته می‌کند.
۴. دکمهٔ برگشت شمارهٔ صفحهٔ فهرست را حفظ می‌کند. callbackهای فهرست قدیمی نیز همچنان معتبرند.

## مدل و منبع اطلاعات

مدل ORM یا جدول جدید ایجاد نشده است. منبع همان جدول `file` است:

- `user_id`: شناسهٔ ثبت‌کننده در INSERTهای موجود از `$from_id` می‌آید. در Query با نام `added_by` نمایش داده می‌شود؛ ستون added_by ایجاد نشده است.
- `imdb_id`: شناسهٔ واقعی محتوا؛ کیفیت، فصل و قسمت کلید یکتای عنوان نیستند.
- `season`، `episode` و الگوی `_S[0-9]+` در `id`: همان شواهد موجود برای تشخیص سریال.
- `added_at`: زمان درج تغییرناپذیر که در نسخهٔ قبلی اضافه شده بود؛ این اصلاح ستون جدیدی اضافه نمی‌کند. نصب اولیهٔ آن همچنان توسط `adminStatsEnsureTimestamp` انجام می‌شود: ابتدا TIMESTAMP NULL DEFAULT NULL، سپس DEFAULT CURRENT_TIMESTAMP برای درج‌های بعدی. رکوردهای قدیمی NULL باقی می‌مانند.
- `date` و `time`: در چند مسیر ویرایش تغییر می‌کنند؛ بنابراین مبنای تاریخ ثبت اولیه نیستند.

مسیرهای بررسی‌شده شامل INSERTهای فیلم/سریال/کیفیت در آپلود معمولی، auto_add، ثبت قسمت و admin_track_add_files، تغییر caption/date/time و اتصال IMDb به فایل موجود بود. UPDATEهای بررسی‌شده user_id یا added_at را تغییر نمی‌دهند. ثبت و ویرایش فعلی تغییر نکرده است.

## Deduplication قبل از فیلتر زمانی

هویت شمارش، `(user_id, LOWER(TRIM(imdb_id)))` است. ابتدا تمام رکوردهای موجود این عنوان برای این ادمین یکپارچه می‌شوند؛ سپس بازهٔ زمانی اعمال می‌شود. کلیدهای IMDb نامعتبر یا خالی به‌عنوان یک عنوان حدسی شمرده نمی‌شوند.

زیرQuery واقعی:

```sql
SELECT user_id AS added_by,
       LOWER(TRIM(imdb_id)) AS imdb_id,
       MAX(CASE WHEN (
           COALESCE(season,0)>0 OR COALESCE(episode,0)>0
           OR id REGEXP '_S[0-9]+'
       ) THEN 1 ELSE 0 END) AS is_series,
       CASE WHEN SUM(added_at IS NULL)>0 THEN NULL
            ELSE MIN(UNIX_TIMESTAMP(added_at)) END AS first_added
FROM file
WHERE user_id IN (?)
  AND type IN ('video','document')
  AND LOWER(TRIM(imdb_id)) REGEXP '^tt[0-9]+$'
GROUP BY user_id, LOWER(TRIM(imdb_id))
```

علامت‌های سؤال به تعداد ادمین‌های درخواستی ساخته و bind می‌شوند. صفحهٔ اختصاصی فقط همان یک ادمین را Query می‌کند.

Query بیرونی روی خروجی بالا:

```sql
SELECT added_by,
 COUNT(DISTINCT CASE WHEN is_series=0 THEN imdb_id END) AS movie_total,
 COUNT(DISTINCT CASE WHEN is_series=1 THEN imdb_id END) AS series_total,
 COUNT(DISTINCT CASE WHEN first_added IS NULL THEN imdb_id END) AS unknown_time,
 COUNT(DISTINCT CASE WHEN is_series=0
   AND first_added>=? AND first_added<? THEN imdb_id END) AS movie_day,
 COUNT(DISTINCT CASE WHEN is_series=1
   AND first_added>=? AND first_added<? THEN imdb_id END) AS series_day
FROM ( /* زیرQuery بالا */ ) AS content
GROUP BY added_by
```

همین دو عبارت شرطی برای `movie_week / series_week` و `movie_month / series_month` ساخته می‌شوند. از COUNT(*) استفاده نمی‌شود. SUM در زیرQuery فقط وجود تاریخ نامعلوم را تشخیص می‌دهد، نه تعداد محتوا را.

نتیجه: یک Add و دو Update برای tt1111111 و یک Add برای tt2222222، دو فیلم است. قسمت‌ها و کیفیت‌های متعدد یک عنوان، یک محتوا هستند. اگر عنوان در ماه قبل ثبت شده باشد، کیفیت جدید امروز آمار ثبت امروز را افزایش نمی‌دهد. اگر یک IMDb در برخی رکوردها نشانهٔ سریال داشته باشد، در گروه همان ادمین فقط سریال شمرده می‌شود، نه هم‌زمان فیلم و سریال.

## محتوای قدیمی و حدود بازیابی

تمام عنوان‌های معتبر موجود، حتی بدون added_at، در آمار کل لحاظ می‌شوند. اگر حتی یک رکورد قدیمی عنوان فاقد تاریخ اولیه باشد، زمان اولین ثبت آن قابل اثبات نیست؛ عنوان در کل می‌ماند اما به امروز/هفته/ماه نسبت داده نمی‌شود. تاریخ آخرین ویرایش جای تاریخ اولیه قرار نمی‌گیرد.

اگر رکوردهای قدیمی قبلاً حذف شده باشند یا ثبت‌کنندهٔ آن‌ها در نسخه‌ای دیگر بازنویسی شده باشد، دیتابیس فعلی به‌تنهایی تاریخچه را بازیابی نمی‌کند. برای چنین بازیابی‌ای نسخهٔ پشتیبان یا لاگ معتبر لازم است. این قابلیت شمارش محتوای موجود است، نه دفتر تاریخچهٔ مستقلِ مقاوم در برابر حذف همهٔ رکوردها. هیچ تاریخ یا مالکیتی حدس زده نشده است.

## بازه‌ها و دسترسی

`adminStatsWindows`: منطقهٔ زمانی Asia/Tehran، روز از ساعت ۰۰:۰۰، هفته از شنبه، ماه از ابتدای ماه میلادی مطابق ساختار فعلی. انتهای بازه timestamp اکنون + یک ثانیه و مقایسهٔ انتها `<` است. UNIX_TIMESTAMP روی ستون TIMESTAMP برای مقایسه با epoch استفاده می‌شود.

هر callback دوباره private بودن چت، تطابق chat_id با from_id، isAdmin، hasPermission(stats) و مسدود نبودن را بررسی می‌کند. target نیز باید در getAllAdminIds موجود باشد. نام‌ها برای HTML escape می‌شوند و مقادیر Query bind می‌شوند.

## توابع

- اصلاح `adminStatsAggregateQuery`: یکپارچه‌سازی قبل از زمان، اولین ثبت، کل محتوای تاریخی و تاریخ نامشخص.
- اصلاح `adminStatsPage`: صفحه‌بندی ثابت فهرست و مقدار پیش‌فرض آمار صفر.
- اصلاح `adminStatsKeyboard`: دکمهٔ هر ادمین، بازه‌های اختصاصی، حفظ شمارهٔ صفحه.
- اصلاح `adminStatsHandle`: مسیریابی دو سطح، اعتبارسنجی target و نمایش آمار اختصاصی.
- افزودن `adminStatsName`: استفادهٔ مشترک از نام ادمین و کش موجود نام‌ها.
- بدون تغییر: `adminStatsEnsureTimestamp`، `adminStatsWindows`، منطق ثبت فایل، Handler اصلی آمار و مجوزهای مرکزی.

## اعتبارسنجی

بررسی نحو کامل PHP موفق بود. ده تست PHP برای پارامترهای Query، صفحه‌بندی، callbackها، بازگشت، دسترسی و target نامعتبر موفق بود. SQL تولیدشده با دادهٔ آزمایشی در SQLite و توابع سازگار REGEXP و UNIX_TIMESTAMP اجرا شد: مثال ۲ به‌جای ۴، جداسازی ادمین‌ها، قسمت‌های سریال، کیفیت جدید عنوان قدیمی، رکورد قدیمی بدون تاریخ و ویرایش مجدد موفق بود. این تست جای اجرای واقعی روی MySQL سرور شما نیست؛ دیتابیس واقعی و Telegram در دسترس این آزمون نبودند.
