Rewritten and finished framework and bot

This commit is contained in:
Malasaur 2025-08-28 20:39:20 +02:00
parent f045967591
commit 49a9964ec5
7 changed files with 150 additions and 33 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
__pycache__
modules
venv
program.py

12
data.py
View file

@ -1,12 +0,0 @@
from subprocess import PIPE, STDOUT, Popen
from typing import Callable, List, Tuple
from re import Pattern
output_binds: List[Tuple[Pattern, Callable]] = []
proc = None
def initializeProc(cmd):
global proc
proc = Popen(cmd.split(), stdin=PIPE, stdout=PIPE,
stderr=STDOUT, text=True, bufsize=1)

31
libcommon.py Normal file
View file

@ -0,0 +1,31 @@
from subprocess import PIPE, STDOUT, Popen, run
from typing import Callable, List, Optional, Tuple
from re import Pattern
minecraftOutputBinds: List[Tuple[Pattern, Callable]] = []
minecraftProc = None
def minecraftInit(cmd: str) -> None:
global minecraftProc
minecraftProc = Popen(cmd.split(), stdin=PIPE, stdout=PIPE,
stderr=STDOUT, text=True, bufsize=1)
class Group:
def __init__(self, name: str, id: str):
self.name = name
self.id = id
def __repr__(self) -> str:
return f'<Group "{self.name}" at "{self.id}">'
signalMessageBinds: List[Tuple[Optional[Group], Callable]] = []
signalProc = None
def signalInit(cmd: str) -> None:
global signalProc
signalProc = run(cmd.split(), stdout=PIPE)

View file

@ -1,19 +1,16 @@
from subprocess import PIPE, STDOUT, Popen
from importlib import import_module
from threading import Thread
from typing import Callable
from pathlib import Path
from re import compile
import data
import libcommon
def reader() -> None:
if data.proc.stdout:
for line in data.proc.stdout:
if libcommon.minecraftProc.stdout:
for line in libcommon.minecraftProc.stdout:
line = line.rstrip("\n")
print(line)
for pattern, func in data.output_binds:
for pattern, func in libcommon.minecraftOutputBinds:
match = pattern.match(line)
if match:
func(*match.groups())
@ -22,30 +19,30 @@ def reader() -> None:
def onConsoleOutput(pattern: str) -> Callable:
def decorator(func: Callable) -> Callable:
data.output_binds.append((compile(pattern), func))
libcommon.minecraftOutputBinds.append((compile(pattern), func))
return func
return decorator
def sendCommand(cmd: str) -> None:
if data.proc.stdin:
data.proc.stdin.write(cmd+"\n")
data.proc.stdin.flush()
if libcommon.minecraftProc.stdin:
libcommon.minecraftProc.stdin.write(cmd+"\n")
libcommon.minecraftProc.stdin.flush()
if __name__ == "__main__":
data.initializeProc("python -u program.py")
for file in Path("modules").iterdir():
if file.name.endswith(".py"):
import_module("modules."+file.name[:-3])
def main() -> None:
libcommon.minecraftInit("python -u program.py")
t = Thread(target=reader, daemon=True)
t.start()
try:
while data.proc.poll() is None:
while libcommon.minecraftProc.poll() is None:
sendCommand(input())
except KeyboardInterrupt:
sendCommand("stop")
data.proc.wait()
libcommon.minecraftProc.wait()
if __name__ == "__main__":
main()

63
libsignal.py Normal file
View file

@ -0,0 +1,63 @@
from typing import Callable, List, Optional
from requests import post as _post
from sseclient import SSEClient
from threading import Thread
from time import sleep
from json import loads
from uuid import uuid4
import libcommon
url = "http://localhost:42069"
def post(method: str, **params):
return _post(url+"/api/v1/rpc", json={"jsonrpc": "2.0", "method": method, "params": params, "id": str(uuid4())}).json()
def sendMessage(message: str, recipient: str | libcommon.Group):
if isinstance(recipient, libcommon.Group):
return post("send", message=message, groupId=recipient.id)
return post("send", message=message, recipient=recipient)
def listGroups() -> List[libcommon.Group]:
grps = post("listGroups")["result"]
groups = []
for group in grps:
groups.append(libcommon.Group(group["name"], group["id"]))
return groups
def onMessage(group: Optional[libcommon.Group] = None) -> Callable:
def decorator(func: Callable) -> Callable:
libcommon.signalMessageBinds.append((group, func))
return func
return decorator
def main():
cli = Thread(target=libcommon.signalInit, args=(
"signal-cli daemon --http localhost:42069",))
cli.start()
sleep(5)
for msg in SSEClient(url+"/api/v1/events"):
data = loads(msg.data)
envelope = data.get("envelope", None)
if envelope:
dataMessage = envelope.get("dataMessage", None)
if dataMessage:
name = envelope["sourceName"]
body = dataMessage["message"]
groupId = dataMessage["groupInfo"]["groupId"]
if body is not None:
for group, func in libcommon.signalMessageBinds:
if group is None or group.id == groupId:
func(name, body)
break
if __name__ == "__main__":
main()

21
main.py Normal file
View file

@ -0,0 +1,21 @@
from importlib import import_module
from threading import Thread
from subprocess import run
from pathlib import Path
import libminecraft
import libsignal
minecraftThread = Thread(target=libminecraft.main)
signalThread = Thread(target=libsignal.main)
minecraftThread.start()
signalThread.start()
for file in Path("modules").iterdir():
if file.name.endswith(".py"):
import_module("modules."+file.name[:-3])
minecraftThread.join()
signalThread.join()

View file

@ -0,0 +1,17 @@
import libminecraft
import libsignal
import libcommon
RSBeta = libcommon.Group(
"RS Beta", "25a58sEeccxAs7Ka27HZW5Sm3R8zJQcyndLKR6FO7sM=")
@libminecraft.onConsoleOutput(r"\[.*\] \[Server thread\/INFO\]: <(.*)> (.*)")
def minecraftToSignal(usr: str, msg: str):
libsignal.sendMessage(f"[{usr}] {msg}", RSBeta)
@libsignal.onMessage(RSBeta)
def signalToMinecraft(usr: str, msg: str):
print(usr, msg)
libminecraft.sendCommand(f"say [{usr}] {msg}")