async def decode_tracks(self, tracks): """ Decodes a list of base64-encoded track strings. Parameters ---------- tracks : `list` of `str` A list of base64-encoded track strings. Returns ------- tracks : `list` of ``Track`` The decoded tracks. Raises ------ RuntimeError - If there are no available nodes. - If the ``SolarClient``'s client is already deconstructed. SolarAuthenticationError Authentication failed towards the node. """ client = self._client_reference() if client is None: raise RuntimeError( f'`{self.__class__.__name__}` client is deconstructed.') available_nodes = self.available_nodes if not available_nodes: raise RuntimeError('No available nodes!') node = choice(available_nodes) async with client.http.post( f'http://{node._host}:{node._port}/decodetracks', headers={ AUTHORIZATION: node._password, CONTENT_TYPE: 'application/json', }, data=to_json(tracks), ) as response: if response.status == 200: track_datas = await response.json() elif response.status in (401, 403): raise SolarAuthenticationError(node, response) else: track_datas = None if (track_datas is None) or (not track_datas): tracks = [] else: tracks = [Track(track_data) for track_data in track_datas] return tracks
async def routeplanner_free_address(self, node, address): """ Removes a single address from the addresses which are currently marked as failing. This method is a coroutine. Parameters ---------- node : ``SolarNode`` The node to use for the query. address : `str` The address to free. Returns ------- success : `bool` Whether the address is freed up. Raises ------ SolarAuthenticationError Authentication failed towards the node. RuntimeError If the ``SolarClient``'s client is already deconstructed. """ client = self._client_reference() if client is None: raise RuntimeError( f'`{self.__class__.__name__}` client is deconstructed.') async with client.http.post( f'http://{node._host}:{node._port}/routeplanner/free/address', headers={ AUTHORIZATION: node._password, CONTENT_TYPE: 'application/json', }, data=to_json({'address': address}), ) as response: status = response.status if status in (401, 403): raise SolarAuthenticationError(node, response) if status == 204: # Success return True if status == 500: # The node has no routeplanner configured. return False # Unexpected case. return False
async def _send(self, data): """ Sends the passed data to the node via the websocket connection. Parameters ---------- data : `dict` The dict to send to Lavalink. """ data = to_json(data) websocket = self.websocket if (websocket is not None): await websocket.send(data)
def _save(self): """ Saves the settings to it's respective path. """ data = {} bot_directories = self.bot_directories if (bot_directories is not None): data[KEY_BOT_DIRECTORIES] = bot_directories raw_data = to_json(data) with open(join_paths(self.directory_path, SETTINGS_FILE_NAME), 'w') as file: file.write(raw_data)
async def send_as_json(self, data): """ Sends the data as json to Discord on the gateway's ``.websocket``. If there is no websocket, or the websocket is closed will not raise. This method is a coroutine. Parameters ---------- data : `dict` of (`str`, `Any`) items or `list` of `Any` """ websocket = self.websocket if websocket is None: return if await self.rate_limit_handler: return try: await websocket.send(to_json(data)) except ConnectionClosed: pass
async def send_as_json(self, data): """ Sends the data as json to Discord on the gateway's ``.websocket``. This method is a coroutine. Parameters ---------- data : `dict` of (`str`, `Any`) items or `list` of `Any` """ data = to_json(data) tasks = [] for gateway in self.gateways: task = Task(self._send_json(gateway, data), KOKORO) tasks.append(task) done, pending = await WaitTillExc(tasks, KOKORO) for task in pending: task.cancel() for task in done: task.result()
async def _request(self, method, url, rate_limit_handler, data=None, query_parameters=None): """ Does a request towards top.gg API. This method is a coroutine. Parameters ---------- method : `str` Http method. url : `str` Endpoint to do request towards. rate_limit_handler : ``RateLimitHandlerBase` Rate limit handle to handle rate limit as. data : `None`, `Any` = `None`, Optional Json serializable data. query_parameters : `None`, `Any` = `None`, Optional Query parameters. Raises ------ ConnectionError No internet connection. TopGGGloballyRateLimited If the client got globally rate limited by top.gg and `raise_on_top_gg_global_rate_limit` was given as `True`. TopGGHttpException Any exception raised by top.gg api. """ headers = self._headers.copy() if (data is not None): headers[CONTENT_TYPE] = 'application/json' data = to_json(data) try_again = 2 while try_again > 0: global_rate_limit_expires_at = self._global_rate_limit_expires_at if global_rate_limit_expires_at > LOOP_TIME(): if self._raise_on_top_gg_global_rate_limit: raise TopGGGloballyRateLimited(None) future = Future(KOKORO) KOKORO.call_at(global_rate_limit_expires_at, Future.set_result_if_pending, future, None) await future async with rate_limit_handler.ctx(): try: async with RequestContextManager( self.http._request(method, url, headers, data, query_parameters)) as response: response_data = await response.text(encoding='utf-8') except OSError as err: if not try_again: raise ConnectionError( 'Invalid address or no connection with Top.gg.' ) from err await sleep(0.5 / try_again, KOKORO) try_again -= 1 continue response_headers = response.headers status = response.status content_type_headers = response_headers.get(CONTENT_TYPE, None) if (content_type_headers is not None ) and content_type_headers.startswith('application/json'): response_data = from_json(response_data) if 199 < status < 305: return response_data # Are we rate limited? if status == 429: try: retry_after = headers[RETRY_AFTER] except KeyError: retry_after = RATE_LIMIT_GLOBAL_DEFAULT_DURATION else: try: retry_after = float(retry_after) except ValueError: retry_after = RATE_LIMIT_GLOBAL_DEFAULT_DURATION self._global_rate_limit_expires_at = LOOP_TIME( ) + retry_after if self._raise_on_top_gg_global_rate_limit: raise TopGGGloballyRateLimited(None) await sleep(retry_after, KOKORO) continue # Python casts sets to frozensets if (status in {400, 401, 402, 404}): raise TopGGHttpException(response, response_data) if try_again and (status >= 500): await sleep(10.0 / try_again, KOKORO) try_again -= 1 continue raise TopGGHttpException(response, response_data)