# Look for user commands. match = re.search(r'<(.+?)> (!\w+)', message) if match: username = match.group(1) command = match.group(2) print("Detected command use: <{}> {}".format(username, command)) # Whitelisted user? if username.lower() in whitelist: # Process the commands. if command == "!creative": client.sendline("gamemode 1 {}".format(username)) elif command == "!survival": client.sendline("gamemode 0 {}".format(username)) else: client.sendline( "say Unsupported command. Valid ones are: !creative !survival" ) else: client.sendline("say Command ignored; not whitelisted.") # Attach the handlers. client.add_handler("auth_ok", on_authed) client.add_handler("auth_error", on_error) client.add_handler("server_message", on_message) # Connect! client.connect() client.start()
class Application: def __init__(self, args): """Initialize the application.""" self.args = args # Verify settings. self.verify_args() self.config = dict() # minecraft-control configuration self.client = None # MinecraftClient instance self.today = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S") self.world = self.get_world_name() log.info("Today's date/time: {}".format(self.today)) # Load the minecraft-control configuration. self.load_mc_config() def verify_args(self): """Do some sanity checking on input arguments.""" # The minecraft-control config file. if not os.path.isfile(self.args.config): log.error("{}: not a file".format(self.args.config)) sys.exit(1) # The server directory. if not os.path.isfile("{}/server.properties".format(self.args.server)): log.error( "{}: not a Minecraft server directory (no " "server.properties file present)".format(self.args.server) ) sys.exit(1) def load_mc_config(self): """Load the configuration from minecraft-control.""" log.debug("Loading minecraft-control settings from {}".format(args.config)) parser = RawConfigParser() with open(self.args.config, "r") as fh: parser.readfp(fh) self.config["host"] = parser.get("tcp-server", "address") self.config["port"] = parser.get("tcp-server", "port") self.config["password"] = parser.get("auth", "password") self.config["method"] = parser.get("auth", "method") def get_world_name(self): """Load the server.properties to find the world name.""" with open("{}/server.properties".format(self.args.server), "r") as fh: for line in fh: if not "=" in line: continue parts = line.split("=", 1) key = parts[0].strip() value = parts[1].strip() if key == "level-name": return value raise ValueError("No level-name found in server.properties!") def run(self): """Run the main program's logic.""" # Change into the server's directory. os.chdir(self.args.server) # If we're only culling backups, do that and exit. if self.args.clean: return self.cull_backups() # Backups directory. backups = os.path.join(self.args.server, "backups") if not os.path.isdir(backups): log.info("Creating backups directory: {}".format(backups)) os.mkdir(backups) # Connect to the Minecraft-Control server. log.info("Connecting to Minecraft control server...") self.client = MinecraftClient( host=self.config["host"], port=self.config["port"], password=self.config["password"], methods=[self.config["method"]], ) self.client.add_handler("auth_ok", self.on_auth_ok) self.client.add_handler("auth_error", self.on_auth_error) self.client.add_handler("server_message", self.on_message) self.client.connect() self.client.start() def on_auth_ok(self, mc): """Handle successful authentication.""" log.info("Connection to server established and authenticated!") # Target file name. fname = self.today + ".tar.gz" target = os.path.join("backups", fname) # Turn off saving and save the world now. log.info("Turning off auto-saving and saving the world now!") self.client.sendline("save-off") self.client.sendline("save-all") time.sleep(5) # Archive the world. log.info("Backing up the world as: {}".format(target)) subprocess.call(["tar", "czvf", target, self.world]) # Turn saving back on. time.sleep(5) log.info("Turning auto-saving back on!") self.client.sendline("save-on") # Cull old backups. self.cull_backups() quit() def cull_backups(self): """Trim the backup copies and remove older backups.""" log.info("Culling backups...") # Date cut-off for daily backups. daily_cutoff = (datetime.datetime.utcnow() - datetime.timedelta(days=DAILY_BACKUPS)).strftime("%Y-%m-%d") log.debug("Daily cutoff date: {}".format(daily_cutoff)) # Number of weekly backups spared. weekly_spared = 0 # Check all the existing backups. for tar in sorted(os.listdir("backups"), reverse=True): if not tar.endswith(".tar.gz"): continue try: dt = datetime.datetime.strptime(tar, "%Y-%m-%d_%H-%M-%S.tar.gz") weekday = dt.weekday() date = dt.strftime("%Y-%m-%d") except Exception as e: log.error("Error parsing datetime from existing backup {}: {}".format(tar, e)) continue # If this is within the daily cutoff, we keep it. if date > daily_cutoff: log.debug("DAILY KEEP: {} is within the daily cutoff".format(tar)) continue # Now we're at backups older than our daily threshhold, so we only # want to keep one backup per week until we have WEEKLY_BACKUPS # copies, and only for backups taken on WEEKLY_WEEKDAY day-of-week. if weekday == WEEKLY_WEEKDAY and weekly_spared < WEEKLY_BACKUPS: log.debug("WEEKLY KEEP: {} is being kept".format(tar)) weekly_spared += 1 continue # All other old backups get deleted. log.info("Cull old backup: {}".format(tar)) os.unlink("backups/{}".format(tar)) def on_auth_error(self, mc, error): """Handle unsuccessful authentication.""" log.error(error) sys.exit(1) def on_message(self, mc, message): """Handle a Minecraft server message.""" log.info("Minecraft server says: {}".format(message))
class Application: def __init__(self, args): """Initialize the application.""" self.args = args # Verify settings. self.verify_args() self.config = dict() # minecraft-control configuration self.client = None # MinecraftClient instance self.today = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S") self.world = self.get_world_name() log.info("Today's date/time: {}".format(self.today)) # Load the minecraft-control configuration. self.load_mc_config() def verify_args(self): """Do some sanity checking on input arguments.""" # The minecraft-control config file. if not os.path.isfile(self.args.config): log.error("{}: not a file".format(self.args.config)) sys.exit(1) # The server directory. if not os.path.isfile("{}/server.properties".format(self.args.server)): log.error("{}: not a Minecraft server directory (no " "server.properties file present)".format( self.args.server)) sys.exit(1) def load_mc_config(self): """Load the configuration from minecraft-control.""" log.debug("Loading minecraft-control settings from {}".format( args.config)) parser = RawConfigParser() with open(self.args.config, "r") as fh: parser.readfp(fh) self.config["host"] = parser.get("tcp-server", "address") self.config["port"] = parser.get("tcp-server", "port") self.config["password"] = parser.get("auth", "password") self.config["method"] = parser.get("auth", "method") def get_world_name(self): """Load the server.properties to find the world name.""" with open("{}/server.properties".format(self.args.server), "r") as fh: for line in fh: if not "=" in line: continue parts = line.split("=", 1) key = parts[0].strip() value = parts[1].strip() if key == "level-name": return value raise ValueError("No level-name found in server.properties!") def run(self): """Run the main program's logic.""" # Change into the server's directory. os.chdir(self.args.server) # If we're only culling backups, do that and exit. if self.args.clean: return self.cull_backups() # Backups directory. backups = os.path.join(self.args.server, "backups") if not os.path.isdir(backups): log.info("Creating backups directory: {}".format(backups)) os.mkdir(backups) # Connect to the Minecraft-Control server. log.info("Connecting to Minecraft control server...") self.client = MinecraftClient( host=self.config["host"], port=self.config["port"], password=self.config["password"], methods=[self.config["method"]], ) self.client.add_handler("auth_ok", self.on_auth_ok) self.client.add_handler("auth_error", self.on_auth_error) self.client.add_handler("server_message", self.on_message) self.client.connect() self.client.start() def on_auth_ok(self, mc): """Handle successful authentication.""" log.info("Connection to server established and authenticated!") # Target file name. fname = self.today + ".tar.gz" target = os.path.join("backups", fname) # Turn off saving and save the world now. log.info("Turning off auto-saving and saving the world now!") self.client.sendline("save-off") self.client.sendline("save-all") time.sleep(5) # Archive the world. log.info("Backing up the world as: {}".format(target)) subprocess.call(["tar", "czvf", target, self.world]) # Turn saving back on. time.sleep(5) log.info("Turning auto-saving back on!") self.client.sendline("save-on") # Cull old backups. self.cull_backups() quit() def cull_backups(self): """Trim the backup copies and remove older backups.""" log.info("Culling backups...") # Date cut-off for daily backups. daily_cutoff = ( datetime.datetime.utcnow() - datetime.timedelta(days=DAILY_BACKUPS)).strftime("%Y-%m-%d") log.debug("Daily cutoff date: {}".format(daily_cutoff)) # Number of weekly backups spared. weekly_spared = 0 # Check all the existing backups. for tar in sorted(os.listdir("backups"), reverse=True): if not tar.endswith(".tar.gz"): continue try: dt = datetime.datetime.strptime(tar, "%Y-%m-%d_%H-%M-%S.tar.gz") weekday = dt.weekday() date = dt.strftime("%Y-%m-%d") except Exception as e: log.error("Error parsing datetime from existing backup {}: {}". format( tar, e, )) continue # If this is within the daily cutoff, we keep it. if date > daily_cutoff: log.debug( "DAILY KEEP: {} is within the daily cutoff".format(tar)) continue # Now we're at backups older than our daily threshhold, so we only # want to keep one backup per week until we have WEEKLY_BACKUPS # copies, and only for backups taken on WEEKLY_WEEKDAY day-of-week. if weekday == WEEKLY_WEEKDAY and weekly_spared < WEEKLY_BACKUPS: log.debug("WEEKLY KEEP: {} is being kept".format(tar)) weekly_spared += 1 continue # All other old backups get deleted. log.info("Cull old backup: {}".format(tar)) os.unlink("backups/{}".format(tar)) def on_auth_error(self, mc, error): """Handle unsuccessful authentication.""" log.error(error) sys.exit(1) def on_message(self, mc, message): """Handle a Minecraft server message.""" log.info("Minecraft server says: {}".format(message))
if match: username = match.group(1) client.sendline("say Welcome, {}!".format(username)) # Look for user commands. match = re.search(r'<(.+?)> (!\w+)', message) if match: username = match.group(1) command = match.group(2) print("Detected command use: <{}> {}".format(username, command)) # Whitelisted user? if username.lower() in whitelist: # Process the commands. if command == "!creative": client.sendline("gamemode 1 {}".format(username)) elif command == "!survival": client.sendline("gamemode 0 {}".format(username)) else: client.sendline("say Unsupported command. Valid ones are: !creative !survival") else: client.sendline("say Command ignored; not whitelisted.") # Attach the handlers. client.add_handler("auth_ok", on_authed) client.add_handler("auth_error", on_error) client.add_handler("server_message", on_message) # Connect! client.connect() client.start()