mirror of
https://github.com/catspace-dev/unicheckbot.git
synced 2024-11-29 00:43:43 +03:00
impl #2, added tcp method
This commit is contained in:
parent
98c7f9075d
commit
10759e0219
|
@ -5,11 +5,11 @@ from tgbot.nodes import nodes as all_nodes
|
||||||
from httpx import Response
|
from httpx import Response
|
||||||
from aiogram.bot import Bot
|
from aiogram.bot import Bot
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from core.coretypes import ErrorPayload, ICMPCheckerResponse, ResponseStatus, APINodeInfo
|
from core.coretypes import APINodeInfo
|
||||||
from .helpers import send_api_requests
|
from .helpers import send_api_requests
|
||||||
|
|
||||||
header = "Отчет о проверке хоста:" \
|
header = "Отчет о проверке хоста:" \
|
||||||
"\n\n— Хост: {target}"\
|
"\n\n— Хост: {target_fq}"\
|
||||||
f"\n— Дата проверки: {datetime.now():%d.%m.%y в %H:%M} (MSK)" # TODO: Get timezone
|
f"\n— Дата проверки: {datetime.now():%d.%m.%y в %H:%M} (MSK)" # TODO: Get timezone
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ class CheckerBaseHandler:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def handler(self, message: Message):
|
async def handler(self, message: Message):
|
||||||
|
"""Always should call check at end"""
|
||||||
raise NotImplemented
|
raise NotImplemented
|
||||||
|
|
||||||
async def check(self, chat_id: int, bot: Bot, data: dict):
|
async def check(self, chat_id: int, bot: Bot, data: dict):
|
||||||
|
@ -39,7 +40,7 @@ class CheckerBaseHandler:
|
||||||
async for res in send_api_requests(self.api_endpoint, data, all_nodes):
|
async for res in send_api_requests(self.api_endpoint, data, all_nodes):
|
||||||
await bot.send_chat_action(chat_id, 'typing')
|
await bot.send_chat_action(chat_id, 'typing')
|
||||||
if res.status_code == 500:
|
if res.status_code == 500:
|
||||||
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. Backend offline!")
|
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. ❌️ Результат операции не доступен.")
|
||||||
else:
|
else:
|
||||||
node_formatted_response = await self.prepare_message(res)
|
node_formatted_response = await self.prepare_message(res)
|
||||||
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. {node_formatted_response}")
|
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. {node_formatted_response}")
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
from aiogram import Dispatcher
|
from aiogram import Dispatcher
|
||||||
|
|
||||||
from .start import start_cmd
|
from .start import start_cmd
|
||||||
from .web import web_cmd
|
from .web import WebCheckerHandler
|
||||||
from .whois import whois_cmd
|
from .whois import whois_cmd
|
||||||
from .icmp import icmp_cmd
|
from .icmp import ICMPCheckerHandler
|
||||||
|
from .tcp import TCPCheckerHandler
|
||||||
|
|
||||||
|
|
||||||
def setup(dp: Dispatcher):
|
def setup(dp: Dispatcher):
|
||||||
dp.register_message_handler(start_cmd, is_forwarded=False, commands=['start'])
|
dp.register_message_handler(start_cmd, is_forwarded=False, commands=['start'])
|
||||||
dp.register_message_handler(web_cmd, is_forwarded=False, commands=['web', 'http'])
|
dp.register_message_handler(WebCheckerHandler().handler, is_forwarded=False, commands=['web', 'http'])
|
||||||
dp.register_message_handler(whois_cmd, is_forwarded=False, commands=['whois'])
|
dp.register_message_handler(whois_cmd, is_forwarded=False, commands=['whois'])
|
||||||
dp.register_message_handler(icmp_cmd, is_forwarded=False, commands=['icmp', 'ping'])
|
dp.register_message_handler(ICMPCheckerHandler().handler, is_forwarded=False, commands=['icmp', 'ping'])
|
||||||
|
dp.register_message_handler(TCPCheckerHandler().handler, is_forwarded=False, commands=['tcp'])
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from tgbot.nodes import nodes as all_nodes
|
from httpx import Response
|
||||||
from httpx import AsyncClient, Response
|
from core.coretypes import ErrorPayload, ICMPCheckerResponse, ResponseStatus
|
||||||
from datetime import datetime
|
from ..base import CheckerBaseHandler, NotEnoughArgs
|
||||||
from core.coretypes import ErrorCodes, ErrorPayload, ICMPCheckerResponse, ResponseStatus, APINodeInfo
|
|
||||||
from ..helpers import send_api_requests
|
|
||||||
|
|
||||||
icmp_help_message = """
|
icmp_help_message = """
|
||||||
❓ Производит проверку хоста по протоколу ICMP.
|
❓ Производит проверку хоста по протоколу ICMP.
|
||||||
|
@ -13,40 +11,35 @@ icmp_help_message = """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
async def prepare_icmp_check_result(res: Response):
|
class ICMPCheckerHandler(CheckerBaseHandler):
|
||||||
node = APINodeInfo(**res.json().get("node", None))
|
help_message = icmp_help_message
|
||||||
message = f"{node.location}:\n"
|
api_endpoint = "/icmp"
|
||||||
status = res.json().get("status", None)
|
|
||||||
|
|
||||||
if status == ResponseStatus.OK:
|
def __init__(self):
|
||||||
payload = ICMPCheckerResponse(**res.json().get("payload"))
|
super(ICMPCheckerHandler, self).__init__()
|
||||||
message += f"✅ {payload.min_rtt}/{payload.max_rtt}/{payload.avg_rtt} " \
|
|
||||||
f"⬆{payload.packets_sent} ️⬇️{payload.packets_received} Loss: {payload.loss}"
|
|
||||||
if status == ResponseStatus.ERROR:
|
|
||||||
payload = ErrorPayload(**res.json().get("payload"))
|
|
||||||
message += f"❌️ {payload.message}"
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
async def handler(self, message: Message):
|
||||||
|
try:
|
||||||
|
args = await self.process_args(message.text)
|
||||||
|
except NotEnoughArgs:
|
||||||
|
return await message.answer(icmp_help_message)
|
||||||
|
await self.check(message.chat.id, message.bot, dict(target=args[0], target_fq=args[0]))
|
||||||
|
|
||||||
async def check_icmp(msg: Message, target: str):
|
async def process_args(self, text: str) -> list:
|
||||||
rsp_msg = await msg.answer(f"Отчет о проверке хоста:"
|
args = text.split(" ")
|
||||||
f"\n\n— Хост: {target}"
|
if len(args) == 1:
|
||||||
f"\n— Дата проверки: {datetime.now():%d.%m.%y в %H:%M} (MSK)" # TODO: Get timezone
|
raise NotEnoughArgs()
|
||||||
)
|
if len(args) >= 2:
|
||||||
iter_keys = 1 # because I can't use enumerate
|
target = args[1]
|
||||||
# using generators for magic...
|
return [target]
|
||||||
async for res in send_api_requests("icmp", dict(target=target), all_nodes):
|
|
||||||
await msg.bot.send_chat_action(msg.chat.id, 'typing')
|
|
||||||
node_formatted_response = await prepare_icmp_check_result(res)
|
|
||||||
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. {node_formatted_response}")
|
|
||||||
iter_keys = iter_keys + 1
|
|
||||||
await rsp_msg.edit_text(rsp_msg.text + f"\n\nПроверка завершена❗")
|
|
||||||
|
|
||||||
|
async def prepare_message(self, res: Response):
|
||||||
async def icmp_cmd(msg: Message):
|
message, status = await self.message_std_vals(res)
|
||||||
args = msg.text.split(" ")
|
if status == ResponseStatus.OK:
|
||||||
if len(args) == 1:
|
payload = ICMPCheckerResponse(**res.json().get("payload"))
|
||||||
return await msg.answer(icmp_help_message, parse_mode="Markdown")
|
message += f"✅ {payload.min_rtt}/{payload.max_rtt}/{payload.avg_rtt} " \
|
||||||
if len(args) >= 2:
|
f"⬆{payload.packets_sent} ️⬇️{payload.packets_received} Loss: {payload.loss}"
|
||||||
target = args[1]
|
if status == ResponseStatus.ERROR:
|
||||||
await check_icmp(msg, target)
|
payload = ErrorPayload(**res.json().get("payload"))
|
||||||
|
message += f"❌️ {payload.message}"
|
||||||
|
return message
|
||||||
|
|
61
apps/tgbot/tgbot/handlers/default/tcp.py
Normal file
61
apps/tgbot/tgbot/handlers/default/tcp.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from aiogram.types import Message
|
||||||
|
from core.coretypes import ResponseStatus, ErrorPayload, PortResponse
|
||||||
|
from httpx import Response
|
||||||
|
|
||||||
|
from tgbot.handlers.base import CheckerBaseHandler, NotEnoughArgs, InvalidPort
|
||||||
|
from tgbot.handlers.helpers import check_int
|
||||||
|
|
||||||
|
tcp_help_message = """
|
||||||
|
❓ Производит проверку TCP порта, открыт ли он или нет
|
||||||
|
|
||||||
|
Использование:
|
||||||
|
`/tcp <hostname> <port>`
|
||||||
|
"""
|
||||||
|
|
||||||
|
invalid_port = """❗Неправильный порт. Напишите /tcp чтобы увидеть справку к данному способу проверки."""
|
||||||
|
|
||||||
|
|
||||||
|
class TCPCheckerHandler(CheckerBaseHandler):
|
||||||
|
help_message = tcp_help_message
|
||||||
|
api_endpoint = "/tcp_port"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
async def handler(self, message: Message):
|
||||||
|
try:
|
||||||
|
args = await self.process_args(message.text)
|
||||||
|
except NotEnoughArgs:
|
||||||
|
return await message.answer(self.help_message, parse_mode="Markdown")
|
||||||
|
except InvalidPort:
|
||||||
|
return await message.answer(invalid_port, parse_mode="Markdown")
|
||||||
|
await self.check(
|
||||||
|
message.chat.id,
|
||||||
|
message.bot,
|
||||||
|
dict(target=args[0], port=args[1], target_fq=f"{args[0]}:{args[1]}")
|
||||||
|
)
|
||||||
|
|
||||||
|
async def process_args(self, text: str) -> list:
|
||||||
|
port = None
|
||||||
|
args = text.split(" ")
|
||||||
|
if len(args) < 3:
|
||||||
|
raise NotEnoughArgs()
|
||||||
|
if len(args) >= 3:
|
||||||
|
port = args[2]
|
||||||
|
if not check_int(port):
|
||||||
|
raise InvalidPort()
|
||||||
|
host = args[1]
|
||||||
|
return [host, port]
|
||||||
|
|
||||||
|
async def prepare_message(self, res: Response):
|
||||||
|
message, status = await self.message_std_vals(res)
|
||||||
|
if status == ResponseStatus.OK:
|
||||||
|
payload = PortResponse(**res.json().get("payload"))
|
||||||
|
if payload.open:
|
||||||
|
message += "✅ Порт ОТКРЫТ"
|
||||||
|
else:
|
||||||
|
message += "❌️ Порт ЗАКРЫТ"
|
||||||
|
if status == ResponseStatus.ERROR:
|
||||||
|
payload = ErrorPayload(**res.json().get("payload"))
|
||||||
|
message += f"❌️ {payload.message}"
|
||||||
|
return message
|
|
@ -1,11 +1,8 @@
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from typing import Optional
|
|
||||||
from tgbot.handlers.helpers import check_int
|
from tgbot.handlers.helpers import check_int
|
||||||
from tgbot.nodes import nodes as all_nodes
|
|
||||||
from httpx import Response
|
from httpx import Response
|
||||||
from core.coretypes import ResponseStatus, HTTP_EMOJI
|
from core.coretypes import ResponseStatus, HTTP_EMOJI, HttpCheckerResponse, ErrorPayload
|
||||||
from datetime import datetime
|
from ..base import CheckerBaseHandler, NotEnoughArgs, InvalidPort
|
||||||
from ..helpers import send_api_requests
|
|
||||||
|
|
||||||
web_help_message = """
|
web_help_message = """
|
||||||
❓ Производит проверку хоста по протоколу HTTP.
|
❓ Производит проверку хоста по протоколу HTTP.
|
||||||
|
@ -18,55 +15,47 @@ web_help_message = """
|
||||||
invalid_port = """❗Неправильный порт. Напишите /web чтобы увидеть справку к данному способу проверки."""
|
invalid_port = """❗Неправильный порт. Напишите /web чтобы увидеть справку к данному способу проверки."""
|
||||||
|
|
||||||
|
|
||||||
async def prepare_webcheck_message(response: Response) -> str:
|
class WebCheckerHandler(CheckerBaseHandler):
|
||||||
# TODO: Use types from core!
|
help_message = web_help_message
|
||||||
message = ""
|
api_endpoint = "/http"
|
||||||
json_rsp = response.json()
|
|
||||||
status = json_rsp.get("status")
|
|
||||||
location = json_rsp['node']['location']
|
|
||||||
if status == ResponseStatus.OK:
|
|
||||||
status_code = json_rsp['payload']['status_code']
|
|
||||||
time = round(json_rsp['payload']['time'], 2)
|
|
||||||
message = f"{location}:" \
|
|
||||||
f"\n{HTTP_EMOJI.get(status_code//100, '')} {status_code}, ⏰ {time} сек."
|
|
||||||
if status == ResponseStatus.ERROR:
|
|
||||||
message = json_rsp['payload']['message']
|
|
||||||
message = f"{location}: " \
|
|
||||||
f"\n❌ {message}"
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
async def check_web(message: Message, host: str, port: Optional[int]):
|
async def handler(self, message: Message):
|
||||||
if port is None:
|
try:
|
||||||
port = 80
|
args = await self.process_args(message.text)
|
||||||
rsp_msg = await message.answer(f"Отчет о проверке хоста:"
|
except NotEnoughArgs:
|
||||||
f"\n\n— Хост: {host}:{port}"
|
return await message.answer(self.help_message, parse_mode="Markdown")
|
||||||
f"\n— Дата проверки: {datetime.now():%d.%m.%y в %H:%M} (MSK)" # TODO: Get timezone
|
except InvalidPort:
|
||||||
)
|
return await message.answer(invalid_port, parse_mode="Markdown")
|
||||||
iter_keys = 1 # because I can't use enumerate
|
await self.check(
|
||||||
# using generators for magic...
|
message.chat.id,
|
||||||
async for res in send_api_requests("http", dict(target=host, port=port), all_nodes):
|
message.bot,
|
||||||
# set typing status...
|
dict(target=args[0], port=args[1], target_fq=f"{args[0]}:{args[1]}")
|
||||||
await message.bot.send_chat_action(message.chat.id, 'typing')
|
)
|
||||||
|
|
||||||
node_formatted_response = await prepare_webcheck_message(res)
|
async def process_args(self, text: str) -> list:
|
||||||
rsp_msg = await rsp_msg.edit_text(rsp_msg.text + f"\n\n{iter_keys}. {node_formatted_response}")
|
port = None
|
||||||
iter_keys = iter_keys + 1
|
args = text.split(" ")
|
||||||
await rsp_msg.edit_text(rsp_msg.text + f"\n\nПроверка завершена❗")
|
if len(args) < 2:
|
||||||
|
raise NotEnoughArgs()
|
||||||
|
if len(args) == 3:
|
||||||
async def web_cmd(msg: Message):
|
port = args[2]
|
||||||
|
if not check_int(port):
|
||||||
port = None
|
raise InvalidPort()
|
||||||
# TODO: Maybe check it in separated function?
|
if len(args) == 2:
|
||||||
args = msg.text.split(" ")
|
port = 80
|
||||||
if len(args) < 2:
|
host = args[1]
|
||||||
return await msg.answer(web_help_message, parse_mode="Markdown")
|
return [host, port]
|
||||||
if len(args) == 3:
|
|
||||||
port = args[2]
|
|
||||||
if not check_int(port):
|
|
||||||
return await msg.answer(invalid_port, parse_mode="Markdown")
|
|
||||||
host = args[1]
|
|
||||||
|
|
||||||
await check_web(msg, host, port)
|
|
||||||
|
|
||||||
|
async def prepare_message(self, res: Response):
|
||||||
|
message, status = await self.message_std_vals(res)
|
||||||
|
if status == ResponseStatus.OK:
|
||||||
|
payload = HttpCheckerResponse(**res.json().get("payload"))
|
||||||
|
message += f"{HTTP_EMOJI.get(payload.status_code // 100, '')} " \
|
||||||
|
f"{payload.status_code}, ⏰ {payload.time * 1000:.2f}ms"
|
||||||
|
if status == ResponseStatus.ERROR:
|
||||||
|
payload = ErrorPayload(**res.json().get("payload"))
|
||||||
|
message += f"❌️ {payload.message}"
|
||||||
|
return message
|
||||||
|
|
|
@ -21,6 +21,7 @@ async def send_api_requests(endpoint: str, data: dict, nodes: List[APINode]):
|
||||||
f"{node.address}/{endpoint}", params=data
|
f"{node.address}/{endpoint}", params=data
|
||||||
)
|
)
|
||||||
except ConnectError:
|
except ConnectError:
|
||||||
|
# TODO: Report problems to admins
|
||||||
# We yield 500 response when backend is offline
|
# We yield 500 response when backend is offline
|
||||||
result = Response(500)
|
result = Response(500)
|
||||||
yield result
|
yield result
|
||||||
|
|
Loading…
Reference in New Issue
Block a user