labylib-chattycape/start.py
Victor Westerlund c2fa933b3a 1.0.0
Release 1.0.0

Added default texture render backend inside 'backend/'
Removed some debugging states from 'start.py'

Tags can now use the full cape size for more flexibility. Updated tag textures for 'us.mineplex.com' to support this.
2020-11-18 14:06:08 +01:00

191 lines
No EOL
4.6 KiB
Python

from labylib import Cape
from threading import Thread, Event
from pathlib import Path
import re
import json
import platform
import getpass
import subprocess
import requests
# Chattycape daemon
class Chattycape(Thread):
pollRate = 1 # Logfile pollrate
updateRate = 15 # Update rate of Labylib cosmetic
def __init__(self,event,phpsessid,endpoint,logfile,me = "None"):
Thread.__init__(self)
self.stopped = event
self.params = (phpsessid,endpoint,logfile,me) # Create tuple from params
self.server = None # Connected to server (hostname)
self.config = None # Chat config for current server
self.line = ""
self.i = 0
# Read last line from logfile
def linefeed(self):
# tail last line from logfile
line = subprocess.check_output(['tail','-1',self.params[2]]).decode("utf-8","ignore")[:-2]
match = re.search(self.config["pattern"],line)
if match:
self.line = line
return
# Extract Minecraft username and tag from current line
def getUser(self):
line = self.line[31:].split() # Split current line. Offset (31) for log-prefixes
username = tag = "None"
start = self.config["lookup"]["start"] # List offset for iterator
ignoreList = self.config["lookup"]["ignore"] # Jumps to next index if match
tagList = self.config["lookup"]["tag"] # 'tag' set if match
for substr in line[start:]: # Start at offset
if substr in ignoreList:
continue
if substr in tagList:
tag = substr.lower()
continue
# Not ignored, and not a tag, so assume we've reached the username
username = substr
break
return (username,tag)
# Update labylib cosmetic
def update(self):
args = self.getUser() # Get minecraft username and tag
urlparams = f"?server={self.server}&username={args[0]}&tag={args[1]}"
# Save cape texture from endpoint to disk
with open(".cache.png","wb") as handle:
fetch = requests.get(self.params[1] + urlparams,stream=True)
if not fetch.ok:
print(fetch)
for block in fetch.iter_content(1024):
if not block:
break
handle.write(block)
cape = Cape.Texture(self.params[0],".cache.png")
cape.update()
# ----------------------------
# Import RegEx patterns for current server
def loadConfig(self):
self.server = "us.mineplex.com"
with open("./servers.json") as config:
data = json.load(config)
self.config = data[self.server]
self.config["pattern"] = r"\[CHAT\]+ " + self.config["pattern"]
# Start the thread
def run(self):
print("\nRunning..")
self.loadConfig()
# Start the chat 'listener'
while not self.stopped.wait(Chattycape.pollRate):
self.linefeed() # Poll logfile
# Cooldown for labylib texture update
if(self.i >= (Chattycape.updateRate * Chattycape.pollRate)):
self.update()
self.i = 0
self.i += 1
# Initializer and watchdog
class Main:
def __init__(self):
print("-- Labylib Chattycape --")
self.logfile = self.locate()
self.endpoint = self.prompt("Cape render endpoint","https://api.victorwesterlund.com/chattycape")
self.me = self.prompt("My Minecraft in-game name (Case Sensitive)","Don't exclude me")
self.phpsessid = self.prompt("PHPSESSID cookie")
self.start()
# Prompt user to enter information
def prompt(self,message,default = "None"):
# Add '[default]' flag if present
if(default != "None"):
message += f" [{default}]"
message += ":"
value = default
userinput = input(message + "\n")
if(userinput):
value = userinput
return value
# Attempt to locate '.minecraft' automatically, otherwise prompt user
def locate(self):
sys = platform.system() # Get operating system
user = getpass.getuser() # Get current user
mclog = "/logs/latest.log"
# Default locations for various systems
paths = {
"Linux": [
"~/.minecraft",
f"/mnt/c/Users/{user}/AppData/Roaming/.minecraft", # Windows Subsystem for Linux (WSL)
f"/home/{user}/.minecraft"
],
"Windows": [
"%APPDATA%\.minecraft",
f"C://Users/{user}/AppData/Roaming/.minecraft",
],
"Darwin": [
"~/Library/Application Support/minecraft" # macOS
]
}
for path in paths[sys]:
if(Path(path).exists()):
return path + mclog
# Failed to locate Minecraft-installation automatically
path = self.prompt("Path to your '.minecraft'-folder")
if(Path(path).exists()):
return path + mclog
print(f"\nInvalid path; No Minecraft-installation found at '{path}'")
self.locate()
def start(self):
stop = Event()
# Start the daemon
chattycape = Chattycape(stop,self.phpsessid,self.endpoint,self.logfile,self.me)
chattycape.start()
input("Press enter to stop the daemon")
stop.set() # Stop the daemon
print("Stopped!")
Main()