Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
DEV_MODE=False
# Enable or disable the development mode
DEV_MODE=False

# Enable or disable plugins in development mode
PLUGINS_DEV_MODE=True

# Rather or not to use WebSockets
USE_WEBSOCKET=True
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
> [!WARNING]
> DO NOT USE THIS BRANCH!
> WEBSOCKETS ARE IN EARLY BETA AND REQUIRE A SPECIAL AUTHENTICATION ON THE API
> LAUNCHING THIS VERSION MIGHT RESULT IN A BAN FROM THE API
>
> PLEASE USE THE `main` BRANCH INSTEAD!

<div align="center">

<img src="./assets/polsu/Polsu.png" alt="Polsu" width="100" height="100" style="border-radius:20px"/>
Expand All @@ -10,8 +17,8 @@ A Hypixel Bedwars Overlay in Python, 100% free and open source!
<a><img src="https://wakatime.com/badge/user/ae13d286-a127-41f2-b631-4c4b2e09d04c/project/bdf90bf4-bb4f-4adb-8305-c4523e188b2c.svg" alt="Wakatime"></a>

</div>


# 📖 Table of Contents

- [📝 About](#-about)
Expand Down
11 changes: 10 additions & 1 deletion dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@
else:
logger.critical("You are running the development version of the overlay! However, you are not in development mode.")

print("You are running the development version of the overlay! However, you are not in development mode.")
print("\n")
print("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")
print("┃ ┃")
print("┃ WARNING ! ┃")
print("┃ ┃")
print("┃ You are running the development version of the overlay! However, you are not in development mode. ┃")
print("┃ Please set the DEV_MODE variable to True in the environment file! Or run the main.py file. ┃")
print("┃ ┃")
print("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
print("\n")

sys.exit(1)
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@


if getattr(sys, 'frozen', False):
import pyi_splash
import pyi_splash # type: ignore


def run(window: Updater, logger: Logger) -> None:
Expand Down
128 changes: 125 additions & 3 deletions src/PolsuAPI/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

import traceback

from aiohttp import ClientSession, ContentTypeError
from aiohttp import ClientSession, ContentTypeError, WSMsgType
from json import load


Expand Down Expand Up @@ -144,6 +144,124 @@ async def login(self) -> User:
self.logger.error(f"An error occurred while logging in!\n\nTraceback: {traceback.format_exc()} | {e}")
return None


async def WebSocketConnection(self, setWebSocket, callback, closed) -> None:
"""
Create a WebSocket connection

:param setWebSocket: A function to set the WebSocket
:param callback: A function to call when a player is received
:param closed: A function to call when the WebSocket is closed
"""
self.logger.info(f"[WS] Creating a WebSocket connection...")

# Wait for messages
expired = False

try:
# Create a WebSocket connection
async with ClientSession() as session:
async with session.ws_connect(
url=f"{self.api}/internal/overlay/websocket",
headers=self.polsuHeaders,
timeout=1800,
receive_timeout=1800,
heartbeat=1800,
autoping=True,
autoclose=False
) as ws:
if not ws.closed:
self.logger.info(f"[WS] Starting websocket connection...")
self.logger.debug(f"[WS] Sending handshake...")

# Send a handshake
await ws.send_json(
{
"protocol": "PolsuWebSocketProtocol - 1.0",
"handshake": self.key
}
)

self.logger.debug(f"[WS] Handshake sent!")

if not ws.closed:
# Wait for the handshake response
async for msg in ws:
try:
data = msg.json()
if data.get('success', False) and data.get('data', {}) == "Connection established.":
self.logger.info(f"[WS] New WebSocket connection established!")
break
else:
self.logger.debug(f"[WS] Couldn't create a WebSocket connection! {msg}")
break
except:
self.logger.debug(f"[WS] Couldn't create a WebSocket connection! {msg}")
break

if not ws.closed:
# Set the WebSocket so the overlay can send messages
await setWebSocket(ws)

self.logger.info(f"[WS] WebSocket connection established!")

async for msg in ws:
if msg.type in (WSMsgType.CLOSED, WSMsgType.ERROR):
return
else:
try:
data = msg.json()

if DEV_MODE:
self.logger.debug(f"[WS] Received data: {data}")

if data.get("data", {}) != {}:
if data.get("success", False):
player = Player(data.get("data"))
player.manual = data.get("data", {}).get("player", {}).get("manual", False)
player.websocket = True
await callback(player)
else:
if isinstance(data.get("data", {}), str):
if data.get("data", {}) == "Expired websocket!":
self.logger.warning(f"[WS] WebSocket connection expired!")
expired = True

raise Exception("Expired websocket!")
elif data.get("data", {}) == "Packet limit reached!":
self.logger.error(f"[WS] Packet limit reached!")
expired = True

raise Exception("Packet limit reached!")
elif data.get("data", {}) == "Malformed JSON" or data.get("data", {}) == "Missing query" or data.get("data", {}) == "Invalid query":
self.logger.error(f"[WS] An error occurred while sending some data: {data.get('data', {})}")
elif data.get("data", {}) == "Too many players":
self.logger.error(f"[WS] Oops we reached the maximum amount of players!")
else:
self.logger.error(f"[WS] An error occurred with the websocket connection: {data.get('data', {})}")
else:
if data.get("cause", None).startswith("Rate Limited"):
self.logger.error(f"[WS] Rate limited! {data.get('cause', None)}")

raise Exception("Rate limited!")
else:
f = open(f"{resource_path('src/PolsuAPI')}/schemas/nicked.json", mode="r", encoding="utf-8")
player = Player(load(f))
player.username = data.get("data", {}).get("player", {}).get("username")
player.rank = f"§c{data.get('data', {}).get('player', {}).get('username')}"
player.manual = data.get("data", {}).get("player", {}).get("manual", False)
player.nicked = True
player.websocket = True
await callback(player)
except Exception as e:
self.logger.error(f"An error occurred while receiving a WebSocket message!\n\nTraceback: {traceback.format_exc()} | {e}")
except Exception as e:
self.logger.error(f"An error occurred while creating a WebSocket connection!\n\nTraceback: {traceback.format_exc()} | {e}")

closed(expired)

self.logger.info(f"[WS] WebSocket connection closed!")


async def get_stats(self, player: str) -> Player:
"""
Expand Down Expand Up @@ -322,15 +440,19 @@ async def load_skin(self, player: Player) -> None:
if DEV_MODE:
self.logger.info(f"GET skins.mcstats.com/face/{player.uuid}")

headers = {
"User-Agent": __header__["User-Agent"]
}

async with ClientSession() as session:
async with session.get(f"https://skins.mcstats.com/face/{player.uuid}", headers=__header__) as response:
async with session.get(f"https://skins.mcstats.com/face/{player.uuid}", headers=headers) as response:
if response.status == 200:
return await response.read()
else:
self.logger.error(f"An error occurred while getting the skin of {player.uuid} ({response.status})!")
raise APIError
except ContentTypeError:
raise APIError
return None
except APIError:
return None
except Exception as e:
Expand Down
12 changes: 12 additions & 0 deletions src/PolsuAPI/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,15 @@ async def loadSkin(self, player: Pl) -> None:
return await self.client.load_skin(player)
except asyncio.TimeoutError:
raise APIError


async def WebSocket(self, setWebSocket, callback, closed) -> None:
"""
Get a Player stats with a WebSocket connection

:param setWebSocket: A setWebSocket function
:param callback: A callback function
:param closed: A closed function
:return: An instance of Pl, representing the Player stats
"""
await self.client.WebSocketConnection(setWebSocket, callback, closed)
35 changes: 35 additions & 0 deletions src/PolsuAPI/objects/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ def __init__(self, data: dict) -> None:
self._ping = Ping(data.get('ping'))
self._local = None

self._manual = False
self._websocket = False


@property
def data(self) -> dict:
Expand Down Expand Up @@ -643,6 +646,38 @@ def local(self, value: LocalBlacklisted) -> None:
"""
self._local = value


@property
def manual(self) -> bool:
"""
Whether the Player is manually requested or not
"""
return self._manual


@manual.setter
def manual(self, value: bool) -> None:
"""
Set whether the Player is manually requested or not
"""
self._manual = value


@property
def websocket(self) -> bool:
"""
Whether the Player was requested through the WebSocket or not
"""
return self._websocket


@websocket.setter
def websocket(self, value: bool) -> None:
"""
Set whether the Player was requested through the WebSocket or not
"""
self._websocket = value


def __repr__(self) -> str:
return f"<Player username={self.username} uuid={self.uuid} rank={self.rank} channel={self.channel} level={self.level} tag={self.tag} cached={self.cached} bedwars={self.bedwars}>"
Expand Down
4 changes: 3 additions & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
__title__ = "PolsuOverlay"
__author__ = "Polsulpicien"
__license__ = "GPL-3.0 License"
__version__ = "2.0.7"
__version__ = "2.0.8"
__description__ = "Polsu's Overlay"

__module__ = getcwd()
Expand All @@ -67,6 +67,8 @@
load_dotenv('.env')

DEV_MODE = True if environ.get("DEV_MODE", "False") == "True" else False
PLUGINS_DEV_MODE = True if environ.get("PLUGINS_DEV_MODE", "False") == "True" else False
USE_WEBSOCKET = True if environ.get("USE_WEBSOCKET", "False") == "True" else True


# This will only change the local cache, not the API cache
Expand Down
2 changes: 0 additions & 2 deletions src/components/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ def __init__(self):
handler.setFormatter(formatter)
self.logger.addHandler(handler)

self.logger.info('Logger Initialised\n\n')


def info(self, message: str) -> None:
"""
Expand Down
Loading