From 51cad28ab366317828ab5eba71515984738eba92 Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Fri, 9 Nov 2018 14:48:19 -0500 Subject: [PATCH 1/9] Add tablist header and footer to mc!tab command * Waiting for upstream support from pyCraft so this uses my fork --- Pipfile | 2 +- Pipfile.lock | 26 +++++++++++++------------- docker-compose.yml | 1 + webhook-bridge.py | 27 ++++++++++++++++++++++++--- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Pipfile b/Pipfile index 58062e3..b311d68 100644 --- a/Pipfile +++ b/Pipfile @@ -6,7 +6,6 @@ name = "pypi" [dev-packages] [packages] -minecraft = {git = "https://github.com/ammaraskar/pyCraft.git"} requests = "*" cryptography = "*" future = "*" @@ -15,6 +14,7 @@ mcstatus = "*" quarry = "*" discord-py = {git = "https://github.com/Rapptz/discord.py.git", ref = "rewrite"} bidict = "*" +minecraft = {git = "https://github.com/starcraft66/pyCraft.git", ref = "feature/add-player-list-header-and-footer-packet"} [requires] python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock index 442f1f4..b22986a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "92f71d787f478e45589c4939466682bc725279f844ced3c62a92a4b5e21515e1" + "sha256": "21ac16059e4c1c4b6aa022bacaa0d0fdc42c09ea4277129278fc848c52742b46" }, "pipfile-spec": 6, "requires": { @@ -154,10 +154,10 @@ }, "future": { "hashes": [ - "sha256:eb6d4df04f1fb538c99f69c9a28b255d1ee4e825d479b9c62fc38c0cf38065a4" + "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8" ], "index": "pypi", - "version": "==0.17.0" + "version": "==0.17.1" }, "hyperlink": { "hashes": [ @@ -188,8 +188,8 @@ "version": "==2.2" }, "minecraft": { - "git": "https://github.com/ammaraskar/pyCraft.git", - "ref": "527f3d31468dbaf8c1cbfffd0cbf46e987d319a0" + "git": "https://github.com/starcraft66/pyCraft.git", + "ref": "154b3e30d4f3690a53b821f22b8c281c7ac893d5" }, "pyasn1": { "hashes": [ @@ -234,11 +234,11 @@ }, "requests": { "hashes": [ - "sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c", - "sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279" + "sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54", + "sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263" ], "index": "pypi", - "version": "==2.20.0" + "version": "==2.20.1" }, "service-identity": { "hashes": [ @@ -256,10 +256,10 @@ }, "sqlalchemy": { "hashes": [ - "sha256:c5951d9ef1d5404ed04bae5a16b60a0779087378928f997a294d1229c6ca4d3e" + "sha256:84412de3794acee05630e7788f25e80e81f78eb4837e7b71d0499129f660486a" ], "index": "pypi", - "version": "==1.2.12" + "version": "==1.2.13" }, "twisted": { "hashes": [ @@ -269,10 +269,10 @@ }, "urllib3": { "hashes": [ - "sha256:41c3db2fc01e5b907288010dec72f9d0a74e37d6994e6eb56849f59fea2265ae", - "sha256:8819bba37a02d143296a4d032373c4dd4aca11f6d4c9973335ca75f9c8475f59" + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" ], - "version": "==1.24" + "version": "==1.24.1" }, "zope.interface": { "hashes": [ diff --git a/docker-compose.yml b/docker-compose.yml index 8bddcf3..d0d5e6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,3 +7,4 @@ services: volumes: - ./:/app:Z - ./:/data:Z + - ../pyCraft:/pyCraft:Z diff --git a/webhook-bridge.py b/webhook-bridge.py index 5f223c2..dc3de5b 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -39,7 +39,8 @@ BOT_USERNAME = "" NEXT_MESSAGE_TIME = datetime.now(timezone.utc) PREVIOUS_MESSAGE = "" PLAYER_LIST = bidict() -MOTD = "Not yet implemented" +TAB_HEADER = "" +TAB_FOOTER = "" LAST_CONNECTION_TIME = datetime.now(timezone.utc) @@ -101,6 +102,13 @@ def remove_emoji(string): return emoji_pattern.sub(r'', string) +def strip_colour(string): + colour_pattern = re.compile( + u"\U000000A7" # selection symbol + ".", flags=re.UNICODE) + return colour_pattern.sub(r'', string) + + def setup_logging(level): if level.lower() == "debug": log_level = logging.DEBUG @@ -234,6 +242,18 @@ def main(): connection.register_packet_listener( handle_tab_list, clientbound.play.PlayerListItemPacket) + connection.register_packet_listener( + handle_player_list_header_and_footer_update, clientbound.play.PlayerListHeaderAndFooterPacket) + + def handle_player_list_header_and_footer_update(header_footer_packet): + global TAB_FOOTER, TAB_HEADER + logging.debug("Got Tablist H/F Update: header={}".format(header_footer_packet.header)) + logging.debug("Got Tablist H/F Update: footer={}".format(header_footer_packet.footer)) + # Strip out colour codes + + TAB_HEADER = strip_colour(json.loads(header_footer_packet.header)["text"]) + TAB_FOOTER = strip_colour(json.loads(header_footer_packet.footer)["text"]) + def handle_tab_list(tab_list_packet): logging.debug("Processing tab list packet") for action in tab_list_packet.actions: @@ -514,8 +534,9 @@ def main(): await message.author.create_dm() send_channel = message.author.dm_channel player_list = ", ".join(list(map(lambda x: x[1], PLAYER_LIST.items()))) - msg = "MOTD: {}\n" \ - "Players online: {}".format(MOTD, player_list) + msg = "{}\n" \ + "Players online: {}\n" \ + "{}".format(TAB_HEADER, player_list, TAB_FOOTER) await send_channel.send(msg) except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): From f050efeb63b5c94b699bb9d9f32a2599858e935e Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Fri, 9 Nov 2018 15:01:23 -0500 Subject: [PATCH 2/9] Always restart the bot and remove accidental line --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index d0d5e6e..ba3e5cc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,4 +7,4 @@ services: volumes: - ./:/app:Z - ./:/data:Z - - ../pyCraft:/pyCraft:Z + restart: always From debf2d9ff9ee3c36632d596a14796521f3ec372b Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Sat, 10 Nov 2018 02:13:56 -0500 Subject: [PATCH 3/9] Strip out bold italics and escape char in discord messages --- webhook-bridge.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index dc3de5b..a333235 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -102,6 +102,14 @@ def remove_emoji(string): return emoji_pattern.sub(r'', string) +def escape_markdown(string): + # Absolutely needs to go first or it will replace our escaping slashes! + string = string.replace("\\", "\\\\") + string = string.replace("_", "\\_") + string = string.replace("*", "\\*") + return string + + def strip_colour(string): colour_pattern = re.compile( u"\U000000A7" # selection symbol @@ -249,10 +257,9 @@ def main(): global TAB_FOOTER, TAB_HEADER logging.debug("Got Tablist H/F Update: header={}".format(header_footer_packet.header)) logging.debug("Got Tablist H/F Update: footer={}".format(header_footer_packet.footer)) - # Strip out colour codes - - TAB_HEADER = strip_colour(json.loads(header_footer_packet.header)["text"]) - TAB_FOOTER = strip_colour(json.loads(header_footer_packet.footer)["text"]) + # Strip out colour codes and some markdown + TAB_HEADER = escape_markdown(strip_colour(json.loads(header_footer_packet.header)["text"])) + TAB_FOOTER = escape_markdown(strip_colour(json.loads(header_footer_packet.footer)["text"])) def handle_tab_list(tab_list_packet): logging.debug("Processing tab list packet") @@ -333,7 +340,7 @@ def main(): return logging.info("Username: {} Message: {}".format(username, original_message)) logging.debug("msg: {}".format(repr(original_message))) - message = remove_emoji(original_message.strip().replace("@", "@\N{zero width space}")) + message = escape_markdown(remove_emoji(original_message.strip().replace("@", "@\N{zero width space}"))) webhook_payload = { 'username': username, 'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid), From ab7e09e045388fb552341933a4a474b54efd4f80 Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Sat, 10 Nov 2018 02:19:14 -0500 Subject: [PATCH 4/9] Escape some markdown characters in messages sent from the discord side --- webhook-bridge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index a333235..a5d3373 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -595,7 +595,7 @@ def main(): message_to_send = remove_emoji( message.clean_content.encode('utf-8').decode('ascii', 'replace')).strip() - message_to_discord = message.clean_content + message_to_discord = escape_markdown(message.clean_content) logging.info(str(len(message_to_send)) + " " + repr(message_to_send)) From 8049701c430edc0988e63bc14e25101b5b0fd82e Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Sat, 10 Nov 2018 02:27:37 -0500 Subject: [PATCH 5/9] Only alter tab list content when formatting a message to send --- webhook-bridge.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index a5d3373..c93aee8 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -257,9 +257,8 @@ def main(): global TAB_FOOTER, TAB_HEADER logging.debug("Got Tablist H/F Update: header={}".format(header_footer_packet.header)) logging.debug("Got Tablist H/F Update: footer={}".format(header_footer_packet.footer)) - # Strip out colour codes and some markdown - TAB_HEADER = escape_markdown(strip_colour(json.loads(header_footer_packet.header)["text"])) - TAB_FOOTER = escape_markdown(strip_colour(json.loads(header_footer_packet.footer)["text"])) + TAB_HEADER = json.loads(header_footer_packet.header)["text"] + TAB_FOOTER = json.loads(header_footer_packet.footer)["text"] def handle_tab_list(tab_list_packet): logging.debug("Processing tab list packet") @@ -543,7 +542,10 @@ def main(): player_list = ", ".join(list(map(lambda x: x[1], PLAYER_LIST.items()))) msg = "{}\n" \ "Players online: {}\n" \ - "{}".format(TAB_HEADER, player_list, TAB_FOOTER) + "{}".format(escape_markdown( + strip_colour(TAB_HEADER)), escape_markdown( + strip_colour(player_list)), escape_markdown( + strip_colour(TAB_FOOTER))) await send_channel.send(msg) except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): From b8b54f007f495ff73343d12425a836e91620a6d3 Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Mon, 12 Nov 2018 01:37:15 -0500 Subject: [PATCH 6/9] Greatly improved bot reconnection handling * No more time-based hacks * End of tab-list back-fill is detected by getting the bot's own tab-list add packet * Users that have disconnected since the bot's disconnected now get their disconnection event logged to elasticsearch once the bot is able to reconnect to the server --- webhook-bridge.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index c93aee8..1e6c3b4 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -39,9 +39,10 @@ BOT_USERNAME = "" NEXT_MESSAGE_TIME = datetime.now(timezone.utc) PREVIOUS_MESSAGE = "" PLAYER_LIST = bidict() +PREVIOUS_PLAYER_LIST = bidict() +ACCEPT_JOIN_EVENTS = True TAB_HEADER = "" TAB_FOOTER = "" -LAST_CONNECTION_TIME = datetime.now(timezone.utc) def mc_uuid_to_username(uuid): @@ -174,7 +175,9 @@ def main(): def handle_disconnect(): logging.info('Disconnected.') - global PLAYER_LIST + global PLAYER_LIST, PREVIOUS_PLAYER_LIST, ACCEPT_JOIN_EVENTS + PREVIOUS_PLAYER_LIST = PLAYER_LIST.copy() + ACCEPT_JOIN_EVENTS = False PLAYER_LIST = bidict() connection.disconnect(immediate=True) time.sleep(15) @@ -261,6 +264,7 @@ def main(): TAB_FOOTER = json.loads(header_footer_packet.footer)["text"] def handle_tab_list(tab_list_packet): + global ACCEPT_JOIN_EVENTS logging.debug("Processing tab list packet") for action in tab_list_packet.actions: if isinstance(action, clientbound.play.PlayerListItemPacket.AddPlayerAction): @@ -273,7 +277,7 @@ def main(): if action.name not in UUID_CACHE.inv: UUID_CACHE.inv[action.name] = action.uuid # Initial tablist backfill - if LAST_CONNECTION_TIME + timedelta(seconds=2.5) < datetime.now(timezone.utc): + if ACCEPT_JOIN_EVENTS: webhook_payload = { 'username': username, 'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid), @@ -285,6 +289,15 @@ def main(): if config.es_enabled: es_connection(uuid=action.uuid, reason=ConnectionReason.CONNECTED, count=len(PLAYER_LIST)) return + else: + # The bot's name is sent last after the initial back-fill + if action.name == BOT_USERNAME: + ACCEPT_JOIN_EVENTS = True + if config.es_enabled: + diff = set(PREVIOUS_PLAYER_LIST.keys()) - set(PLAYER_LIST.keys()) + for uuid in diff: + es_connection(uuid=uuid, reason=ConnectionReason.DISCONNECTED, count=len(PLAYER_LIST)) + if config.es_enabled: es_connection(uuid=action.uuid, reason=ConnectionReason.SEEN) if isinstance(action, clientbound.play.PlayerListItemPacket.RemovePlayerAction): @@ -305,8 +318,7 @@ def main(): del PLAYER_LIST[action.uuid] def handle_join_game(join_game_packet): - global PLAYER_LIST, LAST_CONNECTION_TIME - LAST_CONNECTION_TIME = datetime.now(timezone.utc) + global PLAYER_LIST logging.info('Connected.') PLAYER_LIST = bidict() From 5b81b32687caa49d63e47838b545e3d655916dd1 Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Mon, 12 Nov 2018 02:17:52 -0500 Subject: [PATCH 7/9] Fixed a few bugs relating to elasticsearch player count * Log the bot's connection * Ignore duplicate bot connection packets * Simulate decrementing player count for missed disconnect events --- webhook-bridge.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index 1e6c3b4..d87fcc3 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -274,6 +274,9 @@ def main(): player_uuid = action.uuid if action.name not in PLAYER_LIST.inv: PLAYER_LIST.inv[action.name] = action.uuid + else: + # Sometimes we get a duplicate add packet on join idk why + return if action.name not in UUID_CACHE.inv: UUID_CACHE.inv[action.name] = action.uuid # Initial tablist backfill @@ -295,8 +298,12 @@ def main(): ACCEPT_JOIN_EVENTS = True if config.es_enabled: diff = set(PREVIOUS_PLAYER_LIST.keys()) - set(PLAYER_LIST.keys()) - for uuid in diff: - es_connection(uuid=uuid, reason=ConnectionReason.DISCONNECTED, count=len(PLAYER_LIST)) + for idx, uuid in enumerate(diff): + es_connection(uuid=uuid, reason=ConnectionReason.DISCONNECTED, + count=len(PREVIOUS_PLAYER_LIST) - (idx + 1)) + # Don't bother announcing the bot's own join message (who cares) but log it for analytics still + if config.es_enabled: + es_connection(uuid=action.uuid, reason=ConnectionReason.CONNECTED, count=len(PLAYER_LIST)) if config.es_enabled: es_connection(uuid=action.uuid, reason=ConnectionReason.SEEN) @@ -312,10 +319,10 @@ def main(): } for webhook in WEBHOOKS: post = requests.post(webhook,json=webhook_payload) - if config.es_enabled: - es_connection(uuid=action.uuid, reason=ConnectionReason.DISCONNECTED, count=len(PLAYER_LIST)) del UUID_CACHE[action.uuid] del PLAYER_LIST[action.uuid] + if config.es_enabled: + es_connection(uuid=action.uuid, reason=ConnectionReason.DISCONNECTED, count=len(PLAYER_LIST)) def handle_join_game(join_game_packet): global PLAYER_LIST From 099d970302b6a567bbc75279ff1a024dadf46fcc Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Mon, 12 Nov 2018 02:32:05 -0500 Subject: [PATCH 8/9] Don't accept join events until the bot's join even on 1st login --- webhook-bridge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook-bridge.py b/webhook-bridge.py index d87fcc3..d45b918 100755 --- a/webhook-bridge.py +++ b/webhook-bridge.py @@ -40,7 +40,7 @@ NEXT_MESSAGE_TIME = datetime.now(timezone.utc) PREVIOUS_MESSAGE = "" PLAYER_LIST = bidict() PREVIOUS_PLAYER_LIST = bidict() -ACCEPT_JOIN_EVENTS = True +ACCEPT_JOIN_EVENTS = False TAB_HEADER = "" TAB_FOOTER = "" From bb6e5bc8b87b7cb28fc2fc6cf4c2414d3a2df6ef Mon Sep 17 00:00:00 2001 From: Tristan Gosselin-Hane Date: Mon, 12 Nov 2018 16:40:03 -0500 Subject: [PATCH 9/9] Revert to pyCraft master and update deps --- Pipfile | 2 +- Pipfile.lock | 50 +++++++++++++++++++++++++------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Pipfile b/Pipfile index b311d68..b658995 100644 --- a/Pipfile +++ b/Pipfile @@ -14,7 +14,7 @@ mcstatus = "*" quarry = "*" discord-py = {git = "https://github.com/Rapptz/discord.py.git", ref = "rewrite"} bidict = "*" -minecraft = {git = "https://github.com/starcraft66/pyCraft.git", ref = "feature/add-player-list-header-and-footer-packet"} +minecraft = {git = "https://github.com/ammaraskar/pyCraft.git"} [requires] python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock index b22986a..2eb0fc2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "21ac16059e4c1c4b6aa022bacaa0d0fdc42c09ea4277129278fc848c52742b46" + "sha256": "92f71d787f478e45589c4939466682bc725279f844ced3c62a92a4b5e21515e1" }, "pipfile-spec": 6, "requires": { @@ -112,28 +112,28 @@ }, "cryptography": { "hashes": [ - "sha256:02602e1672b62e803e08617ec286041cc453e8d43f093a5f4162095506bc0beb", - "sha256:10b48e848e1edb93c1d3b797c83c72b4c387ab0eb4330aaa26da8049a6cbede0", - "sha256:17db09db9d7c5de130023657be42689d1a5f60502a14f6f745f6f65a6b8195c0", - "sha256:227da3a896df1106b1a69b1e319dce218fa04395e8cc78be7e31ca94c21254bc", - "sha256:2cbaa03ac677db6c821dac3f4cdfd1461a32d0615847eedbb0df54bb7802e1f7", - "sha256:31db8febfc768e4b4bd826750a70c79c99ea423f4697d1dab764eb9f9f849519", - "sha256:4a510d268e55e2e067715d728e4ca6cd26a8e9f1f3d174faf88e6f2cb6b6c395", - "sha256:6a88d9004310a198c474d8a822ee96a6dd6c01efe66facdf17cb692512ae5bc0", - "sha256:76936ec70a9b72eb8c58314c38c55a0336a2b36de0c7ee8fb874a4547cadbd39", - "sha256:7e3b4aecc4040928efa8a7cdaf074e868af32c58ffc9bb77e7bf2c1a16783286", - "sha256:8168bcb08403ef144ff1fb880d416f49e2728101d02aaadfe9645883222c0aa5", - "sha256:8229ceb79a1792823d87779959184a1bf95768e9248c93ae9f97c7a2f60376a1", - "sha256:8a19e9f2fe69f6a44a5c156968d9fc8df56d09798d0c6a34ccc373bb186cee86", - "sha256:8d10113ca826a4c29d5b85b2c4e045ffa8bad74fb525ee0eceb1d38d4c70dfd6", - "sha256:be495b8ec5a939a7605274b6e59fbc35e76f5ad814ae010eb679529671c9e119", - "sha256:dc2d3f3b1548f4d11786616cf0f4415e25b0fbecb8a1d2cd8c07568f13fdde38", - "sha256:e4aecdd9d5a3d06c337894c9a6e2961898d3f64fe54ca920a72234a3de0f9cb3", - "sha256:e79ab4485b99eacb2166f3212218dd858258f374855e1568f728462b0e6ee0d9", - "sha256:f995d3667301e1754c57b04e0bae6f0fa9d710697a9f8d6712e8cca02550910f" + "sha256:02915ee546b42ce513e8167140e9937fc4c81a06a82216e086ccce51f347948a", + "sha256:03cc8bc5a69ae3d44acf1a03facdb7c10a94c67907862c563e10efe72b737977", + "sha256:07f76bde6815c55195f3b3812d35769cc7c765144c0bb71ae45e02535d078591", + "sha256:13eac1c477b9af7e9a9024369468d08aead6ad78ed599d163ad046684474364b", + "sha256:179bfb585c5efc87ae0e665770e4896727b92dbc1f810c761b1ebf8363e2fec8", + "sha256:414af0ba308e74c1f8bc5b11befc86cb66b10be8959547786f64258830d2096f", + "sha256:41a1ca14f255df8c44dd22c6006441d631d1589104045ec7263cc47e9772f41a", + "sha256:54947eb98bc4eef99ddf49f45d2694ea5a3929ab3edc9806ad01967368594d82", + "sha256:5bac7a2abda07d0c3c8429210349bb54149ad8940dc7bcffedcd56519b410a3c", + "sha256:7f41af8c586bed9f59cfe8832d818b3b75c860d7025da9cd2db76875a72ff785", + "sha256:8004fae1b3cb2dbd90a011ad972e49a7e78a871b89c70cc7213cf4ebd2532bcb", + "sha256:8e0eccadc3b465e12c50a5b8fb4d39cf401b44d7bb9936c70fddb5e5aaf740d5", + "sha256:95b4741722269cfdc134fec23b7ae6503ee2aea83d0924cfee6d6ec54cd42d8e", + "sha256:a06f5aa6d7a94531dfe82eb2972e669258c452fe9cf88f76116610de4c789785", + "sha256:b0833d27c7eb536bc27323a1e8e22cb39ebac78c4ef3be0167ba40f447344808", + "sha256:b72dec675bc59a01edc96616cd48ec465b714481caa0938c8bbca5d18f17d5df", + "sha256:c800ddc23b5206ce025f23225fdde89cdc0e64016ad914d5be32d1f602ce9495", + "sha256:c980c8c313a5e014ae12e2245e89e7b30427e5a98cbb88afe478ecae85f3abaa", + "sha256:e85b410885addaeb31a867eabcefc9ef4a7e904ad45eac9e60a763a54b244626" ], "index": "pypi", - "version": "==2.3.1" + "version": "==2.4.1" }, "discord-py": { "git": "https://github.com/Rapptz/discord.py.git", @@ -188,8 +188,8 @@ "version": "==2.2" }, "minecraft": { - "git": "https://github.com/starcraft66/pyCraft.git", - "ref": "154b3e30d4f3690a53b821f22b8c281c7ac893d5" + "git": "https://github.com/ammaraskar/pyCraft.git", + "ref": "316ea4d63d6be5c59fe4b121db920b117130c6e9" }, "pyasn1": { "hashes": [ @@ -256,10 +256,10 @@ }, "sqlalchemy": { "hashes": [ - "sha256:84412de3794acee05630e7788f25e80e81f78eb4837e7b71d0499129f660486a" + "sha256:9de7c7dabcf06319becdb7e15099c44e5e34ba7062f9ba10bc00e562f5db3d04" ], "index": "pypi", - "version": "==1.2.13" + "version": "==1.2.14" }, "twisted": { "hashes": [