User name is awinic. Current Date is Wednesday, October 23, 2024, current time is 06:34:32.import os
import re
import json
import logging
import asyncio
import aiohttp
import html
from aiogram import types, Router, filters
from aiogram.filters import Command
from aiogram.types import FSInputFile
import subprocess
import aiofiles

# Настраиваем логирование (переносим сюда, если используется отдельно)
logger = logging.getLogger(__name__)

# Создаем маршрутизатор
router = Router()

# Функция для выполнения системной команды
async def run_command(command: str) -> str:
    process = await asyncio.create_subprocess_shell(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    stdout, stderr = await process.communicate()
    return stdout.decode() if stdout else stderr.decode()


# Обрабатывает команду /start
@router.message(Command("start"))
async def cmd_start(message: types.Message):
    await message.answer(
    "Привет! Я бот, который может выполнять команды dig, whois, traceroute, ping, curl, wget и другие.\n"
    "Используйте следующие команды:\n"
    "/dig <домен> - Выполнить команду dig для домена\n"
    "/whois <домен> - Получить информацию whois для домена\n"
    "/traceroute <хост> - Выполнить команду traceroute для хоста\n"
    "/ping <хост> - Проверить доступность хоста\n"
    "/curl <URL> - Выполнить запрос curl\n"
    "/wget <URL> - Скачать файл по URL\n"
    "/myip - Узнать свой IP-адрес\n"
    "/ip <IP-адрес> - Получить информацию об IP-адресе\n"
    "/nmap <хост> - Получить открытые порты хоста\n"
    "/sslscan <домен> - Выполнить сканирование SSL для домена"
)
    logger.info(f"Команда /start от пользователя {message.from_user.id}")


# Функция для симуляции "печатает" активность
async def send_typing_action(message: types.Message, action_type='typing'):
    await message.bot.send_chat_action(message.chat.id, action=action_type)


# Обрабатывает команду /dig
@router.message(Command("dig"))
async def cmd_dig(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите домен.")
        return
    domain = message.text.split(maxsplit=1)[1]
    response = await run_command(f"dig {domain}")

    if response.strip():
        await message.answer(f"<pre>{response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")
    logger.info(f"Команда /dig от пользователя {message.from_user.id} с аргументом: {domain}")

# Обрабатывает команду /whois
@router.message(Command("whois"))
async def cmd_whois(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите домен.")
        return
    domain = message.text.split(maxsplit=1)[1]
    response = await run_command(f"whois {domain}")

    if response.strip():
        await message.answer(f"<pre>{response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")
    logger.info(f"Команда /whois от пользователя {message.from_user.id} с аргументом: {domain}")

# Обрабатывает команду /traceroute
@router.message(Command("traceroute"))
async def cmd_traceroute(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите хост.")
        return
    host = message.text.split(maxsplit=1)[1]
    response = await run_command(f"traceroute {host}")

    if response.strip():
        await message.answer(f"<pre>{response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")
    logger.info(f"Команда /traceroute от пользователя {message.from_user.id} с аргументом: {host}")

# Обрабатывает команду /ping
@router.message(Command("ping"))
async def cmd_ping(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите хост.")
        return
    host = message.text.split(maxsplit=1)[1]
    response = await run_command(f"ping -c 4 {host}")

    if response.strip():
        await message.answer(f"<pre>{response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")
    logger.info(f"Команда /ping от пользователя {message.from_user.id} с аргументом: {host}")
    
@router.message(Command("curl"))
async def cmd_curl(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите URL.")
        return
    url = message.text.split(maxsplit=1)[1]
    
    if not re.match(r'http(s)?://', url):
        url = f"http://{url}"

    response = await run_command(f"curl -s {url}")
    stderr = await run_command(f"echo $?")  # Get the exit code

    if stderr.strip() != "0":
        logger.error(f"Ошибка curl: {stderr.decode()}")
        await message.answer("Ошибка: не удалось выполнить запрос curl.")
        return

    if response.strip():
        if len(response) <= 4000:
            await message.answer(f"<pre>{response}</pre>", parse_mode="HTML")
        else:
            with open('response.txt', 'w') as f:
                f.write(response)
            input_file = FSInputFile('response.txt')
            await message.answer_document(input_file, caption="Ответ слишком большой, вот файл.")
            os.remove('response.txt')
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")

# Обрабатывает команду /wget
@router.message(Command("wget"))
async def cmd_wget(message: types.Message):
    await send_typing_action(message, action_type='upload_document')
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите URL для загрузки.")
        return

    url = message.text.split(maxsplit=1)[1]

    if not re.match(r'http(s)?://', url):
        url = f"http://{url}"

    try:
        async with aiohttp.ClientSession() as session:
            async with session.head(url) as response:
                if response.status == 200:
                    content_length = response.headers.get('Content-Length')
                    if content_length:
                        file_size = int(content_length)
                        if file_size > 45 * 1024 * 1024:
                            await message.answer(f"Ошибка: размер файла {file_size / (1024 * 1024):.2f} МБ превышает лимит 45 МБ.")
                            return
                    else:
                        await message.answer("Ошибка: заголовок Content-Length отсутствует.")
                        return
                else:
                    await message.answer(f"Ошибка: не удалось получить информацию о файле. Статус: {response.status}")
                    return

            async with session.get(url) as response:
                if response.status == 200:
                    file_data = await response.read()
                    if len(file_data) > 45 * 1024 * 1024:
                        await message.answer("Ошибка: размер файла превышает 45 МБ после загрузки.")
                        return

                    async with aiofiles.tempfile.NamedTemporaryFile("wb", delete=False) as temp_file:
                        temp_file_name = temp_file.name
                        await temp_file.write(file_data)

                    input_file = FSInputFile(temp_file_name, filename=os.path.basename(url))
                    await message.answer_document(input_file)

                    os.remove(temp_file_name)
                else:
                    await message.answer(f"Ошибка: не удалось загрузить файл. Статус: {response.status}")
    except Exception as e:
        await message.answer(f"Ошибка: {str(e)}")
    logger.info(f"Команда /wget от пользователя {message.from_user.id} с URL: {url}")

# Обрабатывает команду /myip
@router.message(Command("myip"))
async def cmd_myip(message: types.Message):
    web_app_info = types.WebAppInfo(url="https://ifconfig.me/")
    button = types.InlineKeyboardButton(text="Узнать свой IP", web_app=web_app_info)
    markup = types.InlineKeyboardMarkup(inline_keyboard=[[button]])

    await message.answer("Нажмите на кнопку ниже, чтобы узнать свой IP:", reply_markup=markup)

# Обрабатывает команду /ip
@router.message(Command("ip"))
async def cmd_ip(message: types.Message):
    await send_typing_action(message)
    args = message.text.split()
    
    if len(args) < 2:
        await message.answer("Пожалуйста, укажите IP-адрес.")
        return

    ip_address = args[1]
    url = f"https://ipinfo.io/{ip_address}"
    
    try:
        response = await run_command(f"curl -s {url}")

        if response.strip():
            try:
                json_data = json.loads(response)
                if json_data:
                    formatted_data = json.dumps(json_data, indent=4)
                    await message.answer(f"Информация по IP {ip_address}:\n<pre>{formatted_data}</pre>", parse_mode="HTML")
                else:
                    await message.answer("Ошибка: не удалось получить данные по указанному IP.")
            except json.JSONDecodeError:
                await message.answer(f"Ошибка: некорректный ответ от сервера для IP {ip_address}.")
        else:
            await message.answer("Ошибка: не удалось получить данные по адресу IP.")
    except Exception as e:
        await message.answer(f"Ошибка: {str(e)}")
    logger.info(f"Команда /ip от пользователя {message.from_user.id} с IP: {ip_address}")

@router.message(Command("nmap"))
async def cmd_nmap(message: types.Message):
    await send_typing_action(message)

    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите домен и/или аргументы для сканирования.")
        return

    # Получаем аргументы после команды /nmap
    arguments = message.text.split(maxsplit=1)[1]

    # Список флагов, которые требуют root прав
    root_required_flags = ['-sA', '-sM', '-sU']

    # Проверка на наличие флагов, требующих root прав
    if any(flag in arguments for flag in root_required_flags):
        await message.answer("<pre>Ошибка: команда не может быть выполнена без root доступа.</pre>", parse_mode="HTML")
        return

    try:
        # Устанавливаем таймаут на выполнение команды
        response = await asyncio.wait_for(run_command(f"nmap {arguments} 2>/dev/null | sed '/^Starting Nmap/d'"), timeout=60.0)
    except asyncio.TimeoutError:
        await message.answer("Ошибка: выполнение команды превысило лимит времени (60 секунд).")
        return

    # Ограничиваем длину ответа до 4000 символов
    if len(response) > 4000:
        response = response[:4000] + "\n... (Ответ обрезан до 4000 символов)"

    # Проверяем, есть ли ответ, и отправляем его
    if response.strip():
        safe_response = html.escape(response)
        await message.answer(f"<pre>{safe_response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")

    logger.info(f"Команда /nmap от пользователя {message.from_user.id} с аргументами: {arguments}")


# Функция для удаления ANSI-кодов
def remove_ansi_escape_sequences(text: str) -> str:
    ansi_escape = re.compile(r'\x1B\[[0-?9;]*[mK]')
    return ansi_escape.sub('', text)

# Обрабатывает команду /sslscan
@router.message(Command("sslscan"))
async def cmd_sslscan(message: types.Message):
    await send_typing_action(message)
    if len(message.text.split()) < 2:
        await message.answer("Пожалуйста, укажите домен.")
        return
    domain = message.text.split(maxsplit=1)[1]

    try:
        response = await asyncio.wait_for(run_command(f"sslscan {domain} 2>/dev/null"), timeout=30)
    except asyncio.TimeoutError:
        await message.answer("Ошибка: выполнение команды превысило лимит времени (30 секунд).")
        return

    # Удаляем ANSI-коды из ответа
    response = remove_ansi_escape_sequences(response)

    # Ограничиваем длину ответа до 4000 символов
    if len(response) > 4000:
        response = response[:4000] + "\n... (Ответ обрезан до 4000 символов)"

    if response.strip():
        safe_response = html.escape(response)
        await message.answer(f"<pre>{safe_response}</pre>", parse_mode="HTML")
    else:
        await message.answer("Ошибка: ответ от сервера пустой или недоступен.")
    
    logger.info(f"Команда /sslscan от пользователя {message.from_user.id} с аргументом: {domain}")

# Обновляем список доступных команд
commands_list = (
    "/start - Приветствие и доступные команды\n"
    "/dig <домен> - Выполнить команду dig для домена\n"
    "/whois <домен> - Получить информацию whois для домена\n"
    "/traceroute <хост> - Выполнить команду traceroute для хоста\n"
    "/ping <хост> - Проверить доступность хоста\n"
    "/curl <URL> - Выполнить запрос curl\n"
    "/wget <URL> - Скачать файл по URL\n"
    "/myip - Узнать свой IP-адрес\n"
    "/ip <IP-адрес> - Получить информацию об IP-адресе\n"
    "/nmap <хост> - Получить открытые порты хоста\n"
    "/sslscan <домен> - Просканировать SSL-сертификаты домена"
)

# Хендлер для нераспознанных команд
@router.message()
async def handle_unknown_command(message: types.Message):
    await send_typing_action(message)
    await message.answer(f"Неизвестная команда. Доступные команды:\n{commands_list}")
    logger.info(f"Нераспознанная команда от пользователя {message.from_user.id}: {message.text}")