mirror of https://github.com/conqp/rcon
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
4.6 KiB
192 lines
4.6 KiB
"""An interactive console."""
|
|
|
|
from getpass import getpass
|
|
|
|
from rcon.client import Client
|
|
from rcon.config import Config
|
|
from rcon.exceptions import RequestIdMismatch, WrongPassword
|
|
|
|
|
|
__all__ = ['rconcmd']
|
|
|
|
|
|
EXIT_COMMANDS = {'exit', 'quit'}
|
|
MSG_LOGIN_ABORTED = '\nLogin aborted. Bye.'
|
|
MSG_EXIT = '\nBye.'
|
|
MSG_SESSION_TIMEOUT = 'Session timed out. Please login again.'
|
|
PROMPT = 'RCON {host}:{port}> '
|
|
|
|
|
|
def read_host() -> str:
|
|
"""Reads the host."""
|
|
|
|
while True:
|
|
try:
|
|
return input('Host: ')
|
|
except KeyboardInterrupt:
|
|
print()
|
|
continue
|
|
|
|
|
|
def read_port() -> int:
|
|
"""Reads the port."""
|
|
|
|
while True:
|
|
try:
|
|
port = input('Port: ')
|
|
except KeyboardInterrupt:
|
|
print()
|
|
continue
|
|
|
|
try:
|
|
port = int(port)
|
|
except ValueError:
|
|
print(f'Invalid integer: {port}')
|
|
continue
|
|
|
|
if 0 <= port <= 65535:
|
|
return port
|
|
|
|
print(f'Invalid port: {port}')
|
|
|
|
|
|
def read_passwd() -> str:
|
|
"""Reads the password."""
|
|
|
|
while True:
|
|
try:
|
|
return getpass('Password: ')
|
|
except KeyboardInterrupt:
|
|
print()
|
|
|
|
|
|
def get_config(host: str, port: int, passwd: str) -> Config:
|
|
"""Reads the necessary arguments."""
|
|
|
|
if host is None:
|
|
host = read_host()
|
|
|
|
if port is None:
|
|
port = read_port()
|
|
|
|
if passwd is None:
|
|
passwd = read_passwd()
|
|
|
|
return Config(host, port, passwd)
|
|
|
|
|
|
def login(client: Client, passwd: str) -> str:
|
|
"""Performs a login."""
|
|
|
|
while True:
|
|
try:
|
|
client.login(passwd)
|
|
except WrongPassword:
|
|
print('Wrong password.')
|
|
passwd = read_passwd()
|
|
continue
|
|
|
|
return passwd
|
|
|
|
|
|
def process_input(client: Client, passwd: str, prompt: str) -> bool:
|
|
"""Processes the CLI input."""
|
|
|
|
try:
|
|
command = input(prompt)
|
|
except KeyboardInterrupt:
|
|
print()
|
|
return True
|
|
except EOFError:
|
|
print(MSG_EXIT)
|
|
return False
|
|
|
|
try:
|
|
command, *args = command.split()
|
|
except ValueError:
|
|
return True
|
|
|
|
if command in EXIT_COMMANDS:
|
|
return False
|
|
|
|
try:
|
|
result = client.run(command, *args)
|
|
except RequestIdMismatch:
|
|
print(MSG_SESSION_TIMEOUT)
|
|
|
|
try:
|
|
passwd = login(client, passwd)
|
|
except EOFError:
|
|
print(MSG_LOGIN_ABORTED)
|
|
return False
|
|
|
|
print(colorize(result))
|
|
print("\033[0m")
|
|
return True
|
|
|
|
|
|
def esc_256f(rgb_full):
|
|
"""Convert 8 bit RGB value to extended ANSI escape sequence"""
|
|
rgb_666_value = 16 + \
|
|
36 * (round(rgb_full[0] / 255.0) * 5) + \
|
|
6 * (round(rgb_full[1] / 255.0) * 5) + \
|
|
(round(rgb_full[2] / 255.0) * 5)
|
|
return "\033[38;5;{}m".format(str(rgb_666_value))
|
|
|
|
|
|
def colorize(text):
|
|
"""Replace Minecraft formating codes with ANSI escape sequences"""
|
|
# https://www.digminecraft.com/lists/color_list_pc.php
|
|
# https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
|
chat_codes = {
|
|
"§4": esc_256f((0xAA, 0x00, 0x00)),
|
|
"§c": esc_256f((0xFF, 0x55, 0x55)),
|
|
"§6": esc_256f((0xFF, 0xAA, 0x00)),
|
|
"§e": esc_256f((0xFF, 0xFF, 0x55)),
|
|
"§2": esc_256f((0x00, 0xAA, 0x00)),
|
|
"§a": esc_256f((0x55, 0xFF, 0x55)),
|
|
"§b": esc_256f((0x55, 0xFF, 0xFF)),
|
|
"§3": esc_256f((0x00, 0xAA, 0xAA)),
|
|
"§1": esc_256f((0x00, 0x00, 0xAA)),
|
|
"§9": esc_256f((0x55, 0x55, 0xFF)),
|
|
"§d": esc_256f((0xFF, 0x55, 0xFF)),
|
|
"§5": esc_256f((0xAA, 0x00, 0xAA)),
|
|
"§f": esc_256f((0xFF, 0xFF, 0xFF)),
|
|
"§7": esc_256f((0xAA, 0xAA, 0xAA)),
|
|
"§8": esc_256f((0x55, 0x55, 0x55)),
|
|
"§0": esc_256f((0x00, 0x00, 0x00)),
|
|
|
|
"§l": "\033[1m", # bold
|
|
"§m": "\033[9m", # strikethrough
|
|
"§n": "\033[4m", # underline
|
|
"§o": "\033[3m", # italic
|
|
|
|
"§r": "\033[0m", # reset
|
|
}
|
|
|
|
for k, v in chat_codes.items():
|
|
text = text.replace(k, v)
|
|
return text
|
|
|
|
|
|
def rconcmd(host: str, port: int, passwd: str, *, prompt: str = PROMPT):
|
|
"""Initializes the console."""
|
|
|
|
try:
|
|
host, port, passwd = get_config(host, port, passwd)
|
|
except EOFError:
|
|
print(MSG_EXIT)
|
|
return
|
|
|
|
prompt = prompt.format(host=host, port=port)
|
|
|
|
with Client(host, port) as client:
|
|
try:
|
|
passwd = login(client, passwd)
|
|
except EOFError:
|
|
print(MSG_LOGIN_ABORTED)
|
|
return
|
|
|
|
while True:
|
|
if not process_input(client, passwd, prompt):
|
|
break
|
|
|