semi-rewrote mcping

This commit is contained in:
Luke Rogers 2014-02-22 22:04:39 +13:00
parent 5008da1331
commit 13df70da1b

View file

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