From a5a5fd2dfb0c47b4253eee7b0e785f0b3e5341e6 Mon Sep 17 00:00:00 2001 From: Dabo Ross Date: Wed, 27 Nov 2013 03:24:11 -0800 Subject: [PATCH 01/21] Update minecraft_ping to 1.7, add commands mcping6 and mcping7 for version-specific pings, having mcping try 7 and revert back to 6 is failed. --- plugins/minecraft_ping.py | 195 ++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 58 deletions(-) diff --git a/plugins/minecraft_ping.py b/plugins/minecraft_ping.py index 3745356..534451e 100644 --- a/plugins/minecraft_ping.py +++ b/plugins/minecraft_ping.py @@ -1,6 +1,8 @@ from util import hook import socket import struct +import json + try: import DNS @@ -9,48 +11,97 @@ try: except ImportError: pydns_installed = 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"] -def format_motd(motd): - empty = "" - colors = [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"] - for s in colors: - lcol = s.split(",") - motd = motd.replace(lcol[1], lcol[0]) - motd = motd.replace(u"\xa7k", empty) + +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 -def mcping_connect(host, port): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +def unpack_varint(s): + d = 0 + i = 0 + while True: + b = ord(s.recv(1)) + d |= (b & 0x7F) << 7 * i + i += 1 + if not b & 0x80: + return d + + +def pack_data(d): + return struct.pack('>b', len(d)) + d + + +def pack_port(i): + return struct.pack('>H', i) + + +def mc_17_ping_to_json(host, port): + + # Connect + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, port)) + + # Send handshake + status request + s.send(pack_data("\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + "\x01")) + s.send(pack_data("\x00")) + + # Read response + unpack_varint(s) # Packet length + unpack_varint(s) # Packet ID + l = unpack_varint(s) # String length + + d = "" + while len(d) < l: + d += s.recv(1024) + + # Close our socket + s.close() + + # Load json and return + return json.loads(d.decode('utf8')) + + +def mcping_17(host, port): + data = mc_17_ping_to_json(host, port) try: - sock.connect((host, port)) - sock.send('\xfe\x01') - response = sock.recv(1) - print response + version = data["version"]["name"] + desc = data["description"] + max = data["players"]["max"] + online = data["players"]["online"] + except Exception as e: + return "Invalid data: {}; error: {}".format(data, e) + return mc_color_format(u"{}\x0f - {}\x0f - {}/{} players".format(desc, version, online, max)).replace("\n", u"\x0f - ") - if response[0] != '\xff': - return "Server gave invalid response: " + repr(response) - length = struct.unpack('!h', sock.recv(2))[0] - values = sock.recv(length * 2).decode('utf-16be') - - data = values.split(u'\x00') # try to decode data using new format - if len(data) == 1: - # failed to decode data, server is using old format - data = values.split(u'\xa7') - message = u"{} - {}/{} players".format(data[0], data[1], data[2]) - else: - # decoded data, server is using new format - message = u"{} \x0f- {} - {}/{} players".format(data[3], data[2], data[4], data[5]) - - sock.close() - return message - - except: - return "Error pinging {}:{}, is it up? Double-check your address!".format(host, str(port)) +def mcping_16(host, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((host, port)) + sock.send('\xfe\x01') + response = sock.recv(1) + print response + if response[0] != '\xff': + return "Server gave invalid response: " + repr(response) + length = struct.unpack('!h', sock.recv(2))[0] + values = sock.recv(length * 2).decode('utf-16be') + data = values.split(u'\x00') # try to decode data using new format + 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]) + 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]) + sock.close() + return message def srvData(domain): @@ -64,34 +115,62 @@ def srvData(domain): return data -@hook.command -def mcping(inp): - """mcping [:port] - Ping a Minecraft server to check status.""" +def get_host_and_port(inp): inp = inp.strip().split(" ")[0] - if ":" in inp: host, port = inp.split(":", 1) try: port = int(port) except: - return "error: invalid port!" - return format_motd(mcping_connect(host, port)) + raise Exception("The port '{}' is invalid.".format(port)) + return host, port + elif pydns_installed: + srv_data = srvData(inp) + if srv_data: + return str(srv_data[1]), int(srv_data[0]) + return inp, 25565 - else: - host = inp - port = 25565 - rdata = format_motd(mcping_connect(host, port)) - if 'is it up' in rdata: - if pydns_installed: - getdata = srvData(inp) - try: - host = str(getdata[1]) - port = int(getdata[0]) - return format_motd(mcping_connect(host, port)) - except: - return "Error pinging {}, is it up? Double-check your address!".format(inp) - else: - return "Error pinging {}, is it up? Double-check your address!".format(inp) - else: - return rdata +@hook.command +@hook.command("mcp6") +def mcping6(inp): + """mcping6 [:port] - Ping a Minecraft server version 1.6 or smaller to check status.""" + try: + host, port = get_host_and_port(inp) + except Exception as ex: + return ex.args[0] + try: + return mcping_16(host, port) + except Exception as e: + return "The 1.6 server {}:{} looks offline from here.".format(host, port) + + +@hook.command +@hook.command("mcp7") +def mcping7(inp): + """mcping [:port] - Ping a Minecraft server version 1.7 or greater to check status.""" + try: + host, port = get_host_and_port(inp) + except Exception as ex: + return ex.args[0] + try: + return mcping_17(host, port) + except Exception as e: + return "The 1.7 server {}:{} looks offline from here.".format(host, port) + + +@hook.command +@hook.command("mcp") +def mcping(inp): + """mcping [:port] - Ping a Minecraft server to check status.""" + try: + host, port = get_host_and_port(inp) + except Exception as ex: + return ex.args[0] + try: + return mcping_17(host, port) + except: + try: + return mcping_16(host, port) + except Exception as e: + return "The 1.6/1.7 server {}:{} looks offline from here.".format(host, port) From 13c7e5915b0394841c426c4dbb82d800db33b73c Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 00:04:28 +1300 Subject: [PATCH 02/21] rouunndddinng --- plugins/bitcoin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/bitcoin.py b/plugins/bitcoin.py index b06e3ee..59c1cba 100755 --- a/plugins/bitcoin.py +++ b/plugins/bitcoin.py @@ -14,11 +14,12 @@ def bitcoin(inp, message=None): } message("Current: \x0307{!s}\x0f - High: \x0307{!s}\x0f" " - Low: \x0307{!s}\x0f - Volume: {!s}".format(ticker['buy'], ticker['high'], ticker['low'], ticker['vol'])) - + + @hook.command(autohelp=False) def litecoin(inp, message=None): """litecoin -- gets current exchange rate for litecoins from BTC-E""" data = http.get_json("https://btc-e.com/api/2/ltc_usd/ticker") ticker = data['ticker'] - message("Current: \x0307${!s}\x0f - High: \x0307${!s}\x0f" - " - Low: \x0307${!s}\x0f - Volume: {!s}LTC".format(ticker['buy'],ticker['high'],ticker['low'],ticker['vol_cur'])) + message("Current: \x0307${:.2f}\x0f - High: \x0307${:.2f}\x0f" + " - Low: \x0307${:.2f}\x0f - Volume: {:.2f}LTC".format(ticker['buy'], ticker['high'], ticker['low'], ticker['vol_cur'])) From 1ecdd3a2462404328456432e2cffda7cd5542721 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 00:36:54 +1300 Subject: [PATCH 03/21] magic? --- plugins/bitcoin.py | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/plugins/bitcoin.py b/plugins/bitcoin.py index 59c1cba..a2826ca 100755 --- a/plugins/bitcoin.py +++ b/plugins/bitcoin.py @@ -1,19 +1,41 @@ from util import http, hook +@hook.command("btc", autohelp=False) @hook.command(autohelp=False) -def bitcoin(inp, message=None): - """bitcoin -- gets current exchange rate for bitcoins from mtgox""" - data = http.get_json("https://data.mtgox.com/api/2/BTCUSD/money/ticker") - data = data['data'] - ticker = { - 'buy': data['buy']['display_short'].encode('ascii', 'ignore'), - 'high': data['high']['display_short'].encode('ascii', 'ignore'), - 'low': data['low']['display_short'].encode('ascii', 'ignore'), - 'vol': data['vol']['display_short'].encode('ascii', 'ignore'), +def bitcoin(inp): + "bitcoin -- Gets current exchange rate for bitcoins from several exchanges, default is MtGox. Supports MtGox, Bitpay, and BitStamp." + exchanges = { + "mtgox": { + "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", + "func": lambda data: u"\x02MtGox\x02 // Current: {}, High: {}, Low: {}, Best Ask: {}, Volume: {}".format(data['return']['last']['display'], \ + data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ + data['return']['vol']['display']) + }, + "bitpay": { + "api_url": "https://bitpay.com/api/rates", + "func": lambda data: u"\x02Bitpay\x02 // Current: ${}".format(data[0]['rate']) + }, + "bitstamp": { + "api_url": "https://www.bitstamp.net/api/ticker/", + "func": lambda data: u"\x02BitStamp\x02 // Current: ${}, High: ${}, Low: ${}, Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ + float(data['volume'])) + } } - message("Current: \x0307{!s}\x0f - High: \x0307{!s}\x0f" - " - Low: \x0307{!s}\x0f - Volume: {!s}".format(ticker['buy'], ticker['high'], ticker['low'], ticker['vol'])) + + inp = inp.lower() + + if inp: + if inp in exchanges: + exchange = exchanges[inp] + else: + return "Invalid Exchange" + else: + exchange = exchanges["mtgox"] + + data = http.get_json(exchange["api_url"]) + func = exchange["func"] + return func(data) @hook.command(autohelp=False) From 181b43df718ef1d918968f8dc4e706fb699c054c Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 01:03:34 +1300 Subject: [PATCH 04/21] gold k --- plugins/bitcoin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/bitcoin.py b/plugins/bitcoin.py index a2826ca..2a88b84 100755 --- a/plugins/bitcoin.py +++ b/plugins/bitcoin.py @@ -8,17 +8,17 @@ def bitcoin(inp): exchanges = { "mtgox": { "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", - "func": lambda data: u"\x02MtGox\x02 // Current: {}, High: {}, Low: {}, Best Ask: {}, Volume: {}".format(data['return']['last']['display'], \ + "func": lambda data: u"\x02MtGox\x02 // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ data['return']['vol']['display']) }, "bitpay": { "api_url": "https://bitpay.com/api/rates", - "func": lambda data: u"\x02Bitpay\x02 // Current: ${}".format(data[0]['rate']) + "func": lambda data: u"\x02Bitpay\x02 // Current: \x0307${}\x0f".format(data[0]['rate']) }, "bitstamp": { "api_url": "https://www.bitstamp.net/api/ticker/", - "func": lambda data: u"\x02BitStamp\x02 // Current: ${}, High: ${}, Low: ${}, Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ + "func": lambda data: u"\x02BitStamp\x02 // Current: \x0307${}\x0f - High: \x0307${}\x0f - Low: \x0307${}\x0f - Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ float(data['volume'])) } } From f95b60791497ae618681ea779d328b07e1394ecd Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 01:06:12 +1300 Subject: [PATCH 05/21] cbf --- plugins/bitcoin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/bitcoin.py b/plugins/bitcoin.py index 2a88b84..277b822 100755 --- a/plugins/bitcoin.py +++ b/plugins/bitcoin.py @@ -8,17 +8,17 @@ def bitcoin(inp): exchanges = { "mtgox": { "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", - "func": lambda data: u"\x02MtGox\x02 // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ + "func": lambda data: u"MtGox // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ data['return']['vol']['display']) }, "bitpay": { "api_url": "https://bitpay.com/api/rates", - "func": lambda data: u"\x02Bitpay\x02 // Current: \x0307${}\x0f".format(data[0]['rate']) + "func": lambda data: u"Bitpay // Current: \x0307${:.2f}\x0f".format(data[0]['rate']) }, "bitstamp": { "api_url": "https://www.bitstamp.net/api/ticker/", - "func": lambda data: u"\x02BitStamp\x02 // Current: \x0307${}\x0f - High: \x0307${}\x0f - Low: \x0307${}\x0f - Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ + "func": lambda data: u"BitStamp // Current: \x0307${}\x0f - High: \x0307${}\x0f - Low: \x0307${}\x0f - Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ float(data['volume'])) } } From 2b5f5e925db811280d6f9e5971b3c79647a213b4 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 01:14:58 +1300 Subject: [PATCH 06/21] v --- plugins/bitcoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/bitcoin.py b/plugins/bitcoin.py index 277b822..956c0f2 100755 --- a/plugins/bitcoin.py +++ b/plugins/bitcoin.py @@ -8,7 +8,7 @@ def bitcoin(inp): exchanges = { "mtgox": { "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", - "func": lambda data: u"MtGox // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ + "func": lambda data: u"MtGox // Current: \x0307{}\x0f High: \x0307{}\x0f Low: \x0307{}\x0f Best Ask: \x0307{}\x0f Volume: {}".format(data['return']['last']['display'], \ data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ data['return']['vol']['display']) }, From 29b25e0b1f9406d4ab7f158943cf98e1154a7e98 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 11:13:11 +1300 Subject: [PATCH 07/21] Tweaks to twitter --- plugins/{bitcoin.py => coins.py} | 0 plugins/twitter.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) rename plugins/{bitcoin.py => coins.py} (100%) mode change 100755 => 100644 diff --git a/plugins/bitcoin.py b/plugins/coins.py old mode 100755 new mode 100644 similarity index 100% rename from plugins/bitcoin.py rename to plugins/coins.py diff --git a/plugins/twitter.py b/plugins/twitter.py index 256ff71..4bf5fce 100755 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -35,7 +35,7 @@ def twitter(inp, bot=None): if e[0][0]['code'] == 34: return "Could not find tweet." else: - return "Error {}: {}".format(e[0][0]['code'], e[0][0]['message']) + return u"Error {}: {}".format(e[0][0]['code'], e[0][0]['message']) user = tweet.user @@ -59,21 +59,21 @@ def twitter(inp, bot=None): if e[0][0]['code'] == 34: return "Could not find user." else: - return "Error {}: {}".format(e[0][0]['code'], e[0][0]['message']) + return u"Error {}: {}".format(e[0][0]['code'], e[0][0]['message']) # get the users tweets user_timeline = api.user_timeline(id=user.id, count=tweet_number + 1) # if the timeline is empty, return an error if not user_timeline: - return "The user \x02{}\x02 has no tweets.".format(user.screen_name) + return u"The user \x02{}\x02 has no tweets.".format(user.screen_name) # grab the newest tweet from the users timeline try: tweet = user_timeline[tweet_number] except IndexError: tweet_count = len(user_timeline) - return "The user \x02{}\x02 only has \x02{}\x02 tweets.".format(user.screen_name, tweet_count) + return u"The user \x02{}\x02 only has \x02{}\x02 tweets.".format(user.screen_name, tweet_count) elif re.match(r'^#\w+$', inp): # user is searching by hashtag @@ -88,7 +88,7 @@ def twitter(inp, bot=None): text = " ".join(tweet.text.split()) if user.verified: - prefix = "+" + prefix = u"\u2713 " else: prefix = "" @@ -126,17 +126,17 @@ def twuser(inp, bot=None): return "Unknown error" if user.verified: - prefix = "+" + prefix = u"\u2713 " else: prefix = "" if user.location: - loc_str = " is located in \x02{}\x02 and".format(user.location) + loc_str = u" is located in \x02{}\x02 and".format(user.location) else: loc_str = "" if user.description: - desc_str = " The users description is \"{}\"".format(user.description) + desc_str = u" The users description is \"{}\"".format(user.description) else: desc_str = "" From e75fe73563bede40d1875994ff4ade7f50772d5c Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 11:18:25 +1300 Subject: [PATCH 08/21] wop --- plugins/twitter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/twitter.py b/plugins/twitter.py index 4bf5fce..cd1b2a0 100755 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -88,7 +88,7 @@ def twitter(inp, bot=None): text = " ".join(tweet.text.split()) if user.verified: - prefix = u"\u2713 " + prefix = u"\u2713" else: prefix = "" @@ -126,7 +126,7 @@ def twuser(inp, bot=None): return "Unknown error" if user.verified: - prefix = u"\u2713 " + prefix = u"\u2713" else: prefix = "" From 9bc72bc57f3b8d13bab711aac07ec11a2bf3b49d Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 11:45:18 +1300 Subject: [PATCH 09/21] more mcping stuff --- plugins/minecraft_ping.py | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/plugins/minecraft_ping.py b/plugins/minecraft_ping.py index 534451e..3cc2add 100644 --- a/plugins/minecraft_ping.py +++ b/plugins/minecraft_ping.py @@ -44,20 +44,21 @@ def pack_port(i): return struct.pack('>H', i) -def mc_17_ping_to_json(host, port): - - # Connect +def mcping_modern(host, port): + """ pings a server using the modern (1.7+) protocol and returns formatted output """ + # connect to the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) - # Send handshake + status request + # send handshake + status request s.send(pack_data("\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + "\x01")) s.send(pack_data("\x00")) - # Read response + # read response unpack_varint(s) # Packet length unpack_varint(s) # Packet ID l = unpack_varint(s) # String length + print l d = "" while len(d) < l: @@ -67,11 +68,7 @@ def mc_17_ping_to_json(host, port): s.close() # Load json and return - return json.loads(d.decode('utf8')) - - -def mcping_17(host, port): - data = mc_17_ping_to_json(host, port) + data = json.loads(d.decode('utf8')) try: version = data["version"]["name"] desc = data["description"] @@ -79,10 +76,11 @@ def mcping_17(host, port): online = data["players"]["online"] except Exception as e: return "Invalid data: {}; error: {}".format(data, e) - return mc_color_format(u"{}\x0f - {}\x0f - {}/{} players".format(desc, version, online, max)).replace("\n", u"\x0f - ") + return mc_color_format(u"{}\x0f - {}\x0f - {}/{} players *".format(desc, version, online, max)).replace("\n", u"\x0f - ") -def mcping_16(host, port): +def mcping_legacy(host, port): + """ pings a server using the legacy (1.6 and older) protocol and returns formatted output """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) sock.send('\xfe\x01') @@ -104,7 +102,8 @@ def mcping_16(host, port): return message -def srvData(domain): +def get_srv_data(domain): + """ takes a domain and finds minecraft SRV records """ DNS.ParseResolvConf() srv_req = DNS.Request(qtype='srv') srv_result = srv_req.req('_minecraft._tcp.{}'.format(domain)) @@ -115,7 +114,8 @@ def srvData(domain): return data -def get_host_and_port(inp): +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: host, port = inp.split(":", 1) @@ -124,8 +124,8 @@ def get_host_and_port(inp): except: raise Exception("The port '{}' is invalid.".format(port)) return host, port - elif pydns_installed: - srv_data = srvData(inp) + if pydns_installed: + srv_data = get_srv_data(inp) if srv_data: return str(srv_data[1]), int(srv_data[0]) return inp, 25565 @@ -136,12 +136,12 @@ def get_host_and_port(inp): def mcping6(inp): """mcping6 [:port] - Ping a Minecraft server version 1.6 or smaller to check status.""" try: - host, port = get_host_and_port(inp) + host, port = parse_input(inp) except Exception as ex: return ex.args[0] try: - return mcping_16(host, port) - except Exception as e: + return mcping_legacy(host, port) + except: return "The 1.6 server {}:{} looks offline from here.".format(host, port) @@ -150,12 +150,12 @@ def mcping6(inp): def mcping7(inp): """mcping [:port] - Ping a Minecraft server version 1.7 or greater to check status.""" try: - host, port = get_host_and_port(inp) + host, port = parse_input(inp) except Exception as ex: return ex.args[0] try: - return mcping_17(host, port) - except Exception as e: + return mcping_modern(host, port) + except: return "The 1.7 server {}:{} looks offline from here.".format(host, port) @@ -164,13 +164,13 @@ def mcping7(inp): def mcping(inp): """mcping [:port] - Ping a Minecraft server to check status.""" try: - host, port = get_host_and_port(inp) - except Exception as ex: + host, port = parse_input(inp) + except Exception as e: return ex.args[0] try: - return mcping_17(host, port) - except: + return mcping_modern(host, port) + except socket.error: try: - return mcping_16(host, port) - except Exception as e: - return "The 1.6/1.7 server {}:{} looks offline from here.".format(host, port) + return mcping_legacy(host, port) + except: + return "The server {}:{} looks offline from here.".format(host, port) From d78f96b3eccf75be5fec759c90d9882fdbb59aba Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 11:55:07 +1300 Subject: [PATCH 10/21] . --- plugins/coins.py | 2 +- plugins/minecraft_ping.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/coins.py b/plugins/coins.py index 956c0f2..277b822 100644 --- a/plugins/coins.py +++ b/plugins/coins.py @@ -8,7 +8,7 @@ def bitcoin(inp): exchanges = { "mtgox": { "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", - "func": lambda data: u"MtGox // Current: \x0307{}\x0f High: \x0307{}\x0f Low: \x0307{}\x0f Best Ask: \x0307{}\x0f Volume: {}".format(data['return']['last']['display'], \ + "func": lambda data: u"MtGox // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ data['return']['vol']['display']) }, diff --git a/plugins/minecraft_ping.py b/plugins/minecraft_ping.py index 3cc2add..8752639 100644 --- a/plugins/minecraft_ping.py +++ b/plugins/minecraft_ping.py @@ -166,11 +166,11 @@ def mcping(inp): try: host, port = parse_input(inp) except Exception as e: - return ex.args[0] + return e.args[0] try: return mcping_modern(host, port) except socket.error: try: return mcping_legacy(host, port) except: - return "The server {}:{} looks offline from here.".format(host, port) + return "The server {} ({}:{}) looks offline from here.".format(inp, host, port) From 2d5208cbdeb8e430c39bf694ab2a6ddc4276b1fb Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 16:18:18 +1300 Subject: [PATCH 11/21] commacommacommacommacommacommacomma --- plugins/coins.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/coins.py b/plugins/coins.py index 277b822..b40f31f 100644 --- a/plugins/coins.py +++ b/plugins/coins.py @@ -14,11 +14,11 @@ def bitcoin(inp): }, "bitpay": { "api_url": "https://bitpay.com/api/rates", - "func": lambda data: u"Bitpay // Current: \x0307${:.2f}\x0f".format(data[0]['rate']) + "func": lambda data: u"Bitpay // Current: \x0307${:,.2f}\x0f".format(data[0]['rate']) }, "bitstamp": { "api_url": "https://www.bitstamp.net/api/ticker/", - "func": lambda data: u"BitStamp // Current: \x0307${}\x0f - High: \x0307${}\x0f - Low: \x0307${}\x0f - Volume: {:.2f} BTC".format(data['last'], data['high'], data['low'], \ + "func": lambda data: u"BitStamp // Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f - Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} BTC".format(float(data['last']), float(data['high']), float(data['low']), \ float(data['volume'])) } } @@ -43,5 +43,5 @@ def litecoin(inp, message=None): """litecoin -- gets current exchange rate for litecoins from BTC-E""" data = http.get_json("https://btc-e.com/api/2/ltc_usd/ticker") ticker = data['ticker'] - message("Current: \x0307${:.2f}\x0f - High: \x0307${:.2f}\x0f" - " - Low: \x0307${:.2f}\x0f - Volume: {:.2f}LTC".format(ticker['buy'], ticker['high'], ticker['low'], ticker['vol_cur'])) + message("Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f" + " - Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} LTC".format(ticker['buy'], ticker['high'], ticker['low'], ticker['vol_cur'])) From 85d8c4e5cbf1ad06757f52af8d63d62dcbac63db Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 16:30:06 +1300 Subject: [PATCH 12/21] switched youtube to like/dislike --- plugins/youtube.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/youtube.py b/plugins/youtube.py index 4f2675e..08dcc24 100755 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -34,9 +34,14 @@ def get_video_description(video_id): out += '%dm ' % (length / 60 % 60) out += "%ds\x02" % (length % 60) - if 'rating' in data: - out += ' - rated \x02%.2f/5.0\x02 (%d)' % (data['rating'], - data['ratingCount']) + if 'ratingCount' in data: + ratings = data['ratingCount'] + likes = int(data['likeCount']) + dislikes = ratings - likes + + percent = 100 * float(likes)/float(ratings) + out += ' - {} likes, {} dislikes (\x02{:.1f}\x02%)'.format(likes, + dislikes, percent) if 'viewCount' in data: out += ' - \x02%s\x02 views' % format(data['viewCount'], ",d") From 0bb1574ebd61f5aeb7bc9b25951b9ae98989ff0a Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 16:35:41 +1300 Subject: [PATCH 13/21] plurals and fanciness --- plugins/youtube.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/plugins/youtube.py b/plugins/youtube.py index 08dcc24..3097614 100755 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -13,6 +13,10 @@ search_api_url = base_url + 'videos?v=2&alt=jsonc&max-results=1' video_url = "http://youtu.be/%s" +def plural(num=0, text=''): + return "%d %s%s" % (num, text, "s"[num==1:]) + + def get_video_description(video_id): request = http.get_json(api_url.format(video_id)) @@ -35,12 +39,11 @@ def get_video_description(video_id): out += "%ds\x02" % (length % 60) if 'ratingCount' in data: - ratings = data['ratingCount'] - likes = int(data['likeCount']) - dislikes = ratings - likes + likes = plural(int(data['likeCount']), "like") + dislikes = plural(data['ratingCount'] - int(data['likeCount']), "dislike") - percent = 100 * float(likes)/float(ratings) - out += ' - {} likes, {} dislikes (\x02{:.1f}\x02%)'.format(likes, + percent = 100 * float(data['likeCount'])/float(data['ratingCount']) + out += ' - {}, {} (\x02{:.1f}\x02%)'.format(likes, dislikes, percent) if 'viewCount' in data: @@ -61,16 +64,6 @@ def get_video_description(video_id): return out -def GetInHMS(seconds): - hours = seconds / 3600 - seconds -= 3600 * hours - minutes = seconds / 60 - seconds -= 60 * minutes - if hours == 0: - return "%02d:%02d" % (minutes, seconds) - return "%02d:%02d:%02d" % (hours, minutes, seconds) - - @hook.regex(*youtube_re) def youtube_url(match): return get_video_description(match.group(1)) From 44c7bceaf46af593295e4302227a0dbf602a4d4a Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 18:16:17 +1300 Subject: [PATCH 14/21] workaround --- plugins/minecraft_ping.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/minecraft_ping.py b/plugins/minecraft_ping.py index 8752639..423c714 100644 --- a/plugins/minecraft_ping.py +++ b/plugins/minecraft_ping.py @@ -3,7 +3,6 @@ import socket import struct import json - try: import DNS # Please remember to install the dependancy 'pydns' @@ -11,6 +10,7 @@ try: except ImportError: pydns_installed = 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", @@ -58,7 +58,9 @@ def mcping_modern(host, port): unpack_varint(s) # Packet length unpack_varint(s) # Packet ID l = unpack_varint(s) # String length - print l + + if not l > 1: + raise Exception d = "" while len(d) < l: @@ -167,9 +169,10 @@ def mcping(inp): host, port = parse_input(inp) except Exception as e: return e.args[0] + try: return mcping_modern(host, port) - except socket.error: + except: try: return mcping_legacy(host, port) except: From 49bb446c272af32492d030507e9e70500b395e82 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 20:00:41 +1300 Subject: [PATCH 15/21] Added newegg item search command! --- plugins/newegg.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 plugins/newegg.py diff --git a/plugins/newegg.py b/plugins/newegg.py new file mode 100644 index 0000000..621f12b --- /dev/null +++ b/plugins/newegg.py @@ -0,0 +1,61 @@ +from util import hook, http, text, web +import json + + +ITEM_URL = "http://www.newegg.com/Product/Product.aspx?Item={}" + +@hook.command +def newegg(inp): + """ newegg -- Searches newegg.com for """ + + request = { + "PageNumber": 1, + "BrandId": -1, + "NValue": "", + "StoreDepaId": -1, + "NodeId": -1, + "Keyword": inp, + "IsSubCategorySearch": False, + "SubCategoryId": -1, + "Sort": "FEATURED", + "CategoryId": -1, + "IsUPCCodeSearch": False + } + + r = http.get_json( + 'http://www.ows.newegg.com/Search.egg/Advanced', + post_data = json.dumps(request) + ) + + item = r["ProductListItems"][0] + + data = { + 'title': text.truncate_str(item["Title"], 50), + 'rating': item["ReviewSummary"]["Rating"], + 'ratingcount': item["ReviewSummary"]["TotalReviews"][1:-1] + } + print item + + if not item["FinalPrice"] == item["OriginalPrice"]: + data['price'] = "{FinalPrice}, was {OriginalPrice}".format(**item) + else: + data['price'] = item["FinalPrice"] + + tags = [] + + if item["Instock"]: + tags.append("\x02Stock Available\x02") + else: + tags.append("\x02Out Of Stock\x02") + + if item["FreeShippingFlag"]: + tags.append("\x02Free Shipping\x02") + + if item["IsFeaturedItem"]: + tags.append("\x02Featured\x02") + + data["tags"] = ", ".join(tags) + + data["url"] = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"])) + + return "\x02{title}\x02 ({price}) - Rated {rating}/5 ({ratingcount} ratings) - {tags} - {url}".format(**data) \ No newline at end of file From 8d7f85342629a076b1a229e9848d494941c38cbf Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 20:40:18 +1300 Subject: [PATCH 16/21] Furthere tweaks to newegg --- plugins/newegg.py | 53 ++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/plugins/newegg.py b/plugins/newegg.py index 621f12b..91af6e3 100644 --- a/plugins/newegg.py +++ b/plugins/newegg.py @@ -1,25 +1,25 @@ from util import hook, http, text, web import json - ITEM_URL = "http://www.newegg.com/Product/Product.aspx?Item={}" + @hook.command def newegg(inp): - """ newegg -- Searches newegg.com for """ + """newegg -- Searches newegg.com for """ request = { - "PageNumber": 1, - "BrandId": -1, - "NValue": "", - "StoreDepaId": -1, - "NodeId": -1, - "Keyword": inp, - "IsSubCategorySearch": False, - "SubCategoryId": -1, - "Sort": "FEATURED", - "CategoryId": -1, - "IsUPCCodeSearch": False + "PageNumber": 1, + "BrandId": -1, + "NValue": "", + "StoreDepaId": -1, + "NodeId": -1, + "Keyword": inp, + "IsSubCategorySearch": False, + "SubCategoryId": -1, + "Sort": "FEATURED", + "CategoryId": -1, + "IsUPCCodeSearch": False } r = http.get_json( @@ -29,17 +29,18 @@ def newegg(inp): item = r["ProductListItems"][0] - data = { - 'title': text.truncate_str(item["Title"], 50), - 'rating': item["ReviewSummary"]["Rating"], - 'ratingcount': item["ReviewSummary"]["TotalReviews"][1:-1] - } - print item + title = text.truncate_str(item["Title"], 50) + + if not item["ReviewSummary"]["TotalReviews"] == "[]": + rating = "Rated {}/5 ({} ratings)".format(item["ReviewSummary"]["Rating"], + item["ReviewSummary"]["TotalReviews"][1:-1]) + else: + rating = "No Ratings" if not item["FinalPrice"] == item["OriginalPrice"]: - data['price'] = "{FinalPrice}, was {OriginalPrice}".format(**item) + price = "{FinalPrice}, was {OriginalPrice}".format(**item) else: - data['price'] = item["FinalPrice"] + price = item["FinalPrice"] tags = [] @@ -54,8 +55,12 @@ def newegg(inp): if item["IsFeaturedItem"]: tags.append("\x02Featured\x02") - data["tags"] = ", ".join(tags) + if item["IsShellShockerItem"]: + tags.append("\x02SHELL SHOCKER®\x02") - data["url"] = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"])) + tag_text = u", ".join(tags) - return "\x02{title}\x02 ({price}) - Rated {rating}/5 ({ratingcount} ratings) - {tags} - {url}".format(**data) \ No newline at end of file + url = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"])) + + return u"\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating, + tag_text, url) \ No newline at end of file From 9c7e2074f518fb2e7f7588e365edf257344b382a Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 21:59:48 +1300 Subject: [PATCH 17/21] comments! --- plugins/newegg.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/newegg.py b/plugins/newegg.py index 91af6e3..9c85b9c 100644 --- a/plugins/newegg.py +++ b/plugins/newegg.py @@ -8,6 +8,7 @@ ITEM_URL = "http://www.newegg.com/Product/Product.aspx?Item={}" def newegg(inp): """newegg -- Searches newegg.com for """ + # form the search request request = { "PageNumber": 1, "BrandId": -1, @@ -22,15 +23,18 @@ def newegg(inp): "IsUPCCodeSearch": False } + # submit the search request r = http.get_json( 'http://www.ows.newegg.com/Search.egg/Advanced', post_data = json.dumps(request) ) + # get the first result item = r["ProductListItems"][0] title = text.truncate_str(item["Title"], 50) + # format the rating nicely if it exists if not item["ReviewSummary"]["TotalReviews"] == "[]": rating = "Rated {}/5 ({} ratings)".format(item["ReviewSummary"]["Rating"], item["ReviewSummary"]["TotalReviews"][1:-1]) @@ -58,8 +62,10 @@ def newegg(inp): if item["IsShellShockerItem"]: tags.append("\x02SHELL SHOCKER®\x02") + # join all the tags together in a comma seperated string ("tag1, tag2, tag3") tag_text = u", ".join(tags) + # create the item URL and shorten it url = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"])) return u"\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating, From 3825c94c9d13e5616b5996f585acc95117c9a870 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Fri, 29 Nov 2013 22:08:38 +1300 Subject: [PATCH 18/21] im feeling bold --- plugins/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stock.py b/plugins/stock.py index 154e2ff..d80fe05 100755 --- a/plugins/stock.py +++ b/plugins/stock.py @@ -25,7 +25,7 @@ def stock(inp): quote['PercentChange'] = 100 * change / (price - change) - ret = "%(Name)s - %(LastTradePriceOnly)s " \ + ret = "\x02%(Name)s\x02 (\x02%(symbol)s\x02) - %(LastTradePriceOnly)s " \ "\x03%(color)s%(Change)s (%(PercentChange).2f%%)\x03 " \ "Day Range: %(DaysRange)s " \ "MCAP: %(MarketCapitalization)s" % quote From d3fbf1bc804f2bf0d4d3806e692235d3aeef9902 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Sat, 30 Nov 2013 00:05:55 +1300 Subject: [PATCH 19/21] bitbitbit --- plugins/coins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/coins.py b/plugins/coins.py index b40f31f..4aac685 100644 --- a/plugins/coins.py +++ b/plugins/coins.py @@ -6,6 +6,11 @@ from util import http, hook def bitcoin(inp): "bitcoin -- Gets current exchange rate for bitcoins from several exchanges, default is MtGox. Supports MtGox, Bitpay, and BitStamp." exchanges = { + "blockchain": { + "api_url": "https://blockchain.info/ticker", + "func": lambda data: u"Blockchain // Buy: \x0307${:,.2f}\x0f - Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], \ + data["USD"]["sell"]) + }, "mtgox": { "api_url": "https://mtgox.com/api/1/BTCUSD/ticker", "func": lambda data: u"MtGox // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'], \ @@ -31,7 +36,7 @@ def bitcoin(inp): else: return "Invalid Exchange" else: - exchange = exchanges["mtgox"] + exchange = exchanges["blockchain"] data = http.get_json(exchange["api_url"]) func = exchange["func"] From 834cbaabd858df0c7a4b223faa75174923a5836b Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Sat, 30 Nov 2013 00:21:26 +1300 Subject: [PATCH 20/21] *puffs* --- plugins/coins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/coins.py b/plugins/coins.py index 4aac685..d566c07 100644 --- a/plugins/coins.py +++ b/plugins/coins.py @@ -1,10 +1,11 @@ from util import http, hook +@hook.command("butt", autohelp=False) @hook.command("btc", autohelp=False) @hook.command(autohelp=False) def bitcoin(inp): - "bitcoin -- Gets current exchange rate for bitcoins from several exchanges, default is MtGox. Supports MtGox, Bitpay, and BitStamp." + "bitcoin -- Gets current exchange rate for bitcoins from several exchanges, default is Blockchain. Supports MtGox, Bitpay, Coinbase and BitStamp." exchanges = { "blockchain": { "api_url": "https://blockchain.info/ticker", @@ -17,6 +18,10 @@ def bitcoin(inp): data['return']['high']['display'], data['return']['low']['display'], data['return']['buy']['display'], \ data['return']['vol']['display']) }, + "coinbase":{ + "api_url": "https://coinbase.com/api/v1/prices/spot_rate", + "func": lambda data: u"Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount'])) + }, "bitpay": { "api_url": "https://bitpay.com/api/rates", "func": lambda data: u"Bitpay // Current: \x0307${:,.2f}\x0f".format(data[0]['rate']) From 4ccfd1e3ca275f0537291500d305354041d9172a Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Sat, 30 Nov 2013 16:37:01 +1300 Subject: [PATCH 21/21] Newegg URL parser? :D --- plugins/newegg.py | 74 ++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/plugins/newegg.py b/plugins/newegg.py index 9c85b9c..ab667e1 100644 --- a/plugins/newegg.py +++ b/plugins/newegg.py @@ -1,37 +1,17 @@ from util import hook, http, text, web import json +import re ITEM_URL = "http://www.newegg.com/Product/Product.aspx?Item={}" +API_PRODUCT = "http://www.ows.newegg.com/Products.egg/{}/ProductDetails" +API_SEARCH = "http://www.ows.newegg.com/Search.egg/Advanced" -@hook.command -def newegg(inp): - """newegg -- Searches newegg.com for """ +NEWEGG_RE = (r"(?:(?:www.newegg.com|newegg.com)/Product/Product\.aspx\?Item=)([-_a-zA-Z0-9]+)", re.I) - # form the search request - request = { - "PageNumber": 1, - "BrandId": -1, - "NValue": "", - "StoreDepaId": -1, - "NodeId": -1, - "Keyword": inp, - "IsSubCategorySearch": False, - "SubCategoryId": -1, - "Sort": "FEATURED", - "CategoryId": -1, - "IsUPCCodeSearch": False - } - - # submit the search request - r = http.get_json( - 'http://www.ows.newegg.com/Search.egg/Advanced', - post_data = json.dumps(request) - ) - - # get the first result - item = r["ProductListItems"][0] +def format_item(item): + """ takes a newegg API item object and returns a description """ title = text.truncate_str(item["Title"], 50) # format the rating nicely if it exists @@ -69,4 +49,44 @@ def newegg(inp): url = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"])) return u"\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating, - tag_text, url) \ No newline at end of file + tag_text, url) + + +@hook.regex(*NEWEGG_RE) +def newegg_url(match): + item_id = match.group(1) + item = http.get_json(API_PRODUCT.format(item_id)) + return format_item(item) + + +@hook.command +def newegg(inp): + """newegg -- Searches newegg.com for """ + + # form the search request + request = { + "PageNumber": 1, + "BrandId": -1, + "NValue": "", + "StoreDepaId": -1, + "NodeId": -1, + "Keyword": inp, + "IsSubCategorySearch": False, + "SubCategoryId": -1, + "Sort": "FEATURED", + "CategoryId": -1, + "IsUPCCodeSearch": False + } + + # submit the search request + r = http.get_json( + 'http://www.ows.newegg.com/Search.egg/Advanced', + post_data = json.dumps(request) + ) + + # get the first result + item = r["ProductListItems"][0] + + return format_item(item) + +