1
0
Fork 0

fixes for python 3 and general cleanup

This commit is contained in:
khr 2020-03-27 07:26:50 +01:00
parent 38b72bc919
commit 58e3d16a11
2 changed files with 116 additions and 68 deletions

3
.gitignore vendored
View File

@ -10,4 +10,5 @@ logs/
matrix-python-sdk-master/
old/
test/
world/
world/
*.swp

View File

@ -1,22 +1,26 @@
import argparse
import atexit
import base64
import glob
import io
import json
import logging
import os
import re
import select
import socket
import sys
import glob
import re
import subprocess
from subprocess import PIPE
import struct
import threading
import time
import urllib
from urllib.parse import urlparse
from matrix_client.api import MatrixHttpApi
import requests
from flask import Flask, jsonify, request
import atexit
import base64
import io, urllib
from urlparse import urlparse
#constants
@ -27,6 +31,9 @@ app = Flask(__name__)
minecraft = None
roomsync = {}
LOG = logging.getLogger(__name__)
class socket_util(object):
def __init__(self, host, port):
self.host = host
@ -35,11 +42,11 @@ class socket_util(object):
self.soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.msglist = []
self.addr = None
print ("Socket Init Complete")
LOG.info ("Socket Init Complete")
self.proc = None
self.exit = False
atexit.register(self.close_socket)
print "Starting Messaging Thread"
LOG.info("Starting Messaging Thread")
msg_process = threading.Thread(target=self.msg_process)
msg_process.daemon = True
msg_process.start()
@ -59,7 +66,7 @@ class socket_util(object):
def send(self, message):
#select returns (readable, writable, error) status on the objects passed in the corresponding lists
r, w, e = select.select([], [self.soc], [], 1)
#print "w" + str(w)
#LOG.info("w" + str(w))
if w == []:
return 1
string_message = json.dumps(message)
@ -81,7 +88,7 @@ class socket_util(object):
def receive(self):
r,s,e = select.select([self.soc], [], [], 1)
#print "r" + str(r)
#LOG.info("r" + str(r))
if r == []:
return ""
message_size = self.read_int()
@ -90,7 +97,7 @@ class socket_util(object):
return None
data = self.read(message_size)
if data == None:
print "data_none"
LOG.debug("data_none")
return None
message = json.loads(data)
@ -113,21 +120,22 @@ class socket_util(object):
return data
class MinecraftWrapper(socket_util):
def __init__(self, host, port):
super(MinecraftWrapper,self).__init__(host, port)
print "Starting Wrapper Polling Thread"
def __init__(self, command, host, port):
super().__init__(host, port)
self.command = command
LOG.info("Starting Wrapper Polling Thread")
poll_process = threading.Thread(target=self.cli_poll)
poll_process.daemon = True
poll_process.start()
self.socket_reset()
def socket_reset(self):
super(MinecraftWrapper,self).socket_reset()
super().socket_reset()
self.soc.connect((self.host, self.port))
print "Socket Connected"
LOG.info("Socket Connected")
def exe_mc(self):
self.proc = subprocess.Popen(sys.argv[1:], shell=True, stdout=PIPE, stdin=PIPE, universal_newlines=True)
self.proc = subprocess.Popen(self.command, shell=True, stdout=PIPE, stdin=PIPE, universal_newlines=True)
for stdout_line in iter(self.proc.stdout.readline, ""):
yield stdout_line
return_code = self.proc.wait()
@ -146,7 +154,7 @@ class MinecraftWrapper(socket_util):
self.msg_handle(rcv)
if status == 0: self.msglist.pop()
except Exception as e:
print e
LOG.info(e)
self.socket_reset()
@ -155,7 +163,7 @@ class MinecraftWrapper(socket_util):
if msg[0] == '/':
self.proc.stdin.write(msg + '\n')
else:
print(msg)
LOG.info(msg)
def proc_monitor(self):
try:
@ -164,47 +172,53 @@ class MinecraftWrapper(socket_util):
self.close_socket()
sys.exit(0)
except:
print "poll error"
LOG.error("poll error")
pass
def cli_poll(self):
prog = re.compile("^\[(.*)\] \[(.*)\]: <(.*)> (.*)")
for line in self.exe_mc():
print(line.rstrip('\n'))
LOG.info(line.rstrip('\n'))
# regex to get user and text: ^<(.*)> (.*)\n
result = prog.search(line)
if result:
#print("user: " + result.group(3) + " msg: " +result.group(4).rstrip('\n'))
#LOG.info("user: " + result.group(3) + " msg: " +result.group(4).rstrip('\n'))
#msb.send({"user":result.group(3),"msg":result.group(4).rstrip('\n')})
self.msglist.insert(0, {"user":result.group(3),"msg":result.group(4).rstrip('\n')})
class MinecraftServerBridge(socket_util):
def __init__(self, host, port):
def __init__(
self,
minecraft_bind_host: int,
minecraft_bind_port: int,
matrix_bind_port: int,
appservice_token: str
):
#starting threads
print ("Starting Appservice Webserver")
flask_thread = threading.Thread(target=app.run,kwargs={"port":global_config['bridge_matrixapi_port']})
LOG.info ("Starting Appservice Webserver")
flask_thread = threading.Thread(target=app.run,kwargs={ "port": minecraft_bind_port })
flask_thread.daemon = True
flask_thread.start()
#socket and other init
super(MinecraftServerBridge,self).__init__(host, port)
print ("Calling Matrix Api")
self.api = MatrixHttpApi("http://localhost:8008", token=global_config['as_token'])
super().__init__(minecraft_bind_host, minecraft_bind_port)
LOG.info ("Calling Matrix Api")
self.api = MatrixHttpApi("http://localhost:8008", token=appservice_token)
self.user_re = re.compile("(?<=\@).*(?=\:)")
self.avatar_update_log = {}
print ("Finished Init")
LOG.info ("Finished Init")
def socket_reset(self):
super(MinecraftServerBridge,self).socket_reset()
print "Server Binding to " + self.host + " " + str(self.port)
super().socket_reset()
LOG.info("Server Binding to " + self.host + " " + str(self.port))
self.soc.bind((self.host, self.port))
print "Server Bound"
LOG.info("Server Bound")
self.soc.listen(1)
print "Server listen to host"
LOG.info("Server listen to host")
self.soc, self.addr = self.soc.accept()
self.soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print "Server accepted connection: " + str(self.addr)
LOG.info("Server accepted connection: " + str(self.addr))
def msg_process(self):
@ -217,7 +231,7 @@ class MinecraftServerBridge(socket_util):
if rcv != "" and rcv != None:
self.msg_handle(rcv)
except Exception as e:
print e
LOG.info(e)
self.socket_reset()
@ -225,41 +239,41 @@ class MinecraftServerBridge(socket_util):
#for msg, create user and post as user
#add minecraft user to minecraft channel, if this fails, no big deal
try:
print "trying to create id..."
LOG.info("trying to create id...")
new_user = "@mc_" + msg['user']
user_id = new_user + ":" + global_config['server_name']
self.api.register("m.login.application_service",username = "mc_" + msg['user'])
except Exception as e:
print e
LOG.info(e)
#for each room we're aware of, post server chat inside. Eventually 1 room should equal 1 server
for room in roomsync:
#generate a unique transaction id based on the current time
txn_id = str(int(time.time() * 1000))
#attempt to join room
print "trying to join room as user and as bridge manager"
LOG.info("trying to join room as user and as bridge manager")
self.api._send("POST", '/rooms/'+room+'/join', query_params={"user_id": user_id}, headers={"Content-Type":"application/json"})
self.api._send("POST", '/rooms/'+room+'/join', headers={"Content-Type":"application/json"})
#set our display name to something nice
print "trying to set display name..."
LOG.info("trying to set display name...")
self.api._send("PUT", '/profile/'+user_id+'/displayname/', content={"displayname":msg["user"]}, query_params={"user_id": user_id}, headers={"Content-Type":"application/json"})
#get our mc skin!!
#backup: #avatar_url = "https://www.minecraftskinstealer.com/face.php?u="+msg['user']
#only get this if the user hasn't updated in a long time
print "Checking if we need to update avatar..."
LOG.info("Checking if we need to update avatar...")
if msg['user'] not in self.avatar_update_log.keys() or abs(self.avatar_update_log[msg['user']] - time.time()) > 180:
self.avatar_update_log[msg['user']] = time.time()
avatar_url = self.get_mc_skin(msg['user'], user_id)
if avatar_url:
print "avatar_url is " + avatar_url
LOG.info("avatar_url is " + avatar_url)
self.api._send("PUT", '/profile/'+user_id+'/avatar_url/', content={"avatar_url":avatar_url}, query_params={"user_id": user_id}, headers={"Content-Type":"application/json"})
#attempt to post in room
print "Attempting to post in Room"
LOG.info("Attempting to post in Room")
self.api._send("PUT", '/rooms/'+room+'/send/m.room.message/' + txn_id, content={"msgtype":"m.text","body":msg["msg"]}, query_params={"user_id": user_id}, headers={"Content-Type":"application/json"})
def get_mc_skin(self, user, user_id):
print("Getting Minecraft Avatar")
LOG.info("Getting Minecraft Avatar")
from PIL import Image
mojang_info = requests.get('https://api.mojang.com/users/profiles/minecraft/'+user).json() #get uuid
mojang_info = requests.get('https://sessionserver.mojang.com/session/minecraft/profile/'+mojang_info['id']).json() #get more info from uuid
@ -275,23 +289,23 @@ class MinecraftServerBridge(socket_util):
#compare to user's current id so we're not uploading the same pic twice
#GET /_matrix/client/r0/profile/{userId}/avatar_url
print "Getting Current Avatar URL"
LOG.info("Getting Current Avatar URL")
curr_url = self.api._send("GET", '/profile/'+user_id+'/avatar_url/', query_params={"user_id": user_id}, headers={"Content-Type":"application/json"})
upload = True
if 'avatar_url' in curr_url.keys():
print "Checking Avatar..."
LOG.info("Checking Avatar...")
file = io.BytesIO(urllib.urlopen(self.api.get_download_url(curr_url['avatar_url'])).read())
im = Image.open(file)
image_buffer_curr = io.BytesIO()
im.save(image_buffer_curr, "PNG")
if (image_buffer_head.getvalue()) == (image_buffer_curr.getvalue()):
print "Image Same"
LOG.debug("Image Same")
upload = False
if upload:
#upload img
#POST /_matrix/media/r0/upload
print "Returning updated avatar"
print image_buffer_head
LOG.debug("Returning updated avatar")
LOG.debug(image_buffer_head)
return self.api.media_upload(image_buffer_head.getvalue(), "image/png")["content_uri"]
else:
return None
@ -299,12 +313,12 @@ class MinecraftServerBridge(socket_util):
@app.route("/transactions/<transaction>", methods=["PUT"])
def on_receive_events(transaction):
print("got event")
LOG.info("got event")
events = request.get_json()["events"]
for event in events:
print "User: %s Room: %s" % (event["user_id"], event["room_id"])
print "Event Type: %s" % event["type"]
print "Content: %s" % event["content"]
LOG.info("User: %s Room: %s" % (event["user_id"], event["room_id"]))
LOG.info("Event Type: %s" % event["type"])
LOG.info("Content: %s" % event["content"])
roomsync[event["room_id"]] = ""
if event['type'] == 'm.room.message' and \
event['content']['msgtype'] == 'm.text' and \
@ -318,7 +332,7 @@ def on_receive_events(transaction):
@app.route("/rooms/<room>", methods=["GET"])
def on_room(room):
print "returning: " + str(room)
LOG.info("returning: " + str(room))
return jsonify({})
@ -331,7 +345,7 @@ def make_config(configfile, server=True):
json.dump(bridge_cfg_skeleton, outfile)
else:
json.dump(wrapper_cfg_skeleton, outfile)
print "Please edit {0} and then run again!".format(configfile)
LOG.error("Please edit {0} and then run again!".format(configfile))
sys.exit(0)
elif glob.glob(configfile):
@ -339,26 +353,59 @@ def make_config(configfile, server=True):
read_config = json.load(config)
return read_config
if __name__=="__main__":
if glob.glob("minecraft_server.*"):
print "Running Minecraft Server Wrapper Mode"
def main():
logging.basicConfig()
parser = argparse.ArgumentParser()
mode_group = parser.add_mutually_exclusive_group(required=True)
mode_group.add_argument(
"--minecraft_wrapper",
dest="mode",
action="store_const",
const="wrapper",
help="Run in Minecraft server wrapper mode",
)
mode_group.add_argument(
"--matrix_bridge",
dest="mode",
action="store_const",
const="bridge",
help="Run in Matrix Appservice mode",
)
parser.add_argument("command", nargs=argparse.REMAINDER)
args = parser.parse_args()
if args.mode == "wrapper":
LOG.info("Running Minecraft Server Wrapper Mode")
global_config = make_config("wrapper.json", server=False)
ip_addr_info = socket.gethostbyname_ex(global_config['server_name'])
minecraft = MinecraftWrapper(ip_addr_info[2][0], global_config['wrapper_mcdata_port'])
minecraft = MinecraftWrapper(
args.command,
host=ip_addr_info[2][0],
port=global_config['wrapper_mcdata_port'],
)
else:
print "Running Minecraft Matrix Bridge Mode"
LOG.info("Running Minecraft Matrix Bridge Mode")
global_config = make_config("server.json", server=True)
minecraft = MinecraftServerBridge("localhost", global_config['bridge_mcdata_port'])
print "All Threads Running"
minecraft = MinecraftServerBridge(
minecraft_bind_host="0.0.0.0",
minecraft_bind_port=global_config['bridge_mcdata_port'],
matrix_bind_port=global_config["bridge_matrixapi_port"],
appservice_token=global_config["as_token"],
)
LOG.info("All Threads Running")
cmd = ""
while(not minecraft.exit):
# Allow stdin commands to the minecraft server
while (not minecraft.exit):
if minecraft.proc != None and 'stop' not in cmd:
cmd = raw_input()
cmd = input()
minecraft.proc.stdin.write(cmd + '\n')
else:
time.sleep(1)
print "Calling exit() in main thread..."
LOG.info("Calling exit() in main thread...")
sys.exit()
if __name__ == "__main__":
main()