mirror of
https://github.com/MelisaDev/melisa.git
synced 2024-09-22 19:22:01 +03:00
formated files with the black formatter
This commit is contained in:
parent
3d66eb79c5
commit
2b2f5a2d85
12 changed files with 152 additions and 199 deletions
|
@ -87,8 +87,10 @@ class Client:
|
||||||
"""
|
"""
|
||||||
inited_shard = Shard(self, 0, 1)
|
inited_shard = Shard(self, 0, 1)
|
||||||
|
|
||||||
asyncio.ensure_future(inited_shard.launch(activity=self._activity,
|
asyncio.ensure_future(
|
||||||
status=self._status), loop=self._loop)
|
inited_shard.launch(activity=self._activity, status=self._status),
|
||||||
|
loop=self._loop,
|
||||||
|
)
|
||||||
self._loop.run_forever()
|
self._loop.run_forever()
|
||||||
|
|
||||||
def run_shards(self, num_shards: int, *, shard_ids: List[int] = None):
|
def run_shards(self, num_shards: int, *, shard_ids: List[int] = None):
|
||||||
|
@ -108,8 +110,10 @@ class Client:
|
||||||
for shard_id in shard_ids:
|
for shard_id in shard_ids:
|
||||||
inited_shard = Shard(self, shard_id, num_shards)
|
inited_shard = Shard(self, shard_id, num_shards)
|
||||||
|
|
||||||
asyncio.ensure_future(inited_shard.launch(activity=self._activity,
|
asyncio.ensure_future(
|
||||||
status=self._status), loop=self._loop)
|
inited_shard.launch(activity=self._activity, status=self._status),
|
||||||
|
loop=self._loop,
|
||||||
|
)
|
||||||
self._loop.run_forever()
|
self._loop.run_forever()
|
||||||
|
|
||||||
def run_autosharded(self):
|
def run_autosharded(self):
|
||||||
|
@ -122,8 +126,10 @@ class Client:
|
||||||
for shard_id in shard_ids:
|
for shard_id in shard_ids:
|
||||||
inited_shard = Shard(self, shard_id, num_shards)
|
inited_shard = Shard(self, shard_id, num_shards)
|
||||||
|
|
||||||
asyncio.ensure_future(inited_shard.launch(activity=self._activity,
|
asyncio.ensure_future(
|
||||||
status=self._status), loop=self._loop)
|
inited_shard.launch(activity=self._activity, status=self._status),
|
||||||
|
loop=self._loop,
|
||||||
|
)
|
||||||
self._loop.run_forever()
|
self._loop.run_forever()
|
||||||
|
|
||||||
async def fetch_user(self, user_id: Union[Snowflake, str, int]):
|
async def fetch_user(self, user_id: Union[Snowflake, str, int]):
|
||||||
|
|
|
@ -20,6 +20,7 @@ from ..utils import APIModelBase, json
|
||||||
@dataclass
|
@dataclass
|
||||||
class GatewayBotInfo(APIModelBase):
|
class GatewayBotInfo(APIModelBase):
|
||||||
"""Gateway info from the `gateway/bot` endpoint"""
|
"""Gateway info from the `gateway/bot` endpoint"""
|
||||||
|
|
||||||
url: str
|
url: str
|
||||||
shards: int
|
shards: int
|
||||||
session_start_limit: dict
|
session_start_limit: dict
|
||||||
|
@ -39,11 +40,7 @@ class Gateway:
|
||||||
HELLO = 10
|
HELLO = 10
|
||||||
HEARTBEAT_ACK = 11
|
HEARTBEAT_ACK = 11
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, client, shard_id: int = 0, num_shards: int = 1, **kwargs):
|
||||||
client,
|
|
||||||
shard_id: int = 0,
|
|
||||||
num_shards: int = 1,
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
self.GATEWAY_VERSION = "9"
|
self.GATEWAY_VERSION = "9"
|
||||||
self.interval = None
|
self.interval = None
|
||||||
|
@ -52,7 +49,7 @@ class Gateway:
|
||||||
self.__session = aiohttp.ClientSession()
|
self.__session = aiohttp.ClientSession()
|
||||||
self.session_id = None
|
self.session_id = None
|
||||||
self.client = client
|
self.client = client
|
||||||
self.latency = float('inf')
|
self.latency = float("inf")
|
||||||
self.ws = None
|
self.ws = None
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.shard_id = shard_id
|
self.shard_id = shard_id
|
||||||
|
@ -63,7 +60,7 @@ class Gateway:
|
||||||
4011: GatewayError("Sharding required"),
|
4011: GatewayError("Sharding required"),
|
||||||
4012: GatewayError("Invalid API version"),
|
4012: GatewayError("Invalid API version"),
|
||||||
4013: GatewayError("Invalid intents"),
|
4013: GatewayError("Invalid intents"),
|
||||||
4014: PrivilegedIntentsRequired("Disallowed intents")
|
4014: PrivilegedIntentsRequired("Disallowed intents"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.listeners = listeners
|
self.listeners = listeners
|
||||||
|
@ -76,19 +73,21 @@ class Gateway:
|
||||||
"properties": {
|
"properties": {
|
||||||
"$os": sys.platform,
|
"$os": sys.platform,
|
||||||
"$browser": "Melisa Python Library",
|
"$browser": "Melisa Python Library",
|
||||||
"$device": "Melisa Python Library"
|
"$device": "Melisa Python Library",
|
||||||
},
|
},
|
||||||
"compress": True,
|
"compress": True,
|
||||||
"shard": [shard_id, num_shards],
|
"shard": [shard_id, num_shards],
|
||||||
"presence": self.generate_presence(kwargs.get("start_activity"),
|
"presence": self.generate_presence(
|
||||||
kwargs.get("start_status"))}
|
kwargs.get("start_activity"), kwargs.get("start_status")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
self._zlib: zlib._Decompress = zlib.decompressobj()
|
self._zlib: zlib._Decompress = zlib.decompressobj()
|
||||||
self._buffer: bytearray = bytearray()
|
self._buffer: bytearray = bytearray()
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self) -> None:
|
||||||
self.ws = await self.__session.ws_connect(
|
self.ws = await self.__session.ws_connect(
|
||||||
f'wss://gateway.discord.gg/?v={self.GATEWAY_VERSION}&encoding=json&compress=zlib-stream'
|
f"wss://gateway.discord.gg/?v={self.GATEWAY_VERSION}&encoding=json&compress=zlib-stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.session_id is None:
|
if self.session_id is None:
|
||||||
|
@ -114,10 +113,10 @@ class Gateway:
|
||||||
if type(msg) is bytes:
|
if type(msg) is bytes:
|
||||||
self._buffer.extend(msg)
|
self._buffer.extend(msg)
|
||||||
|
|
||||||
if len(msg) < 4 or msg[-4:] != b'\x00\x00\xff\xff':
|
if len(msg) < 4 or msg[-4:] != b"\x00\x00\xff\xff":
|
||||||
return None
|
return None
|
||||||
msg = self._zlib.decompress(self._buffer)
|
msg = self._zlib.decompress(self._buffer)
|
||||||
msg = msg.decode('utf-8')
|
msg = msg.decode("utf-8")
|
||||||
self._buffer = bytearray()
|
self._buffer = bytearray()
|
||||||
|
|
||||||
return json.loads(msg)
|
return json.loads(msg)
|
||||||
|
@ -125,7 +124,7 @@ class Gateway:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def handle_data(self, data):
|
async def handle_data(self, data):
|
||||||
if data['op'] == self.DISPATCH:
|
if data["op"] == self.DISPATCH:
|
||||||
self.sequence = int(data["s"])
|
self.sequence = int(data["s"])
|
||||||
event_type = data["t"].lower()
|
event_type = data["t"].lower()
|
||||||
|
|
||||||
|
@ -134,12 +133,12 @@ class Gateway:
|
||||||
if event_to_call is not None:
|
if event_to_call is not None:
|
||||||
ensure_future(event_to_call(self.client, self, data["d"]))
|
ensure_future(event_to_call(self.client, self, data["d"]))
|
||||||
|
|
||||||
elif data['op'] == self.INVALID_SESSION:
|
elif data["op"] == self.INVALID_SESSION:
|
||||||
await self.ws.close(code=4000)
|
await self.ws.close(code=4000)
|
||||||
await self.handle_close(4000)
|
await self.handle_close(4000)
|
||||||
elif data['op'] == self.HELLO:
|
elif data["op"] == self.HELLO:
|
||||||
await self.send_hello(data)
|
await self.send_hello(data)
|
||||||
elif data['op'] == self.HEARTBEAT_ACK:
|
elif data["op"] == self.HEARTBEAT_ACK:
|
||||||
self.latency = time.perf_counter() - self._last_send
|
self.latency = time.perf_counter() - self._last_send
|
||||||
|
|
||||||
async def receive(self) -> None:
|
async def receive(self) -> None:
|
||||||
|
@ -184,34 +183,28 @@ class Gateway:
|
||||||
self._buffer.clear()
|
self._buffer.clear()
|
||||||
|
|
||||||
async def send_hello(self, data: Dict) -> None:
|
async def send_hello(self, data: Dict) -> None:
|
||||||
interval = data['d']['heartbeat_interval'] / 1000
|
interval = data["d"]["heartbeat_interval"] / 1000
|
||||||
await asyncio.sleep((interval - 2000) / 1000)
|
await asyncio.sleep((interval - 2000) / 1000)
|
||||||
self.loop.create_task(self.send_heartbeat(interval))
|
self.loop.create_task(self.send_heartbeat(interval))
|
||||||
|
|
||||||
async def send_identify(self) -> None:
|
async def send_identify(self) -> None:
|
||||||
await self.send(self.opcode(
|
await self.send(self.opcode(self.IDENTIFY, self.auth))
|
||||||
self.IDENTIFY,
|
|
||||||
self.auth
|
|
||||||
))
|
|
||||||
|
|
||||||
async def resume(self) -> None:
|
async def resume(self) -> None:
|
||||||
await self.send(
|
await self.send(
|
||||||
self.opcode(
|
self.opcode(
|
||||||
self.RESUME,
|
self.RESUME,
|
||||||
{
|
{
|
||||||
'token': self.client._token,
|
"token": self.client._token,
|
||||||
'session_id': self.session_id,
|
"session_id": self.session_id,
|
||||||
'seq': self.sequence,
|
"seq": self.sequence,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_presence(activity: BotActivity = None, status: str = None):
|
def generate_presence(activity: BotActivity = None, status: str = None):
|
||||||
data = {
|
data = {"since": time.time() * 1000, "afk": False}
|
||||||
"since": time.time() * 1000,
|
|
||||||
"afk": False
|
|
||||||
}
|
|
||||||
|
|
||||||
if activity is not None:
|
if activity is not None:
|
||||||
data["activities"] = activity.to_dict()
|
data["activities"] = activity.to_dict()
|
||||||
|
@ -226,8 +219,5 @@ class Gateway:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def opcode(opcode: int, payload) -> str:
|
def opcode(opcode: int, payload) -> str:
|
||||||
data = {
|
data = {"op": opcode, "d": payload}
|
||||||
"op": opcode,
|
|
||||||
"d": payload
|
|
||||||
}
|
|
||||||
return json.dumps(data)
|
return json.dumps(data)
|
||||||
|
|
|
@ -8,7 +8,8 @@ from typing import Dict, Optional
|
||||||
|
|
||||||
from aiohttp import ClientSession, ClientResponse
|
from aiohttp import ClientSession, ClientResponse
|
||||||
|
|
||||||
from melisa.exceptions import (NotModifiedError,
|
from melisa.exceptions import (
|
||||||
|
NotModifiedError,
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
ForbiddenError,
|
ForbiddenError,
|
||||||
UnauthorizedError,
|
UnauthorizedError,
|
||||||
|
@ -16,7 +17,8 @@ from melisa.exceptions import (NotModifiedError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
MethodNotAllowedError,
|
MethodNotAllowedError,
|
||||||
ServerError,
|
ServerError,
|
||||||
RateLimitError)
|
RateLimitError,
|
||||||
|
)
|
||||||
from .ratelimiter import RateLimiter
|
from .ratelimiter import RateLimiter
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +33,7 @@ class HTTPClient:
|
||||||
headers: Dict[str, str] = {
|
headers: Dict[str, str] = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": f"Bot {token}",
|
"Authorization": f"Bot {token}",
|
||||||
"User-Agent": "Melisa Python Library"
|
"User-Agent": "Melisa Python Library",
|
||||||
}
|
}
|
||||||
|
|
||||||
self.__http_exceptions: Dict[int, HTTPException] = {
|
self.__http_exceptions: Dict[int, HTTPException] = {
|
||||||
|
@ -41,7 +43,7 @@ class HTTPClient:
|
||||||
403: ForbiddenError(),
|
403: ForbiddenError(),
|
||||||
404: NotFoundError(),
|
404: NotFoundError(),
|
||||||
405: MethodNotAllowedError(),
|
405: MethodNotAllowedError(),
|
||||||
429: RateLimitError()
|
429: RateLimitError(),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.__aiohttp_session: ClientSession = ClientSession(headers=headers)
|
self.__aiohttp_session: ClientSession = ClientSession(headers=headers)
|
||||||
|
@ -57,12 +59,7 @@ class HTTPClient:
|
||||||
await self.__aiohttp_session.close()
|
await self.__aiohttp_session.close()
|
||||||
|
|
||||||
async def __send(
|
async def __send(
|
||||||
self,
|
self, method: str, endpoint: str, *, _ttl: int = None, **kwargs
|
||||||
method: str,
|
|
||||||
endpoint: str,
|
|
||||||
*,
|
|
||||||
_ttl: int = None,
|
|
||||||
**kwargs
|
|
||||||
) -> Optional[Dict]:
|
) -> Optional[Dict]:
|
||||||
"""Send an API request to the Discord API."""
|
"""Send an API request to the Discord API."""
|
||||||
|
|
||||||
|
@ -71,15 +68,14 @@ class HTTPClient:
|
||||||
if ttl == 0:
|
if ttl == 0:
|
||||||
raise ServerError(f"Maximum amount of retries for `{endpoint}`.")
|
raise ServerError(f"Maximum amount of retries for `{endpoint}`.")
|
||||||
|
|
||||||
await self.__rate_limiter.wait_until_not_ratelimited(
|
await self.__rate_limiter.wait_until_not_ratelimited(endpoint, method)
|
||||||
endpoint,
|
|
||||||
method
|
|
||||||
)
|
|
||||||
|
|
||||||
url = f"{self.url}/{endpoint}"
|
url = f"{self.url}/{endpoint}"
|
||||||
|
|
||||||
async with self.__aiohttp_session.request(method, url, **kwargs) as response:
|
async with self.__aiohttp_session.request(method, url, **kwargs) as response:
|
||||||
return await self.__handle_response(response, method, endpoint, _ttl=ttl, **kwargs)
|
return await self.__handle_response(
|
||||||
|
response, method, endpoint, _ttl=ttl, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
async def __handle_response(
|
async def __handle_response(
|
||||||
self,
|
self,
|
||||||
|
@ -88,13 +84,11 @@ class HTTPClient:
|
||||||
endpoint: str,
|
endpoint: str,
|
||||||
*,
|
*,
|
||||||
_ttl: int = None,
|
_ttl: int = None,
|
||||||
**kwargs
|
**kwargs,
|
||||||
) -> Optional[Dict]:
|
) -> Optional[Dict]:
|
||||||
"""Handle responses from the Discord API."""
|
"""Handle responses from the Discord API."""
|
||||||
|
|
||||||
self.__rate_limiter.save_response_bucket(
|
self.__rate_limiter.save_response_bucket(endpoint, method, res.headers)
|
||||||
endpoint, method, res.headers
|
|
||||||
)
|
|
||||||
|
|
||||||
if res.ok:
|
if res.ok:
|
||||||
return await res.json()
|
return await res.json()
|
||||||
|
@ -106,11 +100,7 @@ class HTTPClient:
|
||||||
timeout = (await res.json()).get("retry_after", 40)
|
timeout = (await res.json()).get("retry_after", 40)
|
||||||
|
|
||||||
await asyncio.sleep(timeout)
|
await asyncio.sleep(timeout)
|
||||||
return await self.__send(
|
return await self.__send(method, endpoint, **kwargs)
|
||||||
method,
|
|
||||||
endpoint,
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
exception.__init__(res.reason)
|
exception.__init__(res.reason)
|
||||||
raise exception
|
raise exception
|
||||||
|
@ -119,18 +109,9 @@ class HTTPClient:
|
||||||
|
|
||||||
await asyncio.sleep(retry_in)
|
await asyncio.sleep(retry_in)
|
||||||
|
|
||||||
return await self.__send(
|
return await self.__send(method, endpoint, _ttl=_ttl - 1, **kwargs)
|
||||||
method,
|
|
||||||
endpoint,
|
|
||||||
_ttl=_ttl - 1,
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
async def get(
|
async def get(self, route: str, params: Optional[Dict] = None) -> Optional[Dict]:
|
||||||
self,
|
|
||||||
route: str,
|
|
||||||
params: Optional[Dict] = None
|
|
||||||
) -> Optional[Dict]:
|
|
||||||
"""|coro|
|
"""|coro|
|
||||||
Sends a GET request to a Discord REST API endpoint.
|
Sends a GET request to a Discord REST API endpoint.
|
||||||
|
|
||||||
|
@ -146,17 +127,9 @@ class HTTPClient:
|
||||||
Optional[:class:`Dict`]
|
Optional[:class:`Dict`]
|
||||||
The response from Discord.
|
The response from Discord.
|
||||||
"""
|
"""
|
||||||
return await self.__send(
|
return await self.__send("GET", route, params=params)
|
||||||
"GET",
|
|
||||||
route,
|
|
||||||
params=params
|
|
||||||
)
|
|
||||||
|
|
||||||
async def post(
|
async def post(self, route: str, data: Optional[Dict] = None) -> Optional[Dict]:
|
||||||
self,
|
|
||||||
route: str,
|
|
||||||
data: Optional[Dict] = None
|
|
||||||
) -> Optional[Dict]:
|
|
||||||
"""|coro|
|
"""|coro|
|
||||||
Sends a POST request to a Discord REST API endpoint.
|
Sends a POST request to a Discord REST API endpoint.
|
||||||
|
|
||||||
|
@ -178,11 +151,7 @@ class HTTPClient:
|
||||||
json=data,
|
json=data,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def delete(
|
async def delete(self, route: str, headers: dict = None) -> Optional[Dict]:
|
||||||
self,
|
|
||||||
route: str,
|
|
||||||
headers: dict = None
|
|
||||||
) -> Optional[Dict]:
|
|
||||||
"""|coro|
|
"""|coro|
|
||||||
Sends a DELETE request to a Discord REST API endpoint.
|
Sends a DELETE request to a Discord REST API endpoint.
|
||||||
|
|
||||||
|
@ -198,8 +167,4 @@ class HTTPClient:
|
||||||
Optional[:class:`Dict`]
|
Optional[:class:`Dict`]
|
||||||
JSON response from the Discord API.
|
JSON response from the Discord API.
|
||||||
"""
|
"""
|
||||||
return await self.__send(
|
return await self.__send("DELETE", route, headers=headers)
|
||||||
"DELETE",
|
|
||||||
route,
|
|
||||||
headers=headers
|
|
||||||
)
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from typing import Dict, Tuple, Any
|
||||||
@dataclass
|
@dataclass
|
||||||
class RateLimitBucket:
|
class RateLimitBucket:
|
||||||
"""Represents a rate limit bucket"""
|
"""Represents a rate limit bucket"""
|
||||||
|
|
||||||
limit: int
|
limit: int
|
||||||
remaining: int
|
remaining: int
|
||||||
reset: float
|
reset: float
|
||||||
|
@ -23,15 +24,12 @@ class RateLimiter:
|
||||||
"""Prevents ``user`` rate limits"""
|
"""Prevents ``user`` rate limits"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.bucket_map: Dict[Tuple[str, str], str] = {} # Dict[Tuple[endpoint, method], bucket_id]
|
self.bucket_map: Dict[
|
||||||
|
Tuple[str, str], str
|
||||||
|
] = {} # Dict[Tuple[endpoint, method], bucket_id]
|
||||||
self.buckets: Dict[str, RateLimitBucket] = {}
|
self.buckets: Dict[str, RateLimitBucket] = {}
|
||||||
|
|
||||||
def save_response_bucket(
|
def save_response_bucket(self, endpoint: str, method: str, header: Any):
|
||||||
self,
|
|
||||||
endpoint: str,
|
|
||||||
method: str,
|
|
||||||
header: Any
|
|
||||||
):
|
|
||||||
ratelimit_bucket_id = header.get("X-RateLimit-Bucket")
|
ratelimit_bucket_id = header.get("X-RateLimit-Bucket")
|
||||||
|
|
||||||
if not ratelimit_bucket_id:
|
if not ratelimit_bucket_id:
|
||||||
|
@ -44,14 +42,10 @@ class RateLimiter:
|
||||||
remaining=int(header["X-RateLimit-Remaining"]),
|
remaining=int(header["X-RateLimit-Remaining"]),
|
||||||
reset=float(header["X-RateLimit-Reset"]),
|
reset=float(header["X-RateLimit-Reset"]),
|
||||||
reset_after_timestamp=float(header["X-RateLimit-Reset-After"]),
|
reset_after_timestamp=float(header["X-RateLimit-Reset-After"]),
|
||||||
since_timestamp=time()
|
since_timestamp=time(),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def wait_until_not_ratelimited(
|
async def wait_until_not_ratelimited(self, endpoint: str, method: str):
|
||||||
self,
|
|
||||||
endpoint: str,
|
|
||||||
method: str
|
|
||||||
):
|
|
||||||
bucket_id = self.bucket_map.get((endpoint, method))
|
bucket_id = self.bucket_map.get((endpoint, method))
|
||||||
|
|
||||||
if not bucket_id:
|
if not bucket_id:
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
# Copyright MelisaDev 2022 - Present
|
# Copyright MelisaDev 2022 - Present
|
||||||
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||||
|
|
||||||
|
|
||||||
class MelisaException(Exception):
|
class MelisaException(Exception):
|
||||||
"""Base exception"""
|
"""Base exception"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ClientException(MelisaException):
|
class ClientException(MelisaException):
|
||||||
"""Handling user errors"""
|
"""Handling user errors"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LoginFailure(ClientException):
|
class LoginFailure(ClientException):
|
||||||
"""Fails to log you in from improper credentials or some other misc."""
|
"""Fails to log you in from improper credentials or some other misc."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,9 +42,11 @@ class PrivilegedIntentsRequired(ClientException):
|
||||||
|
|
||||||
def __init__(self, shard_id):
|
def __init__(self, shard_id):
|
||||||
self.shard_id = shard_id
|
self.shard_id = shard_id
|
||||||
message = "Shard ID {} is requesting privileged intents " \
|
message = (
|
||||||
"that have not been explicitly enabled in the " \
|
"Shard ID {} is requesting privileged intents "
|
||||||
|
"that have not been explicitly enabled in the "
|
||||||
"developer portal. Please visit to https://discord.com/developers/applications/ "
|
"developer portal. Please visit to https://discord.com/developers/applications/ "
|
||||||
|
)
|
||||||
|
|
||||||
super().__init__(message.format(self.shard_id))
|
super().__init__(message.format(self.shard_id))
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ from ..user import BotActivity
|
||||||
|
|
||||||
|
|
||||||
class Shard:
|
class Shard:
|
||||||
def __init__(self,
|
def __init__(self, client, shard_id: int, num_shards: int):
|
||||||
client,
|
|
||||||
shard_id: int,
|
|
||||||
num_shards: int):
|
|
||||||
self._client = client
|
self._client = client
|
||||||
|
|
||||||
self._shard_id: int = shard_id
|
self._shard_id: int = shard_id
|
||||||
|
@ -38,11 +35,13 @@ class Shard:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Launches new shard"""
|
Launches new shard"""
|
||||||
self._gateway = Gateway(self._client,
|
self._gateway = Gateway(
|
||||||
|
self._client,
|
||||||
self._shard_id,
|
self._shard_id,
|
||||||
self._num_shards,
|
self._num_shards,
|
||||||
start_activity=kwargs.get("activity"),
|
start_activity=kwargs.get("activity"),
|
||||||
start_status=kwargs.get("status"))
|
start_status=kwargs.get("status"),
|
||||||
|
)
|
||||||
|
|
||||||
self._client.shards[self._shard_id] = self
|
self._client.shards[self._shard_id] = self
|
||||||
|
|
||||||
|
@ -58,7 +57,9 @@ class Shard:
|
||||||
"""
|
"""
|
||||||
create_task(self._gateway.close())
|
create_task(self._gateway.close())
|
||||||
|
|
||||||
async def update_presence(self, activity: BotActivity = None, status: str = None) -> Shard:
|
async def update_presence(
|
||||||
|
self, activity: BotActivity = None, status: str = None
|
||||||
|
) -> Shard:
|
||||||
"""
|
"""
|
||||||
|coro|
|
|coro|
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ class ActivityType(IntEnum):
|
||||||
COMPETING:
|
COMPETING:
|
||||||
Competing in {name} (Competing in Arena World Champions)
|
Competing in {name} (Competing in Arena World Champions)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
GAME = 0
|
GAME = 0
|
||||||
STREAMING = 1
|
STREAMING = 1
|
||||||
LISTENING = 2
|
LISTENING = 2
|
||||||
|
@ -61,6 +62,7 @@ class ActivityTimestamp(BasePresence, APIModelBase):
|
||||||
end: Optional[:class:`int`]
|
end: Optional[:class:`int`]
|
||||||
Unix time (in milliseconds) of when the activity ends
|
Unix time (in milliseconds) of when the activity ends
|
||||||
"""
|
"""
|
||||||
|
|
||||||
start: APINullable[int] = None
|
start: APINullable[int] = None
|
||||||
end: APINullable[int] = None
|
end: APINullable[int] = None
|
||||||
|
|
||||||
|
@ -78,6 +80,7 @@ class ActivityEmoji(BasePresence, APIModelBase):
|
||||||
animated: Optional[:class:`bool`]
|
animated: Optional[:class:`bool`]
|
||||||
Whether this emoji is animated
|
Whether this emoji is animated
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = None
|
||||||
animated: APINullable[bool] = None
|
animated: APINullable[bool] = None
|
||||||
|
@ -94,6 +97,7 @@ class ActivityParty(BasePresence, APIModelBase):
|
||||||
size: Optional[Tuple[:class:`int`, :class:`int`]]
|
size: Optional[Tuple[:class:`int`, :class:`int`]]
|
||||||
Array of two integers (current_size, max_size)
|
Array of two integers (current_size, max_size)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[str] = None
|
id: APINullable[str] = None
|
||||||
size: APINullable[Tuple[int, int]] = None
|
size: APINullable[Tuple[int, int]] = None
|
||||||
|
|
||||||
|
@ -115,6 +119,7 @@ class ActivityAssets(BasePresence, APIModelBase):
|
||||||
small_text: Optional[:class:`str`]
|
small_text: Optional[:class:`str`]
|
||||||
text displayed when hovering over the small image of the activity
|
text displayed when hovering over the small image of the activity
|
||||||
"""
|
"""
|
||||||
|
|
||||||
large_image: APINullable[str] = None
|
large_image: APINullable[str] = None
|
||||||
large_text: APINullable[str] = None
|
large_text: APINullable[str] = None
|
||||||
small_image: APINullable[str] = None
|
small_image: APINullable[str] = None
|
||||||
|
@ -134,6 +139,7 @@ class ActivitySecrets(BasePresence, APIModelBase):
|
||||||
match: Optional[:class:`str`]
|
match: Optional[:class:`str`]
|
||||||
The secret for a specific instanced match
|
The secret for a specific instanced match
|
||||||
"""
|
"""
|
||||||
|
|
||||||
join: APINullable[str] = None
|
join: APINullable[str] = None
|
||||||
spectate: APINullable[str] = None
|
spectate: APINullable[str] = None
|
||||||
match_: APINullable[str] = None
|
match_: APINullable[str] = None
|
||||||
|
@ -172,6 +178,7 @@ class ActivityButton(BasePresence, APIModelBase):
|
||||||
url: :class:`str`
|
url: :class:`str`
|
||||||
The url opened when clicking the button (1-512 characters)
|
The url opened when clicking the button (1-512 characters)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
label: str
|
label: str
|
||||||
url: str
|
url: str
|
||||||
|
|
||||||
|
@ -251,11 +258,11 @@ class BotActivity(BasePresence, APIModelBase):
|
||||||
|
|
||||||
|
|
||||||
class StatusType(Enum):
|
class StatusType(Enum):
|
||||||
ONLINE = 'online'
|
ONLINE = "online"
|
||||||
OFFLINE = 'offline'
|
OFFLINE = "offline"
|
||||||
IDLE = 'idle'
|
IDLE = "idle"
|
||||||
DND = 'dnd'
|
DND = "dnd"
|
||||||
INVISIBLE = 'invisible'
|
INVISIBLE = "invisible"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
|
@ -169,20 +169,12 @@ class User(APIModelBase):
|
||||||
"""APINullable[:class:`~melisa.models.user.user.PremiumTypes`]: The
|
"""APINullable[:class:`~melisa.models.user.user.PremiumTypes`]: The
|
||||||
user their premium type in a usable enum.
|
user their premium type in a usable enum.
|
||||||
"""
|
"""
|
||||||
return (
|
return None if self.premium_type is None else PremiumTypes(self.premium_type)
|
||||||
None
|
|
||||||
if self.premium_type is None
|
|
||||||
else PremiumTypes(self.premium_type)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def flags(self) -> Optional[UserFlags]:
|
def flags(self) -> Optional[UserFlags]:
|
||||||
"""Flags of user"""
|
"""Flags of user"""
|
||||||
return(
|
return None if self.flags is None else UserFlags(self.flags)
|
||||||
None
|
|
||||||
if self.flags is None
|
|
||||||
else UserFlags(self.flags)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String representation of the User object"""
|
"""String representation of the User object"""
|
||||||
|
@ -195,10 +187,13 @@ class User(APIModelBase):
|
||||||
|
|
||||||
def avatar_url(self) -> str:
|
def avatar_url(self) -> str:
|
||||||
"""Avatar url (from the Discord CDN server)"""
|
"""Avatar url (from the Discord CDN server)"""
|
||||||
return "https://cdn.discordapp.com/avatars/{}/{}.png?size=1024".format(self.id, self.avatar)
|
return "https://cdn.discordapp.com/avatars/{}/{}.png?size=1024".format(
|
||||||
|
self.id, self.avatar
|
||||||
|
)
|
||||||
|
|
||||||
async def create_dm_channel(self):
|
async def create_dm_channel(self):
|
||||||
# ToDo: Add docstrings
|
# ToDo: Add docstrings
|
||||||
# ToDo: Add checking this channel in cache
|
# ToDo: Add checking this channel in cache
|
||||||
return await self._http.post(
|
return await self._http.post(
|
||||||
"/users/@me/channels", data={"recipient_id": self.id})
|
"/users/@me/channels", data={"recipient_id": self.id}
|
||||||
|
)
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
# Copyright MelisaDev 2022 - Present
|
# Copyright MelisaDev 2022 - Present
|
||||||
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||||
|
|
||||||
from .types import (
|
from .types import Coro
|
||||||
Coro
|
|
||||||
)
|
|
||||||
|
|
||||||
from .snowflake import Snowflake
|
from .snowflake import Snowflake
|
||||||
|
|
||||||
|
|
||||||
from .api_model import APIModelBase
|
from .api_model import APIModelBase
|
||||||
|
|
||||||
__all__ = (
|
__all__ = ("Coro", "Snowflake", "APIModelBase")
|
||||||
"Coro",
|
|
||||||
"Snowflake",
|
|
||||||
"APIModelBase"
|
|
||||||
)
|
|
||||||
|
|
|
@ -66,9 +66,7 @@ class APIModelBase:
|
||||||
cls._client = client
|
cls._client = client
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(
|
def from_dict(cls: Generic[T], data: Dict[str, Union[str, bool, int, Any]]) -> T:
|
||||||
cls: Generic[T], data: Dict[str, Union[str, bool, int, Any]]
|
|
||||||
) -> T:
|
|
||||||
"""
|
"""
|
||||||
Parse an API object from a dictionary.
|
Parse an API object from a dictionary.
|
||||||
"""
|
"""
|
||||||
|
@ -81,13 +79,10 @@ class APIModelBase:
|
||||||
map(
|
map(
|
||||||
lambda key: (
|
lambda key: (
|
||||||
key,
|
key,
|
||||||
data[key].value
|
data[key].value if isinstance(data[key], Enum) else data[key],
|
||||||
if isinstance(data[key], Enum)
|
|
||||||
else data[key],
|
|
||||||
),
|
),
|
||||||
filter(
|
filter(
|
||||||
lambda object_argument: data.get(object_argument)
|
lambda object_argument: data.get(object_argument) is not None,
|
||||||
is not None,
|
|
||||||
getfullargspec(cls.__init__).args,
|
getfullargspec(cls.__init__).args,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,12 +15,14 @@ else:
|
||||||
HAS_ORJSON = True
|
HAS_ORJSON = True
|
||||||
|
|
||||||
if HAS_ORJSON:
|
if HAS_ORJSON:
|
||||||
|
|
||||||
def dumps(obj: Any) -> str:
|
def dumps(obj: Any) -> str:
|
||||||
return orjson.dumps(obj).decode('utf-8')
|
return orjson.dumps(obj).decode("utf-8")
|
||||||
|
|
||||||
loads = orjson.loads
|
loads = orjson.loads
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def dumps(obj: Any) -> str:
|
def dumps(obj: Any) -> str:
|
||||||
return json.dumps(obj, separators=(',', ':'), ensure_ascii=True)
|
return json.dumps(obj, separators=(",", ":"), ensure_ascii=True)
|
||||||
|
|
||||||
loads = json.loads
|
loads = json.loads
|
||||||
|
|
|
@ -24,9 +24,7 @@ class Snowflake(int):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
if self < self._MIN_VALUE:
|
if self < self._MIN_VALUE:
|
||||||
raise ValueError(
|
raise ValueError("snowflake value should be greater than or equal to 0.")
|
||||||
"snowflake value should be greater than or equal to 0."
|
|
||||||
)
|
|
||||||
|
|
||||||
if self > self._MAX_VALUE:
|
if self > self._MAX_VALUE:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
Loading…
Reference in a new issue