From 8a6439a154aa3f98cd1f42b426a6e355744cea80 Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Sat, 29 Jul 2023 12:21:05 +0100 Subject: [PATCH 1/7] Proto Updates --- rustplus.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rustplus.proto b/rustplus.proto index ccbe695..c345eed 100644 --- a/rustplus.proto +++ b/rustplus.proto @@ -20,9 +20,9 @@ message Vector4 { } message Half3 { - optional float x = 1; - optional float y = 2; - optional float z = 3; + optional uint32 x = 1; + optional uint32 y = 2; + optional uint32 z = 3; } message Color { From 6497339f4c78c783fb2883fa7cccbeef0c8216c9 Mon Sep 17 00:00:00 2001 From: olijeffers0n <69084614+olijeffers0n@users.noreply.github.com> Date: Sat, 29 Jul 2023 12:28:36 +0100 Subject: [PATCH 2/7] Compile Protobuf Def's --- rustplus/api/remote/rustplus_proto/rustplus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rustplus/api/remote/rustplus_proto/rustplus.py b/rustplus/api/remote/rustplus_proto/rustplus.py index 175cdaa..0af528a 100644 --- a/rustplus/api/remote/rustplus_proto/rustplus.py +++ b/rustplus/api/remote/rustplus_proto/rustplus.py @@ -53,9 +53,9 @@ class Vector4(betterproto.Message): @dataclass class Half3(betterproto.Message): - x: float = betterproto.float_field(1) - y: float = betterproto.float_field(2) - z: float = betterproto.float_field(3) + x: int = betterproto.uint32_field(1) + y: int = betterproto.uint32_field(2) + z: int = betterproto.uint32_field(3) @dataclass @@ -277,6 +277,7 @@ class AppInfo(betterproto.Message): nexus: str = betterproto.string_field(13) nexus_id: int = betterproto.int32_field(14) nexus_zone: str = betterproto.string_field(15) + cameras_enabled: bool = betterproto.bool_field(16) @dataclass @@ -492,4 +493,4 @@ class AppCameraRaysEntity(betterproto.Message): position: "Vector3" = betterproto.message_field(3) rotation: "Vector3" = betterproto.message_field(4) size: "Vector3" = betterproto.message_field(5) - name: str = betterproto.string_field(6) + name: str = betterproto.string_field(6) \ No newline at end of file From 2a36a420fa3dbf3e966d6410b95a3832cf51010c Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:57:38 +0100 Subject: [PATCH 3/7] Clans Implementation --- rustplus.proto | 58 +++--- rustplus/api/base_rust_api.py | 48 +++++ rustplus/api/remote/events/__init__.py | 9 +- rustplus/api/remote/events/event_handler.py | 27 ++- rustplus/api/remote/events/events.py | 31 ++- .../api/remote/rustplus_proto/rustplus.py | 2 +- rustplus/api/remote/rustws.py | 25 ++- rustplus/api/rust_api.py | 58 ++++++ rustplus/api/structures/__init__.py | 2 + rustplus/api/structures/rust_chat_message.py | 11 +- rustplus/api/structures/rust_clan_info.py | 194 ++++++++++++++++++ rustplus/api/structures/rust_clan_message.py | 25 +++ 12 files changed, 448 insertions(+), 42 deletions(-) create mode 100644 rustplus/api/structures/rust_clan_info.py create mode 100644 rustplus/api/structures/rust_clan_message.py diff --git a/rustplus.proto b/rustplus.proto index c345eed..c53f151 100644 --- a/rustplus.proto +++ b/rustplus.proto @@ -37,12 +37,12 @@ message Ray { optional Vector3 direction = 2; } -message ClanActionResult { - required int32 requestId = 1; - required int32 result = 2; - required bool hasClanInfo = 3; - optional ClanInfo clanInfo = 4; -} +// message ClanActionResult { +// required int32 requestId = 1; +// required int32 result = 2; +// required bool hasClanInfo = 3; +// optional ClanInfo clanInfo = 4; +// } message ClanInfo { required int64 clanId = 1; @@ -89,29 +89,29 @@ message ClanInfo { } } -message ClanLog { - required int64 clanId = 1; - repeated ClanLog.Entry logEntries = 2; - - message Entry { - required int64 timestamp = 1; - required string eventKey = 2; - optional string arg1 = 3; - optional string arg2 = 4; - optional string arg3 = 5; - optional string arg4 = 6; - } -} - -message ClanInvitations { - repeated ClanInvitations.Invitation invitations = 1; - - message Invitation { - required int64 clanId = 1; - required uint64 recruiter = 2; - required int64 timestamp = 3; - } -} +// message ClanLog { +// required int64 clanId = 1; +// repeated ClanLog.Entry logEntries = 2; +// +// message Entry { +// required int64 timestamp = 1; +// required string eventKey = 2; +// optional string arg1 = 3; +// optional string arg2 = 4; +// optional string arg3 = 5; +// optional string arg4 = 6; +// } +// } + +// message ClanInvitations { +// repeated ClanInvitations.Invitation invitations = 1; +// +// message Invitation { +// required int64 clanId = 1; +// required uint64 recruiter = 2; +// required int64 timestamp = 3; +// } +// } enum AppEntityType { Switch = 1; diff --git a/rustplus/api/base_rust_api.py b/rustplus/api/base_rust_api.py index 57444c9..4daf9e8 100644 --- a/rustplus/api/base_rust_api.py +++ b/rustplus/api/base_rust_api.py @@ -16,6 +16,7 @@ TeamEvent, ChatEvent, ProtobufEvent, + ClanInfoEvent, ) from ..utils import deprecated from ..conversation import ConversationFactory @@ -404,6 +405,21 @@ def protobuf_received(self, coro) -> RegisteredListener: ProtobufEvent.handlers.register(listener, self.server_id) return listener + def clan_info_event(self, coro) -> RegisteredListener: + """ + A Decorator to register an event listener for clan info being received on the websocket + + :param coro: The coroutine to call when the command is called + :return: RegisteredListener - The listener object + """ + + if isinstance(coro, RegisteredListener): + coro = coro.get_coro() + + listener = RegisteredListener("clan_info_received", coro) + ClanInfoEvent.handlers.register(listener, self.server_id) + return listener + def remove_listener(self, listener) -> bool: """ This will remove a listener, command or event. Takes a RegisteredListener instance @@ -612,3 +628,35 @@ async def get_camera_manager(self, cam_id: str) -> CameraManager: :raises RequestError: If the camera is not found, or you cannot access it. See reason for more info """ raise NotImplementedError("Not Implemented") + + async def get_clan_info(self) -> RustClanInfo: + """ + Gets the clan info from the server + + :return RustClanInfo: The clan info. Will be None if not in a clan + """ + raise NotImplementedError("Not Implemented") + + async def get_clan_chat(self) -> List[RustClanMessage]: + """ + Gets the clan chat from the server + + :return List[RustClanMessage]: The clan chat messages + """ + raise NotImplementedError("Not Implemented") + + async def send_clan_message(self, message: str) -> None: + """ + Sends a message to the clan chat + + :param message: The message to send + """ + raise NotImplementedError("Not Implemented") + + async def set_clan_motd(self, message: str) -> None: + """ + Sets the clan MOTD + + :param message: The message to set + """ + raise NotImplementedError("Not Implemented") diff --git a/rustplus/api/remote/events/__init__.py b/rustplus/api/remote/events/__init__.py index e303ed1..8da66f2 100644 --- a/rustplus/api/remote/events/__init__.py +++ b/rustplus/api/remote/events/__init__.py @@ -1,4 +1,11 @@ from .registered_listener import RegisteredListener -from .events import EntityEvent, TeamEvent, ChatEvent, MarkerEvent, ProtobufEvent +from .events import ( + EntityEvent, + TeamEvent, + ChatEvent, + MarkerEvent, + ProtobufEvent, + ClanInfoEvent, +) from .event_loop_manager import EventLoopManager from .event_handler import EventHandler diff --git a/rustplus/api/remote/events/event_handler.py b/rustplus/api/remote/events/event_handler.py index 6c45f9e..2e80cb5 100644 --- a/rustplus/api/remote/events/event_handler.py +++ b/rustplus/api/remote/events/event_handler.py @@ -1,7 +1,8 @@ from typing import Set, Union +from ...structures import RustChatMessage, RustClanInfo from ....utils import ServerID -from .events import EntityEvent, TeamEvent, ChatEvent, ProtobufEvent +from .events import EntityEvent, TeamEvent, ChatEvent, ProtobufEvent, ClanInfoEvent from .registered_listener import RegisteredListener from ..rustplus_proto import AppMessage @@ -33,7 +34,29 @@ async def run_team_event(app_message: AppMessage, server_id: ServerID) -> None: async def run_chat_event(app_message: AppMessage, server_id: ServerID) -> None: handlers: Set[RegisteredListener] = ChatEvent.handlers.get_handlers(server_id) for handler in handlers.copy(): - await handler.get_coro()(ChatEvent(app_message)) + message = RustChatMessage(app_message.broadcast.team_message.message) + await handler.get_coro()(ChatEvent(message, False, None)) + + @staticmethod + async def run_clan_chat_event(app_message: AppMessage, server_id: ServerID) -> None: + handlers: Set[RegisteredListener] = ChatEvent.handlers.get_handlers(server_id) + for handler in handlers.copy(): + message = RustChatMessage(app_message.broadcast.clan_message.message) + await handler.get_coro()( + ChatEvent(message, True, app_message.broadcast.clan_message.clan_id) + ) + + @staticmethod + async def run_clan_info_event(app_message: AppMessage, server_id: ServerID) -> None: + handlers: Set[RegisteredListener] = ClanInfoEvent.handlers.get_handlers( + server_id + ) + for handler in handlers.copy(): + await handler.get_coro()( + ClanInfoEvent( + RustClanInfo(app_message.broadcast.clan_changed.clan_info) + ) + ) @staticmethod async def run_proto_event(byte_data: bytes, server_id: ServerID) -> None: diff --git a/rustplus/api/remote/events/events.py b/rustplus/api/remote/events/events.py index 6a6afaa..9da12d9 100644 --- a/rustplus/api/remote/events/events.py +++ b/rustplus/api/remote/events/events.py @@ -1,7 +1,7 @@ -from typing import List +from typing import List, Union from ..rustplus_proto import AppMessage, AppEntityPayloadItem -from ...structures import RustChatMessage +from ...structures import RustChatMessage, RustClanInfo from ...structures.rust_team_info import RustTeamInfo from ...structures.rust_marker import RustMarker from .handler_list import HandlerList, EntityHandlerList @@ -45,13 +45,25 @@ def team_info(self) -> RustTeamInfo: class ChatEvent: handlers = HandlerList() - def __init__(self, app_message: AppMessage) -> None: - self._message = RustChatMessage(app_message.broadcast.team_message.message) + def __init__( + self, message: RustChatMessage, is_clan: bool, clan_id: Union[int, None] + ) -> None: + self._message = message + self._is_clan = is_clan + self._clan_id = clan_id @property def message(self) -> RustChatMessage: return self._message + @property + def is_clan(self) -> bool: + return self._is_clan + + @property + def clan_id(self) -> Union[int, None]: + return self._clan_id + class EntityEvent: handlers = EntityHandlerList() @@ -124,3 +136,14 @@ def __init__(self, byte_data) -> None: @property def byte_data(self) -> bytes: return self._byte_data + + +class ClanInfoEvent: + handlers = HandlerList() + + def __init__(self, clan_info: RustClanInfo) -> None: + self._clan_info = clan_info + + @property + def clan_info(self) -> RustClanInfo: + return self._clan_info diff --git a/rustplus/api/remote/rustplus_proto/rustplus.py b/rustplus/api/remote/rustplus_proto/rustplus.py index 0af528a..74e6c92 100644 --- a/rustplus/api/remote/rustplus_proto/rustplus.py +++ b/rustplus/api/remote/rustplus_proto/rustplus.py @@ -493,4 +493,4 @@ class AppCameraRaysEntity(betterproto.Message): position: "Vector3" = betterproto.message_field(3) rotation: "Vector3" = betterproto.message_field(4) size: "Vector3" = betterproto.message_field(5) - name: str = betterproto.string_field(6) \ No newline at end of file + name: str = betterproto.string_field(6) diff --git a/rustplus/api/remote/rustws.py b/rustplus/api/remote/rustws.py index 81be523..0c7942c 100644 --- a/rustplus/api/remote/rustws.py +++ b/rustplus/api/remote/rustws.py @@ -80,7 +80,10 @@ async def connect( ) address += f"?v={str(self.magic_value)}" self.connection = await connect( - address, close_timeout=0, ping_interval=None, max_size=1_000_000_000 + address, + close_timeout=0, + ping_interval=None, + max_size=1_000_000_000, ) self.connected_time = time.time() @@ -228,6 +231,12 @@ async def handle_message(self, app_message: AppMessage) -> None: # This means that the team of the current player has changed await EventHandler.run_team_event(app_message, self.server_id) + elif self.is_clan_message(app_message): + await EventHandler.run_clan_chat_event(app_message, self.server_id) + + elif self.is_clan_change_info(app_message): + await EventHandler.run_clan_info_event(app_message, self.server_id) + elif self.is_message(app_message): # This means that a message has been sent to the team chat @@ -289,6 +298,18 @@ def is_message(app_message: AppMessage) -> bool: app_message.broadcast.team_message.message ) + @staticmethod + def is_clan_message(app_message: AppMessage) -> bool: + return betterproto.serialized_on_wire( + app_message.broadcast.clan_message.message + ) + + @staticmethod + def is_clan_change_info(app_message: AppMessage) -> bool: + return betterproto.serialized_on_wire( + app_message.broadcast.clan_changed.clan_info + ) + @staticmethod def is_camera_broadcast(app_message: AppMessage) -> bool: return betterproto.serialized_on_wire(app_message.broadcast.camera_rays) @@ -329,7 +350,7 @@ def error_present(message) -> bool: """ Checks message for error """ - return message != "" + return message != "" and "clan" not in message @staticmethod async def run_coroutine_non_blocking(coroutine: Coroutine) -> Task: diff --git a/rustplus/api/rust_api.py b/rustplus/api/rust_api.py index 7b99ac3..5143d06 100644 --- a/rustplus/api/rust_api.py +++ b/rustplus/api/rust_api.py @@ -18,6 +18,8 @@ RustEntityInfo, RustContents, RustItem, + RustClanInfo, + RustClanMessage, ) from .remote.rustplus_proto import ( AppEmpty, @@ -407,3 +409,59 @@ async def get_tc_storage_contents( async def get_camera_manager(self, cam_id: str) -> CameraManager: return await self.remote.create_camera_manager(cam_id) + + async def get_clan_info(self) -> RustClanInfo: + await self._handle_ratelimit() + + app_request = self._generate_protobuf() + app_request.get_clan_info = AppEmpty() + + await self.remote.send_message(app_request) + + app_message = await self.remote.get_response(app_request.seq, app_request) + + if app_message.response.error.error != "": + return None + + return RustClanInfo(app_message.response.clan_info) + + async def get_clan_chat(self) -> List[RustClanMessage]: + await self._handle_ratelimit() + + app_request = self._generate_protobuf() + app_request.get_clan_chat = AppEmpty() + + await self.remote.send_message(app_request) + + app_message = await self.remote.get_response(app_request.seq, app_request) + + return [ + RustClanMessage(message) + for message in app_message.response.clan_chat.messages + ] + + async def send_clan_message(self, message: str) -> None: + await self._handle_ratelimit(2) + + app_send_message = AppSendMessage() + app_send_message.message = str(message) + + app_request = self._generate_protobuf() + app_request.send_clan_message = app_send_message + + await self.remote.add_ignored_response(app_request.seq) + + await self.remote.send_message(app_request) + + async def set_clan_motd(self, message: str) -> None: + await self._handle_ratelimit() + + app_send_message = AppSendMessage() + app_send_message.message = str(message) + + app_request = self._generate_protobuf() + app_request.set_clan_motd = app_send_message + + await self.remote.add_ignored_response(app_request.seq) + + await self.remote.send_message(app_request) diff --git a/rustplus/api/structures/__init__.py b/rustplus/api/structures/__init__.py index 5ac6b8f..998a43e 100644 --- a/rustplus/api/structures/__init__.py +++ b/rustplus/api/structures/__init__.py @@ -8,3 +8,5 @@ from .rust_contents import RustContents from .rust_item import RustItem from .util import Vector +from .rust_clan_info import RustClanInfo +from .rust_clan_message import RustClanMessage diff --git a/rustplus/api/structures/rust_chat_message.py b/rustplus/api/structures/rust_chat_message.py index a6ba71b..f2decbb 100644 --- a/rustplus/api/structures/rust_chat_message.py +++ b/rustplus/api/structures/rust_chat_message.py @@ -1,14 +1,19 @@ -from ..remote.rustplus_proto import AppTeamMessage +from typing import Union +from ..remote.rustplus_proto import AppTeamMessage, AppClanMessage class RustChatMessage: - def __init__(self, data: AppTeamMessage): + def __init__(self, data: Union[AppTeamMessage, AppClanMessage]): self._steam_id: int = data.steam_id self._name: str = data.name self._message: str = data.message - self._colour: str = data.color self._time: int = data.time + if isinstance(data, AppTeamMessage): + self._colour: str = data.color + else: + self._colour: str = "#af5" + @property def steam_id(self) -> int: return self._steam_id diff --git a/rustplus/api/structures/rust_clan_info.py b/rustplus/api/structures/rust_clan_info.py new file mode 100644 index 0000000..1435b8c --- /dev/null +++ b/rustplus/api/structures/rust_clan_info.py @@ -0,0 +1,194 @@ +from ..remote.rustplus_proto import ( + ClanInfo, + ClanInfoRole, + ClanInfoMember, + ClanInfoInvite, +) +from typing import List + + +class RustClanRole: + def __init__(self, role: ClanInfoRole) -> None: + self._role_id: int = role.role_id + self._rank: int = role.rank + self._name: str = role.name + self._can_set_motd: bool = role.can_set_motd + self._can_set_logo: bool = role.can_set_logo + self._can_invite: bool = role.can_invite + self._can_kick: bool = role.can_kick + self._can_promote: bool = role.can_promote + self._can_demote: bool = role.can_demote + self._can_set_player_notes: bool = role.can_set_player_notes + self._can_access_logs: bool = role.can_access_logs + + @property + def role_id(self) -> int: + return self._role_id + + @property + def rank(self) -> int: + return self._rank + + @property + def name(self) -> str: + return self._name + + @property + def can_set_motd(self) -> bool: + return self._can_set_motd + + @property + def can_set_logo(self) -> bool: + return self._can_set_logo + + @property + def can_invite(self) -> bool: + return self._can_invite + + @property + def can_kick(self) -> bool: + return self._can_kick + + @property + def can_promote(self) -> bool: + return self._can_promote + + @property + def can_demote(self) -> bool: + return self._can_demote + + @property + def can_set_player_notes(self) -> bool: + return self._can_set_player_notes + + @property + def can_access_logs(self) -> bool: + return self._can_access_logs + + +class RustClanMember: + def __init__(self, member: ClanInfoMember) -> None: + self._steam_id: int = member.steam_id + self._role_id: int = member.role_id + self._joined_time: int = member.joined + self._last_seen: int = member.last_seen + self._notes: str = member.notes + self._online: bool = member.online + + @property + def steam_id(self) -> int: + return self._steam_id + + @property + def role_id(self) -> int: + return self._role_id + + @property + def joined_time(self) -> int: + return self._joined_time + + @property + def last_seen(self) -> int: + return self._last_seen + + @property + def notes(self) -> str: + return self._notes + + @property + def online(self) -> bool: + return self._online + + +class RustClanInvite: + def __init__(self, invite: ClanInfoInvite) -> None: + self._steam_id: int = invite.steam_id + self._recruiter: int = invite.recruiter + self._invited_time: int = invite.timestamp + + @property + def steam_id(self) -> int: + return self._steam_id + + @property + def recruiter(self) -> int: + return self._recruiter + + @property + def invited_time(self) -> int: + return self._invited_time + + +class RustClanInfo: + def __init__(self, clan_info: ClanInfo) -> None: + self._clan_id: int = clan_info.clan_id + self._clan_name: str = clan_info.clan_name + self._clan_time_created: int = clan_info.created + self._creator: int = clan_info.creator + self._motd: str = clan_info.motd + self._motd_time_set: int = clan_info.motd_timestamp + self._motd_author: int = clan_info.motd_author + self._logo: bytes = clan_info.logo + self._colour: int = clan_info.color + self._roles: List[RustClanRole] = [ + RustClanRole(role) for role in clan_info.roles + ] + self._members: List[RustClanMember] = [ + RustClanMember(member) for member in clan_info.members + ] + self._invites: List[RustClanInvite] = [ + RustClanInvite(invite) for invite in clan_info.invites + ] + self._max_members: int = clan_info.max_member_count + + @property + def clan_id(self) -> int: + return self._clan_id + + @property + def clan_name(self) -> str: + return self._clan_name + + @property + def clan_time_created(self) -> int: + return self._clan_time_created + + @property + def creator(self) -> int: + return self._creator + + @property + def motd(self) -> str: + return self._motd + + @property + def motd_time_set(self) -> int: + return self._motd_time_set + + @property + def motd_author(self) -> int: + return self._motd_author + + @property + def logo(self) -> bytes: + return self._logo + + @property + def colour(self) -> int: + return self._colour + + @property + def roles(self) -> List[RustClanRole]: + return self._roles + + @property + def members(self) -> List[RustClanMember]: + return self._members + + @property + def invites(self) -> List[RustClanInvite]: + return self._invites + + @property + def max_members(self) -> int: + return self._max_members diff --git a/rustplus/api/structures/rust_clan_message.py b/rustplus/api/structures/rust_clan_message.py new file mode 100644 index 0000000..b1a19bd --- /dev/null +++ b/rustplus/api/structures/rust_clan_message.py @@ -0,0 +1,25 @@ +from ..remote.rustplus_proto import AppClanMessage + + +class RustClanMessage: + def __init__(self, message: AppClanMessage) -> None: + self._steam_id: int = message.steam_id + self._name: str = message.name + self._message: str = message.message + self._timestamp: int = message.time + + @property + def steam_id(self) -> int: + return self._steam_id + + @property + def name(self) -> str: + return self._name + + @property + def message(self) -> str: + return self._message + + @property + def timestamp(self) -> int: + return self._timestamp From 5e748de6f086621a654c4264f43b627ecbffd5c1 Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:46:15 +0100 Subject: [PATCH 4/7] Add some initial nexus logic --- rustplus/api/base_rust_api.py | 9 +++++++++ rustplus/api/rust_api.py | 16 ++++++++++++++++ rustplus/api/structures/rust_info.py | 20 +++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/rustplus/api/base_rust_api.py b/rustplus/api/base_rust_api.py index 57444c9..4805ed7 100644 --- a/rustplus/api/base_rust_api.py +++ b/rustplus/api/base_rust_api.py @@ -612,3 +612,12 @@ async def get_camera_manager(self, cam_id: str) -> CameraManager: :raises RequestError: If the camera is not found, or you cannot access it. See reason for more info """ raise NotImplementedError("Not Implemented") + + async def get_nexus_player_token(self, app_key: str) -> str: + """ + Gets the Nexus Player Token for the server + + :param app_key: The App Key for the server + :return str: The Nexus Player Token + """ + raise NotImplementedError("Not Implemented") diff --git a/rustplus/api/rust_api.py b/rustplus/api/rust_api.py index 7b99ac3..d9e7a50 100644 --- a/rustplus/api/rust_api.py +++ b/rustplus/api/rust_api.py @@ -24,6 +24,7 @@ AppSendMessage, AppSetEntityValue, AppPromoteToLeader, + AppGetNexusAuth, ) from .remote import HeartBeat, RateLimiter from ..commands import CommandOptions @@ -407,3 +408,18 @@ async def get_tc_storage_contents( async def get_camera_manager(self, cam_id: str) -> CameraManager: return await self.remote.create_camera_manager(cam_id) + + async def get_nexus_player_token(self, app_key: str) -> str: + await self._handle_ratelimit() + + app_request = self._generate_protobuf() + + get_auth = AppGetNexusAuth() + get_auth.app_key = app_key + app_request.get_nexus_auth = get_auth + + await self.remote.send_message(app_request) + + app_message = await self.remote.get_response(app_request.seq, app_request) + + return app_message.response.nexus_auth diff --git a/rustplus/api/structures/rust_info.py b/rustplus/api/structures/rust_info.py index 88eb128..f6d5f79 100644 --- a/rustplus/api/structures/rust_info.py +++ b/rustplus/api/structures/rust_info.py @@ -14,6 +14,9 @@ def __init__(self, data: AppInfo) -> None: self._queued_players: int = data.queued_players self._seed: int = data.seed self._logo_image: str = data.logo_image + self._nexus: str = data.nexus + self._nexus_id: int = data.nexus_id + self._nexus_zone: str = data.nexus_zone @property def url(self) -> str: @@ -59,10 +62,22 @@ def header_image(self) -> str: def logo_image(self) -> str: return self._logo_image + @property + def nexus(self) -> str: + return self._nexus + + @property + def nexus_id(self) -> int: + return self._nexus_id + + @property + def nexus_zone(self) -> str: + return self._nexus_zone + def __str__(self) -> str: return ( "RustInfo[url={}, name={}, map={}, size={}, players={}, max_players={}, queued_players={}, seed={}, " - "wipe_time={}, header_image={}, logo_image={}]".format( + "wipe_time={}, header_image={}, logo_image={}, nexus={}, nexus_id={}, nexus_zone={}]".format( self._url, self._name, self._map, @@ -74,5 +89,8 @@ def __str__(self) -> str: self._wipe_time, self._header_image, self._logo_image, + self._nexus, + self._nexus_id, + self._nexus_zone, ) ) From 6628bee091f6a03fae36f5230243fc7acc1f9f63 Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Wed, 22 May 2024 22:17:08 +0100 Subject: [PATCH 5/7] Change Signature and Unregister ClanInfoEvent --- rustplus/api/base_rust_api.py | 6 +++++- rustplus/api/rust_api.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rustplus/api/base_rust_api.py b/rustplus/api/base_rust_api.py index d8ac7b4..464c1d9 100644 --- a/rustplus/api/base_rust_api.py +++ b/rustplus/api/base_rust_api.py @@ -469,6 +469,10 @@ def remove_listener(self, listener) -> bool: ProtobufEvent.handlers.unregister(listener, self.server_id) return True + if ClanInfoEvent.handlers.has(listener, self.server_id): + ClanInfoEvent.handlers.unregister(listener, self.server_id) + return True + return False @staticmethod @@ -652,7 +656,7 @@ async def get_camera_manager(self, cam_id: str) -> CameraManager: """ raise NotImplementedError("Not Implemented") - async def get_clan_info(self) -> RustClanInfo: + async def get_clan_info(self) -> Union[RustClanInfo, None]: """ Gets the clan info from the server diff --git a/rustplus/api/rust_api.py b/rustplus/api/rust_api.py index 667a47f..6e2bac8 100644 --- a/rustplus/api/rust_api.py +++ b/rustplus/api/rust_api.py @@ -430,7 +430,7 @@ async def get_tc_storage_contents( async def get_camera_manager(self, cam_id: str) -> CameraManager: return await self.remote.create_camera_manager(cam_id) - async def get_clan_info(self) -> RustClanInfo: + async def get_clan_info(self) -> Union[RustClanInfo, None]: await self._handle_ratelimit() app_request = self._generate_protobuf() From d52d865bd0ef855ca3cdca7943d68aa09ed68d66 Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Wed, 22 May 2024 22:24:43 +0100 Subject: [PATCH 6/7] Formatting Changes --- rustplus/api/base_rust_api.py | 14 +++++++------- rustplus/api/rust_api.py | 12 ++---------- rustplus/api/structures/rust_marker.py | 2 ++ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/rustplus/api/base_rust_api.py b/rustplus/api/base_rust_api.py index 464c1d9..fb4f479 100644 --- a/rustplus/api/base_rust_api.py +++ b/rustplus/api/base_rust_api.py @@ -321,20 +321,20 @@ def command( self.remote.command_handler.register_command(cmd_data) return RegisteredListener(coro.__name__, cmd_data.coro) - def wrap_func(coro): + def wrap_func(coroutine: Union[RegisteredListener, Coroutine]): if self.command_options is None: raise CommandsNotEnabledError("Not enabled") - if isinstance(coro, RegisteredListener): - coro = coro.get_coro() + if isinstance(coroutine, RegisteredListener): + coroutine = coroutine.get_coro() - cmd_data = CommandData( - coro, + command_data = CommandData( + coroutine, aliases, alias_func, ) - self.remote.command_handler.register_command(cmd_data) - return RegisteredListener(coro.__name__, cmd_data.coro) + self.remote.command_handler.register_command(command_data) + return RegisteredListener(coroutine.__name__, coroutine) return wrap_func diff --git a/rustplus/api/rust_api.py b/rustplus/api/rust_api.py index 6e2bac8..69fc847 100644 --- a/rustplus/api/rust_api.py +++ b/rustplus/api/rust_api.py @@ -245,11 +245,7 @@ async def get_map( for marker in map_markers: if add_events: if ( - marker.type == 2 - or marker.type == 4 - or marker.type == 5 - or marker.type == 6 - or marker.type == 8 + marker.type in RustMarker.Events ): icon = convert_marker(str(marker.type), marker.rotation) if marker.type == 6: @@ -364,11 +360,7 @@ async def get_current_events(self) -> List[RustMarker]: return [ marker for marker in (await self.get_markers()) - if marker.type == 2 - or marker.type == 4 - or marker.type == 5 - or marker.type == 6 - or marker.type == 8 + if marker.type in RustMarker.Events ] async def get_contents( diff --git a/rustplus/api/structures/rust_marker.py b/rustplus/api/structures/rust_marker.py index 0b18604..8003de8 100644 --- a/rustplus/api/structures/rust_marker.py +++ b/rustplus/api/structures/rust_marker.py @@ -94,6 +94,8 @@ class RustMarker(Serializable): RadiusMarker = 7 PatrolHelicopterMarker = 8 + Events = (ExplosionMarker, ChinookMarker, CargoShipMarker, CrateMarker, PatrolHelicopterMarker) + def __init__(self, data: AppMarker) -> None: self._id: int = data.id self._type: int = data.type From 741e299f30a813a368d0413d3b134166960f832c Mon Sep 17 00:00:00 2001 From: Ollie <69084614+olijeffers0n@users.noreply.github.com> Date: Wed, 22 May 2024 22:32:16 +0100 Subject: [PATCH 7/7] Formatting Changes --- rustplus/api/remote/rustws.py | 7 ++++--- rustplus/api/rust_api.py | 4 +--- rustplus/api/structures/rust_marker.py | 8 +++++++- rustplus/commands/command_handler.py | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/rustplus/api/remote/rustws.py b/rustplus/api/remote/rustws.py index d9d953f..c129818 100644 --- a/rustplus/api/remote/rustws.py +++ b/rustplus/api/remote/rustws.py @@ -214,9 +214,10 @@ async def run(self) -> None: try: # This creates an asyncio task rather than awaiting the coroutine directly. - # This fixes the bug where if you called a BaseRustSocket#get... from within a RegisteredListener or callback, - # It would hang the websocket. This is because the websocket event loop would be stuck on the callback rather than polling the socket. - # This way, we can schedule the execution of all logic for this message, but continue polling the WS + # This fixes the bug where if you called a BaseRustSocket#get... from within a RegisteredListener or + # callback, It would hang the websocket. This is because the websocket event loop would be stuck on the + # callback rather than polling the socket. This way, we can schedule the execution of all logic for this + # message, but continue polling the WS await self.run_coroutine_non_blocking(self.handle_message(app_message)) except Exception: self.logger.exception( diff --git a/rustplus/api/rust_api.py b/rustplus/api/rust_api.py index 69fc847..cec21d5 100644 --- a/rustplus/api/rust_api.py +++ b/rustplus/api/rust_api.py @@ -244,9 +244,7 @@ async def get_map( for marker in map_markers: if add_events: - if ( - marker.type in RustMarker.Events - ): + if marker.type in RustMarker.Events: icon = convert_marker(str(marker.type), marker.rotation) if marker.type == 6: x = marker.x diff --git a/rustplus/api/structures/rust_marker.py b/rustplus/api/structures/rust_marker.py index 8003de8..a7cef3e 100644 --- a/rustplus/api/structures/rust_marker.py +++ b/rustplus/api/structures/rust_marker.py @@ -94,7 +94,13 @@ class RustMarker(Serializable): RadiusMarker = 7 PatrolHelicopterMarker = 8 - Events = (ExplosionMarker, ChinookMarker, CargoShipMarker, CrateMarker, PatrolHelicopterMarker) + Events = ( + ExplosionMarker, + ChinookMarker, + CargoShipMarker, + CrateMarker, + PatrolHelicopterMarker, + ) def __init__(self, data: AppMarker) -> None: self._id: int = data.id diff --git a/rustplus/commands/command_handler.py b/rustplus/commands/command_handler.py index 47d5107..87a6961 100644 --- a/rustplus/commands/command_handler.py +++ b/rustplus/commands/command_handler.py @@ -23,7 +23,7 @@ def register_command(self, data: CommandData) -> None: async def run_command(self, message: RustChatMessage, prefix) -> None: if prefix == self.command_options.prefix: - command = shlex.split(message.message)[0][len(prefix) :] + command = shlex.split(message.message)[0][len(prefix):] else: command = prefix