mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Added and updated repositories 2025-12-21 01:14:41
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see https://www.gnu.org/licenses/."""
|
||||
|
||||
__version__ = (2, 0, 0)
|
||||
__version__ = (3, 0, 1)
|
||||
|
||||
# meta developer: @RUIS_VlP
|
||||
# при поддержке @hikka_mods
|
||||
@@ -26,12 +26,11 @@ import asyncio
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
async def clean_domain(value: str) -> List[str]:
|
||||
async def clean_domain(value: str) -> str:
|
||||
# Убираем протокол, порт, путь
|
||||
value = re.sub(r'^(https?://)?', '', value)
|
||||
value = value.split('/')[0]
|
||||
value = value.split(':')[0]
|
||||
|
||||
return value
|
||||
|
||||
async def ipcheck(value: str) -> str:
|
||||
@@ -47,20 +46,53 @@ async def ipcheck(value: str) -> str:
|
||||
|
||||
return "domain"
|
||||
|
||||
async def get_whois(identifier, API_KEY: str) -> dict:
|
||||
url = "https://api.jsonwhoisapi.com/v1/whois"
|
||||
headers = {
|
||||
"Authorization": API_KEY
|
||||
}
|
||||
params = {
|
||||
"identifier": identifier
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, headers=headers, params=params) as resp:
|
||||
resp.raise_for_status()
|
||||
response = await resp.json()
|
||||
return response
|
||||
async def get_whois(identifier, API_KEY: str = None) -> dict:
|
||||
"""Получение данных через RDAP для доменов и ipwho.is для IP"""
|
||||
check = await ipcheck(identifier)
|
||||
|
||||
if check == "ip":
|
||||
url = f"http://ipwho.is/{identifier}"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as resp:
|
||||
resp.raise_for_status()
|
||||
response = await resp.json()
|
||||
return response
|
||||
else:
|
||||
# Основной RDAP сервис
|
||||
url_primary = f"https://rdap.org/domain/{identifier}"
|
||||
# Резервный RDAP сервис
|
||||
url_backup = f"https://rdap.active.domains/domain/{identifier}"
|
||||
|
||||
# Заголовки для обхода блокировок
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Accept': 'application/rdap+json, application/json'
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.get(url_primary, headers=headers, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
||||
if resp.status in (404, 403, 429, 500, 502, 503):
|
||||
# Если ошибка на основном, пробуем резервный
|
||||
async with session.get(url_backup, headers=headers) as resp_backup:
|
||||
resp_backup.raise_for_status()
|
||||
response = await resp_backup.json()
|
||||
return response
|
||||
resp.raise_for_status()
|
||||
response = await resp.json()
|
||||
return response
|
||||
except aiohttp.ClientResponseError as e:
|
||||
# Любая HTTP ошибка - пробуем резервный
|
||||
async with session.get(url_backup, headers=headers) as resp_backup:
|
||||
resp_backup.raise_for_status()
|
||||
response = await resp_backup.json()
|
||||
return response
|
||||
except (aiohttp.ClientError, asyncio.TimeoutError):
|
||||
# При ошибке соединения пробуем резервный
|
||||
async with session.get(url_backup, headers=headers) as resp_backup:
|
||||
resp_backup.raise_for_status()
|
||||
response = await resp_backup.json()
|
||||
return response
|
||||
|
||||
async def fetch_dns_record(session, domain, record_type):
|
||||
url = "https://dns.google/resolve"
|
||||
@@ -94,64 +126,166 @@ async def get_ips(domain):
|
||||
ipv6_task = fetch_dns_record(session, domain, "AAAA")
|
||||
ipv4, ipv6 = await asyncio.gather(ipv4_task, ipv6_task)
|
||||
return [ipv4, ipv6]
|
||||
|
||||
async def json2text_ip(data: dict) -> str:
|
||||
"""Форматирование данных IP из ipwho.is"""
|
||||
def get(value):
|
||||
return str(value) if value not in (None, '', [], {}) else 'Неизвестно'
|
||||
|
||||
if not data.get("success", False):
|
||||
return f"❌ <b>IP адрес не найден или недоступен</b>"
|
||||
|
||||
ip = get(data.get("ip"))
|
||||
ip_type = get(data.get("type"))
|
||||
continent = get(data.get("continent"))
|
||||
country = get(data.get("country"))
|
||||
country_code = get(data.get("country_code"))
|
||||
region = get(data.get("region"))
|
||||
city = get(data.get("city"))
|
||||
postal = get(data.get("postal"))
|
||||
|
||||
flag = data.get("flag", {})
|
||||
flag_emoji = flag.get("emoji", "🌍")
|
||||
|
||||
connection = data.get("connection", {})
|
||||
asn = get(connection.get("asn"))
|
||||
org = get(connection.get("org"))
|
||||
isp = get(connection.get("isp"))
|
||||
domain = get(connection.get("domain"))
|
||||
|
||||
timezone_data = data.get("timezone", {})
|
||||
timezone_id = get(timezone_data.get("id"))
|
||||
timezone_utc = get(timezone_data.get("utc"))
|
||||
|
||||
lines = [
|
||||
f"<emoji document_id=5224450179368767019>🌎</emoji><b>IP адрес:</b> <code>{ip}</code>",
|
||||
f"<emoji document_id=4992466832364405778>🖥</emoji> <b>Тип:</b> <code>{ip_type}</code>",
|
||||
"",
|
||||
f"{flag_emoji} <b>Местоположение:</b>",
|
||||
f" • Континент: <code>{continent}</code>",
|
||||
f" • Страна: <code>{country} ({country_code})</code>",
|
||||
f" • Регион: <code>{region}</code>",
|
||||
f" • Город: <code>{city}</code>",
|
||||
f" • Индекс: <code>{postal}</code>",
|
||||
"",
|
||||
f"<emoji document_id=5431376038628171216>🔗</emoji> <b>Подключение:</b>",
|
||||
f" • ASN: <code>{asn}</code>",
|
||||
f" • Организация: <code>{org}</code>",
|
||||
f" • Провайдер: <code>{isp}</code>",
|
||||
f" • Домен: <code>{domain}</code>",
|
||||
"",
|
||||
f"<emoji document_id=5431815452437257407>🕐</emoji> <b>Часовой пояс:</b>",
|
||||
f" • Зона: <code>{timezone_id}</code>",
|
||||
f" • UTC: <code>{timezone_utc}</code>",
|
||||
]
|
||||
|
||||
return '\n'.join(line for line in lines if '<code>Неизвестно</code>' not in line)
|
||||
|
||||
async def json2text(data: dict, ips, check) -> str:
|
||||
def get(value):
|
||||
return str(value) if value not in (None, '', [], {}) else 'Неизвестно'
|
||||
|
||||
# Обработка RDAP формата
|
||||
ldhName = data.get("ldhName", data.get("handle", ""))
|
||||
|
||||
# Проверка статуса регистрации
|
||||
status = data.get("status", [])
|
||||
status_str = ', '.join(status) if isinstance(status, list) else get(status)
|
||||
if isinstance(status, list):
|
||||
status_str = ', '.join(status) if status else 'Неизвестно'
|
||||
else:
|
||||
status_str = get(status)
|
||||
|
||||
nameservers = data.get("nameservers", []) or ['Неизвестнo']
|
||||
registered = 'Да' if data.get('registered') else 'Нет'
|
||||
if registered == "Нет":
|
||||
return f"<emoji document_id=5224450179368767019>🌎</emoji><b>Домен:</b> <code>{(get(data.get('name'))).encode('ascii').decode('idna')}</code>\n\n<emoji document_id=4985637404867036136>🖥</emoji> <b>Домен свободен</b>"
|
||||
# Проверка на свободный домен
|
||||
if not ldhName or "object not found" in str(data).lower():
|
||||
domain_name = data.get("ldhName", data.get("handle", "Неизвестно"))
|
||||
return f"<emoji document_id=5224450179368767019>🌎</emoji><b>Домен:</b> <code>{domain_name}</code>\n\n<emoji document_id=4985637404867036136>🖥</emoji> <b>Домен свободен</b>"
|
||||
|
||||
# Извлечение событий (даты)
|
||||
events = data.get("events", [])
|
||||
created = next((e["eventDate"] for e in events if e.get("eventAction") == "registration"), None)
|
||||
changed = next((e["eventDate"] for e in events if e.get("eventAction") == "last changed"), None)
|
||||
expires = next((e["eventDate"] for e in events if e.get("eventAction") == "expiration"), None)
|
||||
|
||||
# Извлечение nameservers
|
||||
nameservers_data = data.get("nameservers", [])
|
||||
nameservers = [ns.get("ldhName", "Неизвестно") for ns in nameservers_data] if nameservers_data else ['Неизвестно']
|
||||
|
||||
# Извлечение контактов
|
||||
entities = data.get("entities", [])
|
||||
admin = {}
|
||||
registrar = {}
|
||||
|
||||
for entity in entities:
|
||||
roles = entity.get("roles", [])
|
||||
if "administrative" in roles or "admin" in roles:
|
||||
vcard = entity.get("vcardArray", [[]])
|
||||
if len(vcard) > 1:
|
||||
for field in vcard[1]:
|
||||
if field[0] == "fn":
|
||||
admin["name"] = field[3]
|
||||
elif field[0] == "email":
|
||||
admin["email"] = field[3]
|
||||
elif field[0] == "org":
|
||||
admin["organization"] = field[3]
|
||||
elif field[0] == "adr":
|
||||
if len(field[3]) > 6:
|
||||
admin["country"] = field[3][6]
|
||||
|
||||
if "registrar" in roles:
|
||||
vcard = entity.get("vcardArray", [[]])
|
||||
registrar["name"] = entity.get("handle", "")
|
||||
if len(vcard) > 1:
|
||||
for field in vcard[1]:
|
||||
if field[0] == "fn":
|
||||
registrar["name"] = field[3]
|
||||
elif field[0] == "email":
|
||||
registrar["email"] = field[3]
|
||||
elif field[0] == "tel":
|
||||
registrar["phone"] = field[3]
|
||||
|
||||
registered = 'Да' if ldhName else 'Нет'
|
||||
|
||||
admin = (data.get("contacts", {}).get("admin") or [{}])[0]
|
||||
registrar = data.get("registrar", {})
|
||||
|
||||
lines = [
|
||||
f"<emoji document_id=5224450179368767019>🌎</emoji><b>Домен:</b> <code>{(get(data.get('name'))).encode('ascii').decode('idna')}</code>",]
|
||||
if len(ips) > 0:
|
||||
f"<emoji document_id=5224450179368767019>🌎</emoji><b>Домен:</b> <code>{ldhName}</code>",
|
||||
]
|
||||
|
||||
if len(ips) > 0 and (ips[0] or ips[1]):
|
||||
lines += ["<emoji document_id=4992466832364405778>🖥</emoji> <b>IP адреса:</b>"]
|
||||
lines += [f" • <code>{ip}</code>" for ip in ips[0]]
|
||||
lines += [f" • <code>{ip}</code>" for ip in ips[1]]
|
||||
else:
|
||||
pass
|
||||
|
||||
lines += [
|
||||
"",
|
||||
f"<emoji document_id=5274055917766202507>🗓</emoji> <b>Дата регистрации:</b> <code>{get(data.get('created'))}</code>",
|
||||
f"♻️ <b>Изменено:</b> <code>{get(data.get('changed'))}</code>",
|
||||
f"<emoji document_id=5325583469344989152>⏳</emoji><b>Истекает:</b> <code>{get(data.get('expires'))}</code>",
|
||||
f"<emoji document_id=5274055917766202507>🗓</emoji> <b>Дата регистрации:</b> <code>{get(created)}</code>",
|
||||
f"♻️ <b>Изменено:</b> <code>{get(changed)}</code>",
|
||||
f"<emoji document_id=5325583469344989152>⏳</emoji><b>Истекает:</b> <code>{get(expires)}</code>",
|
||||
f"<emoji document_id=5206607081334906820>✔️</emoji> <b>Зарегистрирован:</b> <code>{registered}</code>",
|
||||
f"<emoji document_id=5231200819986047254>📊</emoji> <b>Статус:</b> <code>{status_str}</code>",
|
||||
"",]
|
||||
|
||||
if check == "domain":
|
||||
lines += [
|
||||
f"<emoji document_id=4985545282113503960>🖥</emoji> <b>DNS-серверы:</b>",
|
||||
"",
|
||||
]
|
||||
|
||||
if check == "domain" and nameservers[0] != 'Неизвестно':
|
||||
lines += ["<emoji document_id=4985545282113503960>🖥</emoji> <b>DNS-серверы:</b>"]
|
||||
lines += [f" • <code>{ns}</code>" for ns in nameservers]
|
||||
|
||||
lines += [
|
||||
"",
|
||||
"<emoji document_id=5936110055404342764>👤</emoji> <b>Админ-контакт:</b>",
|
||||
f" • Имя: <code>{get(admin.get('name'))}</code>",
|
||||
f" • Email: <code>{get(admin.get('email'))}</code>",
|
||||
f" • Организация: <code>{get(admin.get('organization'))}</code>",
|
||||
f" • Страна: <code>{get(admin.get('country'))}</code>",
|
||||
"",]
|
||||
|
||||
if check == "domain":
|
||||
if admin:
|
||||
lines += [
|
||||
"<emoji document_id=5445353829304387411>💳</emoji> <b>Регистратор:</b>",
|
||||
f" • ID: <code>{get(registrar.get('id'))}</code>",
|
||||
f" • Название: <code>{get(registrar.get('name'))}</code>",
|
||||
f" • Email: <code>{get(registrar.get('email'))}</code>",
|
||||
f" • Сайт: <code>{get(registrar.get('url'))}</code>",
|
||||
f" • Телефон: <code>{get(registrar.get('phone'))}</code>",
|
||||
]
|
||||
"",
|
||||
"<emoji document_id=5936110055404342764>👤</emoji> <b>Админ-контакт:</b>",
|
||||
f" • Имя: <code>{get(admin.get('name'))}</code>",
|
||||
f" • Email: <code>{get(admin.get('email'))}</code>",
|
||||
f" • Организация: <code>{get(admin.get('organization'))}</code>",
|
||||
f" • Страна: <code>{get(admin.get('country'))}</code>",
|
||||
"",
|
||||
]
|
||||
|
||||
if check == "domain" and registrar:
|
||||
lines += [
|
||||
"<emoji document_id=5445353829304387411>💳</emoji> <b>Регистратор:</b>",
|
||||
f" • Название: <code>{get(registrar.get('name'))}</code>",
|
||||
f" • Email: <code>{get(registrar.get('email'))}</code>",
|
||||
f" • Телефон: <code>{get(registrar.get('phone'))}</code>",
|
||||
]
|
||||
|
||||
return '\n'.join(line for line in lines if '<code>Неизвестно</code>' not in line)
|
||||
|
||||
@@ -161,41 +295,32 @@ class WhoisMod(loader.Module):
|
||||
|
||||
strings = {"name": "Whois"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
"None",
|
||||
lambda: "API ключ с сайта https://jsonwhoisapi.com/",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def whois(self, message):
|
||||
"""<домен> - получить информацию о домене или IP"""
|
||||
api_key = self.config["api_key"]
|
||||
if api_key == "None":
|
||||
await utils.answer(message, '❌ <b>Не указан API ключ! Получите его на</b> jsonwhoisapi.com <b>и вставьте в config</b> (<code>.config Whois</code>)')
|
||||
return
|
||||
|
||||
domain = ((utils.get_args_raw(message)).split()[0]).encode('idna').decode('ascii')
|
||||
if not domain:
|
||||
await utils.answer(message, "❌ <b>Вы не указали домен!</b>")
|
||||
return
|
||||
|
||||
await utils.answer(message, "📡 <i>Отправляю запрос...</i>")
|
||||
try:
|
||||
check = await ipcheck(domain)
|
||||
clean = await clean_domain(domain)
|
||||
|
||||
if check == "ip":
|
||||
info = await get_whois(clean, api_key)
|
||||
text = await json2text(info, [], "ip")
|
||||
await utils.answer(message, text)
|
||||
return
|
||||
whois = get_whois(clean, api_key)
|
||||
info = await get_whois(clean)
|
||||
text = await json2text_ip(info)
|
||||
await utils.answer(message, text)
|
||||
return
|
||||
|
||||
whois = get_whois(clean)
|
||||
ips = get_ips(clean)
|
||||
info, ips = await asyncio.gather(whois, ips)
|
||||
text = await json2text(info, ips, "domain")
|
||||
await utils.answer(message, text)
|
||||
except Exception as e:
|
||||
await utils.answer(message, f"❌ <b>Ошибка!</b>\n\n<code>{e}</code>")
|
||||
if "404" in str(e):
|
||||
await utils.answer(message, f"<emoji document_id=5224450179368767019>🌎</emoji><b>Домен:</b> <code>{clean}</code>\n\n<emoji document_id=4985637404867036136>🖥</emoji> <b>Домен свободен</b>")
|
||||
return
|
||||
else:
|
||||
await utils.answer(message, f"❌ <b>Ошибка!</b>\n\n<code>{e}</code>")
|
||||
Reference in New Issue
Block a user