semi-rewrote mcping

This commit is contained in:
Luke Rogers 2014-02-22 22:04:39 +13:00
parent 5008da1331
commit 13df70da1b
1 changed files with 91 additions and 66 deletions

View File

@ -1,4 +1,3 @@
# TODO: Rewrite this whole mess
import socket
import struct
import json
@ -8,24 +7,39 @@ from util import hook
try:
import DNS
# Please remember to install the dependency 'pydns'
pydns_installed = True
has_dns = True
except ImportError:
pydns_installed = False
has_dns = False
mccolors = [u"\x0300,\xa7f", u"\x0301,\xa70", u"\x0302,\xa71", u"\x0303,\xa72", u"\x0304,\xa7c", u"\x0305,\xa74",
u"\x0306,\xa75", u"\x0307,\xa76", u"\x0308,\xa7e", u"\x0309,\xa7a", u"\x0310,\xa73", u"\x0311,\xa7b",
u"\x0312,\xa71", u"\x0313,\xa7d", u"\x0314,\xa78", u"\x0315,\xa77", u"\x02,\xa7l", u"\x0310,\xa79",
u"\x09,\xa7o", u"\x13,\xa7m", u"\x0f,\xa7r", u"\x15,\xa7n"]
mc_colors = [(u'\xa7f', u'\x0300'), (u'\xa70', u'\x0301'), (u'\xa71', u'\x0302'), (u'\xa72', u'\x0303'),
(u'\xa7c', u'\x0304'), (u'\xa74', u'\x0305'), (u'\xa75', u'\x0306'), (u'\xa76', u'\x0307'),
(u'\xa7e', u'\x0308'), (u'\xa7a', u'\x0309'), (u'\xa73', u'\x0310'), (u'\xa7b', u'\x0311'),
(u'\xa71', u'\x0312'), (u'\xa7d', u'\x0313'), (u'\xa78', u'\x0314'), (u'\xa77', u'\x0315'),
(u'\xa7l', u'\x02'), (u'\xa79', u'\x0310'), (u'\xa7o', u'\t'), (u'\xa7m', u'\x13'),
(u'\xa7r', u'\x0f'), (u'\xa7n', u'\x15')]
def mc_color_format(motd):
for colorcombo in mccolors:
colorarray = colorcombo.split(",")
motd = motd.replace(colorarray[1], colorarray[0])
motd = motd.replace(u"\xa7k", "")
return motd
## EXCEPTIONS
class PingError(Exception):
def __init__(self, text):
self.text = text
def __str__(self):
return self.text
class ParseError(Exception):
def __init__(self, text):
self.text = text
def __str__(self):
return self.text
## MISC
def unpack_varint(s):
@ -38,17 +52,14 @@ def unpack_varint(s):
if not b & 0x80:
return d
pack_data = lambda d: struct.pack('>b', len(d)) + d
pack_port = lambda i: struct.pack('>H', i)
def pack_data(d):
return struct.pack('>b', len(d)) + d
def pack_port(i):
return struct.pack('>H', i)
## DATA FUNCTIONS
def mcping_modern(host, port):
""" pings a server using the modern (1.7+) protocol and returns formatted output """
""" pings a server using the modern (1.7+) protocol and returns data """
# connect to the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
@ -86,12 +97,19 @@ def mcping_modern(host, port):
from pprint import pprint
pprint(data)
return "Invalid data - check console ({})".format(e)
return mc_color_format(u"{}\x0f - {}\x0f - {}/{} players.".format(desc, version, online,
max_players)).replace("\n", u"\x0f - ")
output = {
"motd": format_colors(desc),
"motd_raw": desc,
"version": version,
"players": online,
"players_max": max_players
}
return output
def mcping_legacy(host, port):
""" pings a server using the legacy (1.6 and older) protocol and returns formatted output """
""" pings a server using the legacy (1.6 and older) protocol and returns data """
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
sock.send('\xfe\x01')
@ -105,16 +123,29 @@ def mcping_legacy(host, port):
if len(data) == 1:
# failed to decode data, server is using old format
data = values.split(u'\xa7')
message = u"{} - {}/{} players".format(mc_color_format(data[0]), data[1], data[2])
output = {
"motd": format_colors(data[0]),
"motd_raw": data[0],
"version": None,
"players": data[1],
"players_max": data[2]
}
else:
# decoded data, server is using new format
message = u"{} \x0f- {} - {}/{} players".format(mc_color_format(data[3]),
mc_color_format(data[2]), data[4], data[5])
output = {
"motd": format_colors(data[3]),
"motd_raw": data[3],
"version": data[2],
"players": data[4],
"players_max": data[5]
}
sock.close()
return message
return output
def get_srv_data(domain):
## FORMATTING/PARSING FUNCTIONS
def check_srv(domain):
""" takes a domain and finds minecraft SRV records """
DNS.DiscoverNameServers()
srv_req = DNS.Request(qtype='srv')
@ -130,61 +161,55 @@ def parse_input(inp):
""" takes the input from the mcping command and returns the host and port """
inp = inp.strip().split(" ")[0]
if ":" in inp:
# the port is defined in the input string
host, port = inp.split(":", 1)
try:
port = int(port)
except:
raise Exception("The port '{}' is invalid.".format(port))
if port > 65535 or port < 0:
raise ParseError("The port '{}' is invalid.".format(port))
except ValueError:
raise ParseError("The port '{}' is invalid.".format(port))
return host, port
if pydns_installed:
srv_data = get_srv_data(inp)
if has_dns:
# the port is not in the input string, but we have PyDNS so look for a SRV record
srv_data = check_srv(inp)
if srv_data:
return str(srv_data[1]), int(srv_data[0])
# return default port
return inp, 25565
@hook.command
@hook.command("mcp6")
def mcping6(inp):
"""mcping6 <server>[:port] - Ping a Minecraft server version 1.6 or smaller to check status."""
#try:
host, port = parse_input(inp)
#except Exception as ex:
# return ex.args[0]
try:
return mcping_legacy(host, port)
except:
return "The 1.6 server {}:{} looks offline from here.".format(host, port)
def format_colors(motd):
for original, replacement in mc_colors:
motd = motd.replace(original, replacement)
motd = motd.replace(u"\xa7k", "")
return motd
@hook.command
@hook.command("mcp7")
def mcping7(inp):
"""mcping <server>[:port] - Ping a Minecraft server version 1.7 or greater to check status."""
try:
host, port = parse_input(inp)
except Exception as ex:
return ex.args[0]
try:
return mcping_modern(host, port)
except:
return "The 1.7 server {}:{} looks offline from here.".format(host, port)
def format_output(data):
if data["version"]:
return u"{motd}\x0f - {version}\x0f - {players}/{players_max}" \
u" players.".format(**data).replace("\n", u"\x0f - ")
else:
return u"{motd}\x0f - {players}/{players_max}" \
u" players.".format(**data).replace("\n", u"\x0f - ")
@hook.command
@hook.command("mcp")
def mcping(inp):
"""mcping <server>[:port] - Ping a Minecraft server to check status."""
# try:
host, port = parse_input(inp)
#except Exception as e:
# return e.args[0]
#
try:
host, port = parse_input(inp)
except ParseError as e:
return "Could not parse input: {}".format(e)
try:
return mcping_modern(host, port)
except:
data = mcping_modern(host, port)
return format_output(data)
except PingError:
try:
return mcping_legacy(host, port)
except:
data = mcping_legacy(host, port)
return format_output(data)
except PingError:
return "The server {} ({}:{}) looks offline from here.".format(inp, host, port)