s

🧩 Syntax:
import asyncio
import json
import os
import random
import aiogram.types
from aiogram.exceptions import TelegramBadRequest
from telethon import TelegramClient, functions, types
import text
import db

# Полная карта всех возможных репортов
REPORT_MAP = {
    'idontlikeit': b'1:c',
    'childabuse': b'2:c',
    'violence': b'3:c',
    'illegalgoods': {
        'main': b'4',
        'weapons': b'41:c',
        'drugs': b'42:c',
        'fakedocs': b'43:c',
        'counterfeit': b'44:c',
        'othergoods': b'45:c'
    },
    'illegaladultcontent': {
        'main': b'5',
        'childabuse': b'51:c',
        'nonconsensual': b'52:c',
        'otherillegal': b'53:c'
    },
    'personaldata': {
        'main': b'6',
        'privateimages': b'61:c',
        'phonenumber': b'62:c',
        'address': b'63:c',
        'otherinfo': b'64:c'
    },
    'terrorism': b'7:c',
    'scamorspam': {
        'main': b'8',
        'phishing': b'81:c',
        'impersonation': b'82:c',
        'fraud': b'83:c',
        'spam': b'84:c'
    },
    'copyright': b'9:c',
    'other': b'a:c',
    'notillegalmbtd': b'b:c'
}

# Быстрые пресеты
QUICK_PRESETS = {
    'scam': 'scamorspam>fraud',
    'spam': 'scamorspam>spam',
    'drugs': 'illegalgoods>drugs',
    'porn': 'illegaladultcontent>otherillegal',
    'personal': 'personaldata>otherinfo'
}

api_id = 11876346
api_hash = '954ec954bedb3afbf649304a5b918553'


async def mass_bot_report(
        bot_username: str,
        msg_to_target: str,
        report_reason: str,
        report_message_string: str,
        message: aiogram.types.Message,
        ogtext=None,
        channels: list = None
) -> list:
    """
    Улучшенная mass_bot_report с поддержкой:
    - Публичных каналов (@username)
    - Приватных каналов (t.me/+invite)
    - Прямых ссылок (t.me/xxx)
    - Отключение уведомлений от бота и удаление их после
    - Автоматическая отписка от каналов после завершения атаки
    - Пометка сообщений как прочитанных после репорта
    """
    report_path = QUICK_PRESETS.get(report_reason, report_reason)
    sessions = db.getReadySessions()
    success, fail = 0, 0
    status_lines = []
    og_text = message.text if not ogtext else ogtext
    current_message = message  # Сообщение для вывода логов

    for session_file, proxy_str in sessions:
        try:
            proxy_dict = text.parseProxyFromString(proxy_str)
            client = TelegramClient(session_file, api_id, api_hash, proxy=proxy_dict)
            await client.connect()
            # 2) Быстро проверяем, авторизована ли сессия
            if not await client.is_user_authorized():
                status_lines.append(f"<b>🖧 {session_file}</b> — <code>Unauthorized</code>")
                fail += 1
                await client.disconnect()  # сразу отключаемся
                continue  # пропускаем эту сессию

            # Блок подписок с улучшенной обработкой ссылок
            sub_info = []
            joined_channels = []
            if channels:
                for channel in channels:
                    try:
                        if channel.startswith(('https://t.me/+', 't.me/+')):
                            invite_hash = channel.split('+')[-1]
                            await client(functions.messages.ImportChatInviteRequest(invite_hash))
                            sub_info.append(f"✓ [приватный t.me/+{invite_hash[:5]}...]")
                            joined_channels.append(invite_hash)
                        elif channel.startswith(('https://t.me/', 't.me/')):
                            username = channel.split('t.me/')[-1]
                            entity = await client.get_entity(username)
                            await client(functions.channels.JoinChannelRequest(entity))
                            sub_info.append(f"✓ t.me/{username}")
                            joined_channels.append(entity)
                        elif channel.startswith('@'):
                            entity = await client.get_entity(channel)
                            await client(functions.channels.JoinChannelRequest(entity))
                            sub_info.append(f"✓ {channel}")
                            joined_channels.append(entity)
                        else:
                            sub_info.append(f"✗ {channel} (неподдерживаемый формат)")
                        await asyncio.sleep(random.uniform(1, 3))
                    except Exception as e:
                        sub_info.append(f"✗ {channel} ({str(e)[:50]})")
            # Основная логика репорта
            bot_entity = await client.get_input_entity(bot_username)
            # Отключаем уведомления
            try:
                await client(functions.account.UpdateNotifySettingsRequest(
                    peer=bot_entity,
                    settings=types.InputPeerNotifySettings(mute_until=2 ** 31 - 1)
                ))
            except Exception as e:
                status_lines.append(f"⚠️ Не удалось отключить уведомления: {str(e)[:50]}")
            await client.send_message(bot_entity, msg_to_target)
            await asyncio.sleep(3)
            target_message = (await client.get_messages(bot_entity, limit=1))[0]
            msg_preview = target_message.text[:15] + "..." if target_message.text else "[медиа]"
            comment = text.unpackBotnetString(report_message_string) if report_message_string != "notext" else ''
            result = await client(functions.messages.ReportRequest(
                peer=bot_entity,
                id=[target_message.id],
                option=REPORT_MAP[report_path.split('>')[0]][report_path.split('>')[1]] if '>' in report_path else
                REPORT_MAP[report_path],
                message=comment
            ))
            # Пометка как прочитанное
            try:
                await client(functions.messages.ReadHistoryRequest(
                    peer=bot_entity,
                    max_id=target_message.id
                ))
            except Exception as e:
                status_lines.append(f"⚠️ Не удалось пометить как прочитанное: {str(e)[:50]}")
            status_lines.append(
                f"<b>🖧 {session_file}</b>\n"
                f" <b>├  Подписки: </b><code>{', '.join(sub_info) if sub_info else 'нет'}</code>\n"
                f" <b>├  Report:</b> <code>отправлен (тип: {report_path})</code>\n"
                f" <b>├  Message: </b><code>{msg_preview}</code>\n"
                f" <b>└  Response: </b><code>{result}</code>\n"
                f"<blockquote>{comment}</blockquote>"
            )
            success += 1
            # Отписка от каналов после
            for entity in joined_channels:
                try:
                    if isinstance(entity, str):  # приватный канал (invite_hash)
                        try:
                            chat = await client.get_entity(f'https://t.me/+{entity}')
                            await client(functions.channels.LeaveChannelRequest(chat))
                        except Exception as e:
                            status_lines.append(f"⚠️ Не удалось отписаться от приватного канала: {str(e)[:50]}")
                    else:
                        await client(functions.channels.LeaveChannelRequest(entity))
                except Exception as e:
                    status_lines.append(f"⚠️ Не удалось отписаться от канала: {str(e)[:50]}")
            await client.disconnect()

        except Exception as e:
            fail += 1
            status_lines.append(
                f"<b>🖧 {session_file}</b>\n"
                f" <b>└  Ошибка: </b><code>{str(e)[:100]}</code>"
            )
            continue

        try:
            await current_message.edit_text(
                f"{og_text}\n" + "\n\n".join(status_lines[-5:]),  # Показываем только последние 5 записей
                parse_mode="HTML"
            )
        except TelegramBadRequest as e:
            if "MESSAGE_TOO_LONG" in str(e):
                # Если сообщение слишком длинное, отправляем новое и продолжаем в нем
                current_message = await current_message.answer(
                    "<b>Продолжение лога:</b>\n" + "\n\n".join(status_lines[-5:]),
                    parse_mode="HTML"
                )
            else:
                raise

        await asyncio.sleep(random.uniform(2, 5))

    # Финальный отчет после обработки всех сессий
    try:
        await current_message.edit_text(
            f"{og_text}\n" + "\n\n".join(status_lines[-10:]) +  # Последние 10 записей
            f"\n\n<b>Итог:</b> Успешно — <code>{success}</code>, Ошибок — <code>{fail}</code>",
            parse_mode="HTML"
        )
    except TelegramBadRequest as e:
        if "MESSAGE_TOO_LONG" in str(e):
            part1 = "\n\n".join(status_lines[:len(status_lines) // 2])
            part2 = "\n\n".join(status_lines[len(status_lines) // 2:])

            await current_message.edit_text(
                f"{og_text}\n{part1}\n\n<b>MESSAGE_TOO_LONG, продолжение в новом сообщении...</b>",
                parse_mode="HTML"
            )
            await current_message.answer(
                f"{part2}\n\n<b>Итог:</b> Успешно — <code>{success}</code>, Ошибок — <code>{fail}</code>",
                parse_mode="HTML"
            )

    await db.addBotnetHistoryRecord(target=bot_username, report_type=report_path, sf=f"{success}/{fail}",
                                    comment_str=report_message_string)
    return [success, fail]


# Причины для репорта профиля
PROFILE_REPORT_PRESETS = {
    'spam': types.InputReportReasonSpam,
    'violence': types.InputReportReasonViolence,
    'porn': types.InputReportReasonPornography,
    'childabuse': types.InputReportReasonChildAbuse,
    'fake': types.InputReportReasonFake,
    'drugs': types.InputReportReasonIllegalDrugs,
    'personal': types.InputReportReasonPersonalDetails,
    'other': types.InputReportReasonOther,
}


async def mass_bot_profile_report(
    bot_username: str,
    report_reason: str,
    report_message: str,
    message: aiogram.types.Message,
    concurrent_limit: int = 10
) -> list[int]:
    ReasonClass = PROFILE_REPORT_PRESETS.get(report_reason)
    if not ReasonClass:
        valid = ', '.join(PROFILE_REPORT_PRESETS.keys())
        raise ValueError(f"Неизвестная причина репорта: {report_reason}. Валид: {valid}")

    sessions = db.getReadySessions()
    sem = asyncio.Semaphore(concurrent_limit)
    results = []

    async def worker(session_file, proxy_str):
        async with sem:
            # Создаем клиент и подключаемся без контекстного менеджера
            client = TelegramClient(
                session_file,
                api_id=11876346,
                api_hash='954ec954bedb3afbf649304a5b918553',
                proxy=text.parseProxyFromString(proxy_str)
            )
            await client.connect()

            # Проверяем, авторизована ли сессия
            if not await client.is_user_authorized():
                await client.disconnect()
                return session_file, False, f"❌ Unauthorized"

            try:
                entity = await client.get_input_entity(bot_username)
                comment = text.unpackBotnetString(report_message) if report_message != "notext" else ''
                resp = await client(functions.account.ReportPeerRequest(
                    peer=entity,
                    reason=ReasonClass(),
                    message=comment
                ))
                log = f"✅ Reported: {resp} | <i>{comment}</i>"
                success = True
            except Exception as e:
                log = f"❌ {e!r}"
                success = False

            # Отключаемся после работы
            await client.disconnect()
            return session_file, success, log

    tasks = [worker(sess, proxy) for sess, proxy in sessions]
    for coro in asyncio.as_completed(tasks):
        session_file, ok, log = await coro
        results.append((session_file, ok, log))
        tail = results[-5:]
        chunk = "\n\n".join(f"<b>{s}</b> — {'✅' if ok else '❌'} <blockquote>{l}</blockquote>" for s, ok, l in tail)
        try:
            await message.edit_text(f"{message.text}\n\n{chunk}", parse_mode="HTML")
        except TelegramBadRequest:
            pass

    good = sum(1 for _, ok, _ in results if ok)
    bad = len(results) - good
    summary = "\n".join(
        f"<b>{s}</b> — {'✅' if ok else '❌'} <blockquote>{l.split('|')[-1].strip()}</blockquote>"
        for s, ok, l in results[-10:]
    )
    await message.edit_text(
        f"{message.text}\n\n{summary}\n\n<b>Итог:</b> Успех {good}, Ошибок {bad}",
        parse_mode="HTML"
    )

    await db.addBotnetHistoryRecord(
        target=bot_username,
        report_type=f"profile:{report_reason}",
        sf=f"{good}/{bad}",
        comment_str=report_message
    )

    return [good, bad]

async def recheckSessions() -> str:
    sessions_casual, file_sessions = db.getReadySessions(), db.getBotnetSesh()
    c_status_lines, s_status_lines = [], []
    cs, cf = 0, 0
    ss, sf = 0, 0
    for session_file, proxy_str in sessions_casual:
        proxy_dict = text.parseProxyFromString(proxy_str)
        type, login, password, ip, port = [*proxy_str.split("@")[0].split(":"), *proxy_str.split("@")[1].split(":")]

        if not (await text.checkProxy(type, login, password, ip, port)).get('success'):
            c_status_lines.append(f"<b>🟡 Proxy unavail. {session_file}</b>")
            cf += 1
            continue
        client = TelegramClient(session_file, api_id, api_hash, proxy=proxy_dict)
        await client.connect()

        if not await client.is_user_authorized():
            c_status_lines.append(f"<b>🔴 {session_file}</b>")
            cf += 1
            await client.disconnect()
            continue
        c_status_lines.append(f"<b>🟢 {session_file}</b>")
        cs += 1
    _cr = '\n'.join(i for i in c_status_lines)
    casualRecheckResult = f"<b>Casual-сессий {len(sessions_casual)}</b>\n" \
                          f"<blockquote expandable>{_cr}</blockquote>"

    for session, phone, user_id, app_id, app_hash, sdk, app_version, device, name, lang_code, system_lang_code, twoFA, proxy in file_sessions:
        type, login, password, ip, port = [*proxy.split("@")[0].split(":"), *proxy.split("@")[1].split(":")]

        if not (await text.checkProxy(type, login, password, ip, port)).get('success'):
            s_status_lines.append(f"<code>🟡 Proxy unavail. {phone}</code>")
            sf += 1
            continue
        info = json.load(open(session, 'r', encoding='utf-8'))
        session_file = info['session_file']
        session_full = os.path.join(os.path.dirname(session), session_file)

        client = TelegramClient(
            session_full,
            info['app_id'],
            info['app_hash'],
            device_model=info.get('sdk'),
            system_version=info.get('sdk'),
            app_version=info.get('app_version'),
            lang_code=info.get('lang_code'),
            system_lang_code=info.get('system_lang_code'),
            proxy=text.parseProxyFromString(proxy)
        )

        await client.connect()
        if not await client.is_user_authorized():
            s_status_lines.append(f"<code>🔴 SS: {phone}</code>")
            sf += 1
            await client.disconnect()
            continue
        s_status_lines.append(f"<code>🟢 SS: {phone}</code>")
        ss += 1
        _sr = '\n'.join(i for i in s_status_lines)
        seshRecheckResult = f"<b>File-сессий {len(file_sessions)}</b>\n" \
                              f"<blockquote expandable>{_sr}</blockquote>"

        return f"{casualRecheckResult}\n\n{seshRecheckResult}"
Guest

Guest