mirror of
https://codeberg.org/vlw/labylib-chattycape.git
synced 2025-09-13 16:13:41 +02:00
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.
191 lines
No EOL
4.6 KiB
Python
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() |