Skip to content

Commit

Permalink
Update docstrings + Update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucino772 committed May 9, 2021
1 parent 734f570 commit 5d618fb
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 46 deletions.
2 changes: 1 addition & 1 deletion mojang/account/auth/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def check_ip(access_token: str) -> bool:
else:
return True

def get_challenges(access_token: str) -> List[ChallengeInfo]:
def get_challenges(access_token: str) -> List['ChallengeInfo']:
"""Return a list of challenges to verify IP
Args:
Expand Down
36 changes: 32 additions & 4 deletions mojang/account/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,32 @@ def status() -> StatusCheck:
Example:
Get status for all services
```python
import mojang
service_status = mojang.status().get('minecraft.net')
print(service_status)
status = mojang.status()
print(status)
```
```bash
(
ServiceStatus(name='minecraft.net', status='green'),
ServiceStatus(name='session.minecraft.net', status='green'),
ServiceStatus(name='account.mojang.com', status='green'),
ServiceStatus(name='authserver.mojang.com', status='green'),
ServiceStatus(name='sessionserver.mojang.com', status='red'),
ServiceStatus(name='api.mojang.com', status='green'),
ServiceStatus(name='textures.minecraft.net', status='green'),
ServiceStatus(name='mojang.com', status='green')
)
```
Get status for one specific service
```python
import mojang
status = mojang.status().get('minecraft.net')
print(status)
```
```bash
ServiceStatus(name='minecraft.net', status='green')
Expand Down Expand Up @@ -61,11 +82,14 @@ def get_uuid(username: str) -> UUIDInfo:
response = requests.get(URLs.uuid(username))
data = handle_response(response)

if not data:
return None

data['uuid'] = data.pop('id')

return UUIDInfo(**data)

def get_uuids(usernames: list) -> List[UUIDInfo]:
def get_uuids(usernames: list) -> List['UUIDInfo']:
"""Get uuid of multiple username
Note: Limited Endpoint
Expand Down Expand Up @@ -143,7 +167,7 @@ def names(uuid: str) -> NameInfoList:
return NameInfoList(_names)

def user(uuid: str) -> UserProfile:
"""Get profile information by uuid
"""Returns the full profile of a user
Args:
uuid (str): The uuid of the profile
Expand Down Expand Up @@ -173,6 +197,10 @@ def user(uuid: str) -> UserProfile:
"""
response = requests.get(URLs.profile(uuid))
data = handle_response(response)

if not data:
return None

_dict = dict.fromkeys([f.name for f in fields(UserProfile) if f.init], None)

# Load profile info
Expand Down
1 change: 1 addition & 0 deletions mojang/account/structures/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass, field


## Authentication
@dataclass(frozen=True)
class AuthenticationInfo:
Expand Down
23 changes: 18 additions & 5 deletions mojang/account/structures/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime as dt
from dataclasses import dataclass, field
from typing import Tuple, Union
from typing import Iterator, Union

from .session import Cape, Skin

Expand All @@ -16,7 +16,7 @@ class ServiceStatus:
name: str = field()
status: str = field()

class StatusCheck(Tuple[ServiceStatus]):
class StatusCheck(tuple):

def get(self, name: str) -> Union[None, 'ServiceStatus']:
"""Get service by name
Expand All @@ -31,6 +31,12 @@ def get(self, name: str) -> Union[None, 'ServiceStatus']:
if len(service) > 0:
return service[0]

def __getitem__(self, x) -> ServiceStatus:
return super().__getitem__(x)

def __iter__(self) -> Iterator['ServiceStatus']:
return super().__iter__()

# UUID and Name
@dataclass(frozen=True)
class UUIDInfo:
Expand All @@ -56,10 +62,10 @@ class NameInfo:
name: str = field()
changed_to_at: dt.datetime = field()

class NameInfoList(Tuple[NameInfo]):
class NameInfoList(tuple):

@property
def current(self) -> 'NameInfo':
def current(self) -> NameInfo:
"""Returns the most recent name"""
if len(self) == 1:
return self[0]
Expand All @@ -68,11 +74,18 @@ def current(self) -> 'NameInfo':
return max(_list, key=lambda n: n.change_to_at)

@property
def first(self) -> 'NameInfo':
def first(self) -> NameInfo:
"""Returns the first name"""
first = list(filter(lambda n: n.changed_to_at == None, self))
return first[0]

def __getitem__(self, x) -> NameInfo:
return super().__getitem__(x)

def __iter__(self) -> Iterator['NameInfo']:
return super().__iter__()


## Profile
@dataclass
class UserProfile:
Expand Down
1 change: 1 addition & 0 deletions mojang/account/utils/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from requests.auth import AuthBase


class BearerAuth(AuthBase):

def __init__(self, token: str):
Expand Down
34 changes: 33 additions & 1 deletion mojang/minecraft/proto/query/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,39 @@ def _get_stats(sock: socket.socket, addr: Tuple[str, int], session_id: int, toke

return ServerStats(**info, player_list=players)

def get_stats(addr: Tuple[str, int], session_id: int = None, timeout: float = 3):
def get_stats(addr: Tuple[str, int], session_id: int = None, timeout: float = 3) -> ServerStats:
"""Returns full stats about server using the Query protocol
Args:
addr (tuple): tuple with the address and the port to connect to
session_id (int, optional): A session id used for the requests (default to None)
timeout (int, optional): Time to wait before closing pending connection (default to 3)
Returns:
ServerStats
Example:
```python
from mojang.minecraft import query
stats = query.get_stats(('localhost', 25585))
print(stats)
```
```bash
ServerStats(
motd='A Minecraft Server',
game_type='SMP',
game_id='MINECRAFT',
version='1.16.5',
map='world',
host=('localhost', 25585),
players=(0, 20),
player_list=[]
)
```
"""
session_id = get_session_id() if not session_id else session_id

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as conn:
Expand Down
24 changes: 22 additions & 2 deletions mojang/minecraft/proto/rcon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import socket
import struct
from contextlib import contextmanager
from typing import IO, Tuple
from typing import IO, Tuple, Callable, Any


def get_request_id():
Expand Down Expand Up @@ -31,7 +31,27 @@ def _write_packet(sock: socket.socket, packet_type: int, payload: str):


@contextmanager
def session(addr: Tuple[str, int], password: str, timeout: float = 3):
def session(addr: Tuple[str, int], password: str, timeout: float = 3) -> Callable[[str], Any]:
"""Open a RCON connection
Args:
addr (tuple): The address and the port to connect to
password (str): The RCON password set in the server properties
timeout (int, optional): Time to wait before closing pending connection (default to 3)
Returns:
A function to send command
Example:
```python
from mojang.minecraft import rcon
with rcon.session(('localhost', 25575), 'my_super_password') as send:
result = send('help') # This execute the /help command
```
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
sock.connect(addr)
Expand Down
31 changes: 30 additions & 1 deletion mojang/minecraft/proto/slp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
import socket
from typing import Tuple

from ._structures import SLPResponse
from .current import slp
from .old import slp_1_6, slp_prior_1_4, slp_prior_1_6


def ping(addr: Tuple[str, int], timeout: int = 3):
def ping(addr: Tuple[str, int], timeout: int = 3) -> SLPResponse:
"""Ping the server for information
Args:
addr (tuple): The address and the port to connect to
timeout (int, optional): Time to wait before closing pending connection (default to 3)
Returns:
SLPResponse
Example:
```python
from mojang.minecraft import slp
stats = slp.ping(('localhost', 25565))
print(stats)
```
```bash
SLPResponse(
protocol_version=754,
version='1.16.5',
motd='A Minecraft Server',
players=Players(count=(0, 20), list=[]),
ping=1
)
```
"""
response = None
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect(addr)
Expand Down
35 changes: 19 additions & 16 deletions tests/test_mojang.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
import unittest

import mojang
from mojang.account.structures.base import NameInfoList, UserProfile, UUIDInfo, NameInfo
from mojang.account.structures.session import Cape, Skin


class TestMojangAPI(unittest.TestCase):

def test_existent_uuid(self):
self.assertEqual(mojang.get_uuid('Notch'), '069a79f444e94726a5befca90e38aaf5')
self.assertEqual(mojang.get_uuid('jeb_'), '853c80ef3c3749fdaa49938b674adae6')
self.assertEqual(mojang.get_uuid('Notch').uuid, '069a79f444e94726a5befca90e38aaf5')
self.assertEqual(mojang.get_uuid('jeb_').uuid, '853c80ef3c3749fdaa49938b674adae6')

def test_unexistent_uuid(self):
self.assertEqual(mojang.get_uuid('UNEXISTENT_PLAYER'), None)

def test_existent_uuids(self):
self.assertEqual(mojang.get_uuids(['Notch','jeb_']), ['069a79f444e94726a5befca90e38aaf5','853c80ef3c3749fdaa49938b674adae6'])
self.assertEqual(mojang.get_uuids(['jeb_','Notch']), ['853c80ef3c3749fdaa49938b674adae6','069a79f444e94726a5befca90e38aaf5'])
self.assertEqual(mojang.get_uuids(['Notch','jeb_']), [UUIDInfo('Notch','069a79f444e94726a5befca90e38aaf5'), UUIDInfo('jeb_','853c80ef3c3749fdaa49938b674adae6')])
self.assertEqual(mojang.get_uuids(['jeb_','Notch']), [UUIDInfo('jeb_','853c80ef3c3749fdaa49938b674adae6'), UUIDInfo('Notch','069a79f444e94726a5befca90e38aaf5')])

def test_unexistent_uuids(self):
self.assertEqual(mojang.get_uuids(['jeb_','UNEXISTENT_PLAYER']), ['853c80ef3c3749fdaa49938b674adae6',None])
self.assertEqual(mojang.get_uuids(['jeb_','UNEXISTENT_PLAYER']), [UUIDInfo('jeb_','853c80ef3c3749fdaa49938b674adae6'),None])
self.assertEqual(mojang.get_uuids(['UNEXISTENT_PLAYER1','UNEXISTENT_PLAYER2']), [None,None])

def test_existent_name(self):
self.assertEqual(mojang.get_username('069a79f444e94726a5befca90e38aaf5'), 'Notch')
self.assertEqual(mojang.get_username('853c80ef3c3749fdaa49938b674adae6'), 'jeb_')

def test_unexistent_name(self):
self.assertEqual(mojang.get_username('069a79f444e94726a5befca90e38aaf6'), None)

def test_existent_names(self):
self.assertEqual(mojang.name_history('069a79f444e94726a5befca90e38aaf5'), [('Notch',None)])
self.assertEqual(mojang.name_history('853c80ef3c3749fdaa49938b674adae6'), [('jeb_', None)])
self.assertEqual(mojang.names('069a79f444e94726a5befca90e38aaf5'), NameInfoList([NameInfo('Notch', None)]))
self.assertEqual(mojang.names('853c80ef3c3749fdaa49938b674adae6'), NameInfoList([NameInfo('jeb_', None)]))

def test_unexistent_names(self):
self.assertEqual(mojang.name_history('069a79f444e94726a5befca90e38aaf6'), [])

self.assertEqual(mojang.names('069a79f444e94726a5befca90e38aaf6'), NameInfoList([]))

def test_existent_profile(self):
self.assertEqual(mojang.user('069a79f444e94726a5befca90e38aaf5'), UserProfile('Notch', '069a79f444e94726a5befca90e38aaf5', False, False, NameInfoList([NameInfo('Notch', None)]), Skin('http://textures.minecraft.net/texture/292009a4925b58f02c77dadc3ecef07ea4c7472f64e0fdc32ce5522489362680', 'classic'), None))
self.assertEqual(mojang.user('853c80ef3c3749fdaa49938b674adae6'), UserProfile('jeb_', '853c80ef3c3749fdaa49938b674adae6', False, False, NameInfoList([NameInfo('jeb_', None)]), Skin('http://textures.minecraft.net/texture/7fd9ba42a7c81eeea22f1524271ae85a8e045ce0af5a6ae16c6406ae917e68b5', 'classic'), Cape('http://textures.minecraft.net/texture/5786fe99be377dfb6858859f926c4dbc995751e91cee373468c5fbf4865e7151', None)))

def test_unexistent_profile(self):
self.assertEqual(mojang.user('069a79f444e94726a5befca90e38aaf6'), None)
16 changes: 0 additions & 16 deletions tests/test_user.py

This file was deleted.

0 comments on commit 5d618fb

Please sign in to comment.