Refactored

This commit is contained in:
Malasaur 2025-12-16 01:15:17 +01:00
parent e7248723f5
commit 715d0527bf
No known key found for this signature in database
6 changed files with 327 additions and 106 deletions

8
libbot.py Normal file
View file

@ -0,0 +1,8 @@
from config import Config
from libminecraft import Minecraft
from libsignal import Signal
signal = Signal(Config.SIGNAL_HOST, Config.SIGNAL_PORT)
minecraft = Minecraft(
Config.MINECRAFT_HOST, Config.MINECRAFT_PORT, Config.MINECRAFT_PASSWORD
)

View file

@ -82,9 +82,9 @@ class Minecraft:
return
groups = match.groups()
if groups:
func(*groups)
await func(*groups)
else:
func(line)
await func(line)
self._hooks.append(callback)

View file

@ -1,11 +1,11 @@
import asyncio
from json import loads
from typing import Callable
from pathlib import Path
from typing import Callable, Literal
from uuid import uuid4
from aiohttp import ClientSession
from config import Config
from sseclient import SSEClient
class User:
@ -41,6 +41,10 @@ class User:
self.profile_last_name = profile_last_name
self.username = username
@classmethod
def self(cls):
return cls("", number="+393406838100")
def __repr__(self) -> str:
return f'<User "{self.name}" at "{self.number}">'
@ -66,49 +70,93 @@ class Group:
return f'<Group "{self.name}" at "{self.id}">'
class ReceivedMessage:
def __init__(self, message: str, user: User, group: Group | None):
"""Creates a new ReceivedMessage.
Args:
message: The Message's body.
user: The User that sent the Message.
group: The Group where the message was sent on, or None for a direct Message.
"""
class Sticker:
def __init__(self, pack_id: str, id: int):
self.pack_id = pack_id
self.id = id
self.message = message
self.user = user
self.group = group
def __repr__(self) -> str:
group = (
' on "' + (self.group.name or self.group.id) + '"'
if self.group and self.group
else ""
class MessageMention:
def __init__(self, start: int, length: int, recipient: User):
self.start = start
self.length = length
self.recipient = recipient
def get(self) -> str:
return (
f"{self.start}:{self.length}:{self.recipient.number or self.recipient.id}"
)
return f'<Message "{self.message}" by "{self.user.name}"{group}>'
class MessageMention: ...
class MessageStyle:
def __init__(
self,
start: int,
length: int,
style: Literal["BOLD", "ITALIC", "SPOILER", "STRIKETHROUGH", "MONOSPACE"],
):
self.start = start
self.length = length
self.style = style
class MessageStyles: ...
def get(self) -> str:
return f"{self.start}:{self.length}:{self.style}"
class Message:
def __init__(
self,
recipient: User | Group,
sticker: str | None = None,
text: str | None = None,
user: User | None = None,
group: Group | None = None,
timestamp: int | None = None,
attachments: list[Path] = [],
view_once: bool = False, # TODO: fix
sticker: Sticker | None = None,
mentions: list[MessageMention] = [],
styles: list[MessageStyles] = [],
view_once: bool = False,
): ...
styles: list[MessageStyle] = [],
quote: "Message | Poll | None" = None,
edit: "Message | None" = None,
):
self.text = text
self.user = user
self.group = group
self.timestamp = timestamp
self.attachments = attachments
self.view_once = view_once
self.sticker = sticker
self.mentions = mentions
self.styles = styles
self.quote = quote
self.edit = edit
class Poll:
def __init__(
self,
question: str,
options: list[str],
user: User | None = None,
group: Group | None = None,
timestamp: int | None = None,
multiple: bool = True,
):
self.question = question
self.options = options
self.user = user
self.group = group
self.timestamp = timestamp
self.multiple = multiple
class Signal:
def __init__(self, host: str, port: int):
self.url = host.rstrip("/") + ":" + str(port)
self._hooks: dict[str, Callable] = {}
self._hooks: list[Callable] = []
async def _post(self, method: str, **params):
async with ClientSession() as session:
@ -127,11 +175,129 @@ class Signal:
f"Error while sending {method} to Server: HTTP code {response.status}."
)
async def sendMessage(self, message: str, recipient: User | Group):
async def sendMessage(self, message: Message, recipient: User | Group):
params: dict = {}
if isinstance(recipient, User):
return await self._post("send", message=message, recipient=recipient.id)
params["recipient"] = recipient.id
if isinstance(recipient, Group):
return await self._post("send", message=message, groupId=recipient.id)
params["groupId"] = recipient.id
if message.attachments:
params["attachment"] = [
str(file.absolute()) for file in message.attachments
]
if message.view_once:
params["viewOnce"] = message.view_once
if message.text:
params["message"] = message.text
if message.mentions:
params["mention"] = [mention.get() for mention in message.mentions]
if message.styles:
params["textStyle"] = [style.get() for style in message.styles]
if message.sticker:
params["sticker"] = message.sticker.pack_id + ":" + str(message.sticker.id)
if message.quote:
if message.quote.timestamp:
params["quoteTimestamp"] = message.quote.timestamp
if message.quote.user:
params["quoteAuthor"] = message.quote.user.id
if isinstance(message.quote, Message):
if message.quote.mentions:
params["quoteMention"] = [
mention.get() for mention in message.quote.mentions
]
if message.quote.styles:
params["quoteTextStyle"] = [
style.get() for style in message.quote.styles
]
if message.quote.attachments:
params["quoteAttachment"] = [
str(file.absolute()) for file in message.quote.attachments
]
if message.edit:
params["editTimestamp"] = message.edit.timestamp
result = await self._post("send", **params)
timestamp = result.get("result", {}).get("timestamp")
if timestamp:
message.timestamp = timestamp
message.user = User.self()
return result
async def sendReaction(self, emoji: str, message: Message, recipient: User | Group):
params: dict = {"emoji": emoji}
if isinstance(recipient, User):
params["recipient"] = recipient.id
if isinstance(recipient, Group):
params["groupId"] = recipient.id
if message.user:
params["targetAuthor"] = message.user.id
if message.timestamp:
params["targetTimestamp"] = message.timestamp
return await self._post("sendReaction", **params)
async def startTyping(self, recipient: User | Group):
params: dict = {}
if isinstance(recipient, User):
params["recipient"] = recipient.id
if isinstance(recipient, Group):
params["groupId"] = recipient.id
return await self._post("sendTyping", **params)
async def stopTyping(self, recipient: User | Group):
params: dict = {"stop": True}
if isinstance(recipient, User):
params["recipient"] = recipient.id
if isinstance(recipient, Group):
params["groupId"] = recipient.id
return await self._post("sendTyping", **params)
async def deleteMessage(self, message: Message, recipient: User | Group):
params: dict = {}
if isinstance(recipient, User):
params["recipient"] = recipient.id
if isinstance(recipient, Group):
params["groupId"] = recipient.id
params["targetTimestamp"] = message.timestamp
return await self._post("remoteDelete", **params)
# TODO: implement polls
"""
async def sendPoll(self, poll: Poll, recipient: User | Group):
params: dict = {}
if isinstance(recipient, User):
params["recipient"] = recipient.id
if isinstance(recipient, Group):
params["groupId"] = recipient.id
params["question"] = poll.question
params["options"] = poll.options
if not poll.multiple:
params["noMulti"] = not poll.multiple
result = await self._post("sendPollCreate", **params)
timestamp = result.get("result", {}).get("timestamp")
if timestamp:
poll.timestamp = timestamp
poll.user = User.self()
return result
"""
async def listGroups(self) -> list[Group]:
return [
@ -139,9 +305,11 @@ class Signal:
id=group["id"],
name=group["name"],
description=group["description"],
# members=[self.getUser(member["id"]) for member in group["members"]],
# admins=[self.getUser(admin["id"]) for admin in group["admins"]],
# banned=[self.getUser(ban["id"]) for ban in group["banned"]],
members=[
await self.getUser(member["uuid"]) for member in group["members"]
],
admins=[await self.getUser(admin["uuid"]) for admin in group["admins"]],
banned=[await self.getUser(ban["uuid"]) for ban in group["banned"]],
)
for group in (await self._post("listGroups"))["result"]
]
@ -167,6 +335,18 @@ class Signal:
for user in (await self._post("listContacts"))["result"]
]
async def getGroup(self, id: str) -> Group:
for group in await self.listGroups():
if group.id == id:
return group
return Group(id)
async def getUser(self, id: str) -> User:
for user in await self.listUsers():
if user.id == id:
return user
return User(id)
def onMessage(
self,
*,
@ -176,50 +356,74 @@ class Signal:
def wrapper(func: Callable):
async def callback(data: dict):
envelope = data.get("envelope", {})
dataMessage = envelope.get("dataMessage", {})
groupInfo = dataMessage.get("groupInfo", {})
if envelope and envelope.get("dataMessage"):
dataMessage = envelope["dataMessage"]
msg_user = User(
envelope.get("sourceName"),
envelope.get("sourceNumber"),
envelope.get("sourceUuid"),
message: str | None = dataMessage["message"]
groupId: str | None = None
if dataMessage.get("groupInfo"):
groupId = dataMessage["groupInfo"]["groupId"]
sticker: Sticker | None = None
if dataMessage.get("sticker"):
s = dataMessage["sticker"]
sticker = Sticker(s["packId"], s["stickerId"])
# attachments
# contacts
viewOnce: bool = dataMessage["viewOnce"]
sourceUuid: str = envelope["sourceUuid"]
timestamp: int = envelope["timestamp"]
msg_user: User = await self.getUser(sourceUuid)
msg_group: Group | None = (
(await self.getGroup(groupId)) if groupId else None
)
msg_group = None
if groupInfo:
msg_group = Group(
groupInfo.get("groupName"), groupInfo.get("groupId")
msg = Message(
text=message,
user=msg_user,
group=msg_group,
timestamp=timestamp,
sticker=sticker,
view_once=viewOnce,
)
msg = None
msg_body = envelope.get("dataMessage", {}).get("message")
if msg_body is not None:
msg = ReceivedMessage(msg_body, msg_user, msg_group)
await self._post(
"sendReceipt",
recipient=msg_user.id,
targetTimestamp=timestamp,
type="viewed",
)
if not user and not group:
return func(msg)
return await func(msg)
if isinstance(user, User) and user.id == msg_user.id:
return func(msg)
return await func(msg)
if (
isinstance(group, Group)
and msg_group
and group.id == msg_group.id
):
return func(msg)
return await func(msg)
if isinstance(user, list):
for usr in user:
if usr.id == msg_user.id:
return func(msg)
return await func(msg)
if isinstance(group, list) and msg_group:
for grp in group:
if grp.id == msg_group.id:
return func(msg)
return await func(msg)
self._hooks[func.__name__] = callback
self._hooks.append(callback)
return func
return wrapper
@ -227,26 +431,24 @@ class Signal:
def onMessageRaw(self) -> Callable:
def wrapper(func: Callable):
async def callback(data: dict):
func(data)
await func(data)
self._hooks[func.__name__] = callback
self._hooks.append(callback)
return func
return wrapper
async def loop(self):
for msg in SSEClient(self.url + "/api/v1/events"):
async with ClientSession() as session:
async with session.get(self.url + "/api/v1/events") as response:
if response.status == 200:
async for msg in response.content:
msg_data = msg.decode().strip()
if msg_data.startswith("data"):
try:
data = loads(msg.data.encode("latin1").decode())
data = loads(msg_data.lstrip("data:"))
except Exception:
data = {}
for callback in self._hooks.values():
await callback(data)
signal = Signal(Config.SIGNAL_HOST, Config.SIGNAL_PORT)
user = asyncio.run(signal.listGroups())[1]
print(user.__dict__)
for callback in self._hooks:
asyncio.create_task(callback(data))

42
main.py
View file

@ -1,41 +1,17 @@
import asyncio
import os
from importlib import import_module
from libminecraft import Minecraft
from libsignal import Group, ReceivedMessage, Signal
from libbot import minecraft, signal
RChat = Group("RS Chat", "5PlbXaPmWZQkhmuyyC/fkWTy8K+BqomjK7byVDyxmpo=")
for module in os.listdir("modules"):
if module.endswith(".py"):
import_module("modules." + module.rstrip(".py"))
async def main():
minecraft = Minecraft("http://localhost", 42101, "1234")
signal = Signal("http://localhost", 42069)
##########################
# === RETARDS SERVER === #
##########################
@signal.onMessage(RChat)
async def onRChatMessage(message: ReceivedMessage):
username = message.user.last_nickname or message.user.name
msg = message.message.replace('"', '\\"')
await minecraft.command(f'tellraw @a "<{username}> {msg}"')
@minecraft.onPlayerChat()
async def onRServerMessage(player: str, message: str):
await signal.sendMessage(f"<{player}> {message}", RChat)
##################
# === GLOBAL === #
##################
@signal.onMessage()
async def onMessage(message: ReceivedMessage):
if "clanker" in message.message.lower():
await signal.sendMessage(
"Don't use the c-word nigga", message.group or message.user
)
await signal.loop()
await asyncio.gather(minecraft.loop(), signal.loop())
asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())

11
modules/everyone.py Normal file
View file

@ -0,0 +1,11 @@
from libbot import signal
from libsignal import Message
@signal.onMessage()
async def onMessage(message: Message):
if message.user and message.text and "@everyone" in message.text:
await signal.sendMessage(
Message("EVERYBODY LISTEN!!!!", quote=message),
message.group or message.user,
)

View file

@ -0,0 +1,24 @@
import asyncio
from libbot import minecraft, signal
from libsignal import Message, MessageStyle
RSChat = asyncio.run(signal.getGroup("5PlbXaPmWZQkhmuyyC/fkWTy8K+BqomjK7byVDyxmpo="))
@minecraft.onPlayerChat()
async def onMinecraftMessage(player: str, message: str):
await signal.sendMessage(
Message(
f"<{player}> {message}", styles=[MessageStyle(0, len(player) + 2, "BOLD")]
),
RSChat,
)
await minecraft.command("say hi")
@signal.onMessage()
async def onSignalMessage(message: Message):
if message.user and message.user.note and "!rserver" in message.user.note:
username = message.user.last_nickname
await minecraft.command(f"tellraw @a <{username}> {message.text}")