User name is awinic. Current Date is Wednesday, October 23, 2024, current time is 09:48:10.import os
import re
import json
import logging
import asyncio
import aiohttp
import html
import os
import string
import random
import shodan
from dotenv import load_dotenv
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()

DEFAULT_PASSWORD_LENGTH = 12

# Загрузка переменных из .env файла
load_dotenv()

# Получение Shodan API ключа из переменных окружения
SHODAN_API_KEY = os.getenv('SHODAN_API_KEY')

# Проверка наличия API-ключа
if not SHODAN_API_KEY:
    raise RuntimeError("Не удалось загрузить API-ключ Shodan. Убедитесь, что ваш файл .env настроен правильно.")

# Инициализация клиента Shodan
shodan_client = shodan.Shodan(SHODAN_API_KEY)


# Функция для выполнения системной команды
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-сертификаты домена\n"
        "/shodan <IP-адрес> - Получить информацию об IP-адресе из Shodan\n"
        "/shodan_dns <домен> - Получить информацию о DNS домена через Shodan\n"
        "/generate_password [длина] - Сгенерировать случайный пароль (по умолчанию 12 символов)"
    )
    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)


def generate_random_password(length=DEFAULT_PASSWORD_LENGTH):
    characters = string.ascii_letters + string.digits + string.punctuation
    password = ''.join(random.choice(characters) for i in range(length))
    return password

# Обработчик команды /generate_password
@router.message(Command("generate_password"))
async def cmd_generate_password(message: types.Message):
    # Определяем длину пароля из сообщения, если указана
    args = message.text.split()
    if len(args) > 1 and args[1].isdigit():
        length = int(args[1])
        if length < 6:
            await message.answer("Пароль должен быть длиной не менее 6 символов. Попробуйте еще раз.")
            return
    else:
        length = DEFAULT_PASSWORD_LENGTH

    # Генерируем пароль
    password = generate_random_password(length)
    await message.answer(f"Ваш случайный пароль: <code>{password}</code>", parse_mode='HTML')

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

    ip_address = message.text.split(maxsplit=1)[1]
    
    try:
        host_info = shodan_client.host(ip_address)
        
        response = (
            f"Информация об IP {ip_address}:\n"
            f"Организация: {host_info.get('org', 'Неизвестно')}\n"
            f"Страна: {host_info.get('country_name', 'Неизвестно')}\n"
            f"Открытые порты: {host_info.get('ports', [])}\n"
        )

        await message.answer(response)
    except shodan.APIError as e:
        await message.answer(f"Ошибка Shodan API: {e}")

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

    domain = message.text.split(maxsplit=1)[1]
    
    try:
        dns_info = shodan_client.dns.domain_info(domain)
        response = f"Информация о домене {domain}:\n" + "\n".join(dns_info.get('subdomains', []))
        
        await message.answer(response)
    except shodan.APIError as e:
        await message.answer(f"Ошибка Shodan API: {e}")
    

    
# Обрабатывает команду /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}")

# Обрабатывает команду /curl
@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]
    response = await run_command(f"curl -s {url}")

    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("Ошибка: ответ от сервера пустой или недоступен.")
    logger.info(f"Команда /curl от пользователя {message.from_user.id} с URL: {url}")

# Обрабатывает команду /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}")

# Обрабатывает команду /nmap
@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
    domain = message.text.split(maxsplit=1)[1]
    response = await run_command(f"nmap -sV {domain} 2>/dev/null | sed '/^# Starting/d; /^Starting Nmap/d'")

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


# Хендлер для нераспознанных команд
@router.message()
async def handle_unknown_command(message: types.Message):
    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-сертификаты домена\n"
        "/shodan <IP-адрес> - Получить информацию об IP-адресе из Shodan\n"
        "/generate_password [длина] - Сгенерировать случайный пароль (по умолчанию 12 символов)"
    )

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

# Другие хендлеры должны быть выше этого!