mirror of
https://github.com/boticord/boticordpy.git
synced 2024-11-11 19:07:29 +03:00
search
This commit is contained in:
parent
e26366da74
commit
daa2e31732
6 changed files with 374 additions and 15 deletions
|
@ -2,16 +2,15 @@
|
||||||
Boticord API Wrapper
|
Boticord API Wrapper
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
A basic wrapper for the BotiCord API.
|
A basic wrapper for the BotiCord API.
|
||||||
:copyright: (c) 2022 Marakarka
|
:copyright: (c) 2023 Marakarka
|
||||||
:license: MIT, see LICENSE for more details.
|
:license: MIT, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = "boticordpy"
|
__title__ = "boticordpy"
|
||||||
__author__ = "Marakarka"
|
__author__ = "Marakarka"
|
||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
__copyright__ = "Copyright 2022 Marakarka"
|
__copyright__ = "Copyright 2021 - 2023 Marakarka"
|
||||||
__version__ = "2.2.2"
|
__version__ = "3.0.0a"
|
||||||
|
|
||||||
from .client import BoticordClient
|
from .client import BoticordClient
|
||||||
|
|
||||||
from .types import *
|
from .types import *
|
||||||
|
|
|
@ -3,6 +3,7 @@ import typing
|
||||||
from . import types as boticord_types
|
from . import types as boticord_types
|
||||||
from .http import HttpClient
|
from .http import HttpClient
|
||||||
from .autopost import AutoPost
|
from .autopost import AutoPost
|
||||||
|
from .exceptions import MeilisearchException
|
||||||
|
|
||||||
|
|
||||||
class BoticordClient:
|
class BoticordClient:
|
||||||
|
@ -18,12 +19,13 @@ class BoticordClient:
|
||||||
BotiCord API version (Default: 3)
|
BotiCord API version (Default: 3)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("http", "_autopost", "_token")
|
__slots__ = ("http", "_autopost", "_token", "_meilisearch_api_key")
|
||||||
|
|
||||||
http: HttpClient
|
http: HttpClient
|
||||||
|
|
||||||
def __init__(self, token: str = None, version: int = 3):
|
def __init__(self, token: str = None, version: int = 3):
|
||||||
self._token = token
|
self._token = token
|
||||||
|
self._meilisearch_api_key = None
|
||||||
self._autopost: typing.Optional[AutoPost] = None
|
self._autopost: typing.Optional[AutoPost] = None
|
||||||
self.http = HttpClient(token, version)
|
self.http = HttpClient(token, version)
|
||||||
|
|
||||||
|
@ -104,6 +106,79 @@ class BoticordClient:
|
||||||
response = await self.http.get_user_info(user_id)
|
response = await self.http.get_user_info(user_id)
|
||||||
return boticord_types.UserProfile.from_dict(response)
|
return boticord_types.UserProfile.from_dict(response)
|
||||||
|
|
||||||
|
async def __search_for(self, index, data):
|
||||||
|
"""Search for smth on BotiCord"""
|
||||||
|
if self._meilisearch_api_key is None:
|
||||||
|
token_response = await self.http.get_search_key()
|
||||||
|
self._meilisearch_api_key = token_response["key"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await self.http.search_for(
|
||||||
|
index, self._meilisearch_api_key, data
|
||||||
|
)
|
||||||
|
except MeilisearchException:
|
||||||
|
token_response = await self.http.get_search_key()
|
||||||
|
self._meilisearch_api_key = token_response["key"]
|
||||||
|
|
||||||
|
response = await self.http.search_for(
|
||||||
|
index, self._meilisearch_api_key, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return response["hits"]
|
||||||
|
|
||||||
|
async def search_for_bots(
|
||||||
|
self, **kwargs
|
||||||
|
) -> typing.List[boticord_types.MeiliIndexedBot]:
|
||||||
|
"""Search for bots on BotiCord.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
You can find every keyword argument `here <https://www.meilisearch.com/docs/reference/api/search#search-parameters>`_.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[:obj:`~.types.MeiliIndexedBot`]:
|
||||||
|
List of found bots
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = await self.__search_for("bots", kwargs)
|
||||||
|
return [boticord_types.MeiliIndexedBot.from_dict(bot) for bot in response]
|
||||||
|
|
||||||
|
async def search_for_servers(
|
||||||
|
self, **kwargs
|
||||||
|
) -> typing.List[boticord_types.MeiliIndexedServer]:
|
||||||
|
"""Search for servers on BotiCord.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
You can find every keyword argument `here <https://www.meilisearch.com/docs/reference/api/search#search-parameters>`_.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[:obj:`~.types.MeiliIndexedServer`]:
|
||||||
|
List of found servers
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = await self.__search_for("servers", kwargs)
|
||||||
|
return [
|
||||||
|
boticord_types.MeiliIndexedServer.from_dict(server) for server in response
|
||||||
|
]
|
||||||
|
|
||||||
|
async def search_for_comments(
|
||||||
|
self, **kwargs
|
||||||
|
) -> typing.List[boticord_types.MeiliIndexedComment]:
|
||||||
|
"""Search for comments on BotiCord.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
You can find every keyword argument `here <https://www.meilisearch.com/docs/reference/api/search#search-parameters>`_.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[:obj:`~.types.MeiliIndexedComment`]:
|
||||||
|
List of found comments
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = await self.__search_for("comments", kwargs)
|
||||||
|
return [
|
||||||
|
boticord_types.MeiliIndexedComment.from_dict(comment)
|
||||||
|
for comment in response
|
||||||
|
]
|
||||||
|
|
||||||
def autopost(self) -> AutoPost:
|
def autopost(self) -> AutoPost:
|
||||||
"""Returns a helper instance for auto-posting.
|
"""Returns a helper instance for auto-posting.
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class InternalException(BoticordException):
|
||||||
|
|
||||||
|
|
||||||
class HTTPException(BoticordException):
|
class HTTPException(BoticordException):
|
||||||
"""Exception that's thrown when an HTTP request operation fails.
|
"""Exception that's thrown when request to BotiCord API operation fails.
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
|
@ -37,6 +37,23 @@ class HTTPException(BoticordException):
|
||||||
super().__init__(fmt)
|
super().__init__(fmt)
|
||||||
|
|
||||||
|
|
||||||
|
class MeilisearchException(BoticordException):
|
||||||
|
"""Exception that's thrown when request to Meilisearch API operation fails.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
response:
|
||||||
|
The response of the failed HTTP request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, response):
|
||||||
|
self.response = response
|
||||||
|
|
||||||
|
fmt = f"{self.response['code']} ({self.response['message']})"
|
||||||
|
|
||||||
|
super().__init__(fmt)
|
||||||
|
|
||||||
|
|
||||||
class StatusCodes(IntEnum):
|
class StatusCodes(IntEnum):
|
||||||
"""Status codes of response"""
|
"""Status codes of response"""
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
from urllib.parse import urlparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
from .types import LinkDomain
|
|
||||||
|
|
||||||
|
|
||||||
class HttpClient:
|
class HttpClient:
|
||||||
|
@ -29,13 +29,17 @@ class HttpClient:
|
||||||
|
|
||||||
self.session = kwargs.get("session") or aiohttp.ClientSession(loop=loop)
|
self.session = kwargs.get("session") or aiohttp.ClientSession(loop=loop)
|
||||||
|
|
||||||
async def make_request(self, method: str, endpoint: str, **kwargs) -> dict:
|
async def make_request(
|
||||||
|
self, method: str, endpoint: str, *, meilisearch_token: str = None, **kwargs
|
||||||
|
) -> dict:
|
||||||
"""Send requests to the API"""
|
"""Send requests to the API"""
|
||||||
|
|
||||||
kwargs["headers"] = {"Content-Type": "application/json"}
|
kwargs["headers"] = {"Content-Type": "application/json"}
|
||||||
|
|
||||||
if self.token is not None:
|
if self.token is not None:
|
||||||
kwargs["headers"]["Authorization"] = self.token
|
kwargs["headers"]["Authorization"] = self.token
|
||||||
|
if meilisearch_token is not None:
|
||||||
|
kwargs["headers"]["Authorization"] = f"Bearer {meilisearch_token}"
|
||||||
|
|
||||||
url = f"{self.API_URL}{endpoint}"
|
url = f"{self.API_URL}{endpoint}"
|
||||||
|
|
||||||
|
@ -43,11 +47,14 @@ class HttpClient:
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
|
|
||||||
if (200, 201).__contains__(response.status):
|
if (200, 201).__contains__(response.status):
|
||||||
return data["result"]
|
return data["result"] if not meilisearch_token else data
|
||||||
else:
|
else:
|
||||||
raise exceptions.HTTPException(
|
if not meilisearch_token:
|
||||||
{"status": response.status, "error": data["errors"][0]["code"]}
|
raise exceptions.HTTPException(
|
||||||
)
|
{"status": response.status, "error": data["errors"][0]["code"]}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise exceptions.MeilisearchException(data)
|
||||||
|
|
||||||
def get_bot_info(self, bot_id: typing.Union[str, int]):
|
def get_bot_info(self, bot_id: typing.Union[str, int]):
|
||||||
"""Get information about the specified bot"""
|
"""Get information about the specified bot"""
|
||||||
|
@ -64,3 +71,16 @@ class HttpClient:
|
||||||
def get_user_info(self, user_id: typing.Union[str, int]):
|
def get_user_info(self, user_id: typing.Union[str, int]):
|
||||||
"""Get information about specified user"""
|
"""Get information about specified user"""
|
||||||
return self.make_request("GET", f"users/{user_id}")
|
return self.make_request("GET", f"users/{user_id}")
|
||||||
|
|
||||||
|
def get_search_key(self):
|
||||||
|
"""Get API key for Meilisearch"""
|
||||||
|
return self.make_request("GET", f"search-key")
|
||||||
|
|
||||||
|
def search_for(self, index: str, api_key: str, data: dict):
|
||||||
|
"""Search for something on BotiCord."""
|
||||||
|
return self.make_request(
|
||||||
|
"POST",
|
||||||
|
f"search/indexes/{index}/search",
|
||||||
|
meilisearch_token=api_key,
|
||||||
|
json=data,
|
||||||
|
)
|
||||||
|
|
|
@ -559,7 +559,12 @@ class PartialUser(APIObjectBase):
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class ResourceServer(APIObjectBase):
|
class ResourceServer(APIObjectBase):
|
||||||
"""Information about server from BotiCord."""
|
"""Information about server from BotiCord.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The result of the reverse conversion (`.to_dict()`) may not match the actual data.
|
||||||
|
"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
"""Server's ID"""
|
"""Server's ID"""
|
||||||
|
@ -700,6 +705,9 @@ class ResourceBot(APIObjectBase):
|
||||||
short_link: Optional[str]
|
short_link: Optional[str]
|
||||||
"""Short link to the bot's page"""
|
"""Short link to the bot's page"""
|
||||||
|
|
||||||
|
standart_banner_id: int
|
||||||
|
"""Server's standart banner ID"""
|
||||||
|
|
||||||
invite_link: str
|
invite_link: str
|
||||||
"""Invite link"""
|
"""Invite link"""
|
||||||
|
|
||||||
|
@ -788,6 +796,7 @@ class ResourceBot(APIObjectBase):
|
||||||
self.support_server_invite_link = data.get("support_server_invite")
|
self.support_server_invite_link = data.get("support_server_invite")
|
||||||
self.website = data.get("website")
|
self.website = data.get("website")
|
||||||
self.up_count = data.get("upCount")
|
self.up_count = data.get("upCount")
|
||||||
|
self.standart_banner_id = data.get("standartBannerID")
|
||||||
|
|
||||||
self.premium_active = data["premium"].get("active")
|
self.premium_active = data["premium"].get("active")
|
||||||
self.premium_splash_url = data["premium"].get("splashURL")
|
self.premium_splash_url = data["premium"].get("splashURL")
|
||||||
|
@ -850,5 +859,231 @@ class UserProfile(PartialUser):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class LinkDomain:
|
@dataclass(repr=False)
|
||||||
pass
|
class MeiliIndexedBot(APIObjectBase):
|
||||||
|
"""Bot found on BotiCord
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The result of the reverse conversion (`.to_dict()`) may not match the actual data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
"""ID of the bot"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""Name of the bot"""
|
||||||
|
|
||||||
|
short_description: str
|
||||||
|
"""Short description of the bot"""
|
||||||
|
|
||||||
|
description: str
|
||||||
|
"""Description of the bot"""
|
||||||
|
|
||||||
|
avatar: Optional[str]
|
||||||
|
"""Avatar of the bot"""
|
||||||
|
|
||||||
|
invite: str
|
||||||
|
"""Invite link"""
|
||||||
|
|
||||||
|
premium_active: bool
|
||||||
|
"""Is premium status active? (True/False)"""
|
||||||
|
|
||||||
|
premium_banner: Optional[str]
|
||||||
|
"""Premium banner URL"""
|
||||||
|
|
||||||
|
banner: int
|
||||||
|
"""Standart banner"""
|
||||||
|
|
||||||
|
rating: int
|
||||||
|
"""Bot's rating"""
|
||||||
|
|
||||||
|
discriminator: str
|
||||||
|
"""Bot's discriminator"""
|
||||||
|
|
||||||
|
library: Optional[BotLibrary]
|
||||||
|
"""The library that the bot is based on"""
|
||||||
|
|
||||||
|
guilds: Optional[int]
|
||||||
|
"""Number of guilds"""
|
||||||
|
|
||||||
|
shards: Optional[int]
|
||||||
|
"""Number of shards"""
|
||||||
|
|
||||||
|
members: Optional[int]
|
||||||
|
"""Number of members"""
|
||||||
|
|
||||||
|
tags: List[BotTag]
|
||||||
|
"""List of bot tags"""
|
||||||
|
|
||||||
|
ups: int
|
||||||
|
"""List of bot's ups"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
"""Generate a MeiliIndexedBot from the given data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data: :class:`dict`
|
||||||
|
The dictionary to convert into a MeiliIndexedBot.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self: MeiliIndexedBot = super().__new__(cls)
|
||||||
|
|
||||||
|
self.id = data.get("id")
|
||||||
|
self.name = data.get("name")
|
||||||
|
self.short_description = data.get("shortDescription")
|
||||||
|
self.description = data.get("description")
|
||||||
|
self.avatar = data.get("avatar")
|
||||||
|
self.invite = data.get("invite")
|
||||||
|
self.discriminator = data.get("discriminator")
|
||||||
|
self.ups = data.get("ups")
|
||||||
|
self.rating = data.get("rating")
|
||||||
|
self.banner = data.get("banner")
|
||||||
|
|
||||||
|
self.premium_active = data.get("premiumActive")
|
||||||
|
self.premium_banner = data.get("premiumBanner")
|
||||||
|
|
||||||
|
self.library = (
|
||||||
|
BotLibrary(data["library"]) if data.get("library") is not None else None
|
||||||
|
)
|
||||||
|
self.tags = [BotTag(tag) for tag in data.get("tags", [])]
|
||||||
|
|
||||||
|
self.guilds = data.get("guilds")
|
||||||
|
self.shards = data.get("shards")
|
||||||
|
self.members = data.get("members")
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class MeiliIndexedServer(APIObjectBase):
|
||||||
|
"""Server found on BotiCord
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The result of the reverse conversion (`.to_dict()`) may not match the actual data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
"""ID of the server"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""Name of the server"""
|
||||||
|
|
||||||
|
short_description: str
|
||||||
|
"""Short description of the server"""
|
||||||
|
|
||||||
|
description: str
|
||||||
|
"""Description of the server"""
|
||||||
|
|
||||||
|
avatar: Optional[str]
|
||||||
|
"""Avatar of the server"""
|
||||||
|
|
||||||
|
invite: str
|
||||||
|
"""Invite link"""
|
||||||
|
|
||||||
|
premium_active: bool
|
||||||
|
"""Is premium status active? (True/False)"""
|
||||||
|
|
||||||
|
premium_banner: Optional[str]
|
||||||
|
"""Premium banner URL"""
|
||||||
|
|
||||||
|
banner: int
|
||||||
|
"""Standart banner"""
|
||||||
|
|
||||||
|
discord_banner: Optional[str]
|
||||||
|
"""Discord banner URL"""
|
||||||
|
|
||||||
|
rating: int
|
||||||
|
"""Server's rating"""
|
||||||
|
|
||||||
|
members: Optional[int]
|
||||||
|
"""Number of members"""
|
||||||
|
|
||||||
|
tags: List[ServerTag]
|
||||||
|
"""List of server tags"""
|
||||||
|
|
||||||
|
ups: int
|
||||||
|
"""List of server's ups"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
"""Generate a MeiliIndexedServer from the given data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data: :class:`dict`
|
||||||
|
The dictionary to convert into a MeiliIndexedServer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self: MeiliIndexedServer = super().__new__(cls)
|
||||||
|
|
||||||
|
self.id = data.get("id")
|
||||||
|
self.name = data.get("name")
|
||||||
|
self.short_description = data.get("shortDescription")
|
||||||
|
self.description = data.get("description")
|
||||||
|
self.avatar = data.get("avatar")
|
||||||
|
self.invite = data.get("invite")
|
||||||
|
self.ups = data.get("ups")
|
||||||
|
self.rating = data.get("rating")
|
||||||
|
self.banner = data.get("banner")
|
||||||
|
|
||||||
|
self.premium_active = data.get("premiumActive")
|
||||||
|
self.premium_banner = data.get("premiumBanner")
|
||||||
|
self.discord_banner = data.get("discordBanner")
|
||||||
|
|
||||||
|
self.tags = [ServerTag(tag) for tag in data.get("tags", [])]
|
||||||
|
|
||||||
|
self.members = data.get("members")
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class MeiliIndexedComment(APIObjectBase):
|
||||||
|
"""Comment found on BotiCord"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
"""ID of the comment"""
|
||||||
|
|
||||||
|
author: str
|
||||||
|
"""Id of the author of the comment"""
|
||||||
|
|
||||||
|
rating: int
|
||||||
|
"""Comment's rating"""
|
||||||
|
|
||||||
|
content: str
|
||||||
|
"""Content of the comment"""
|
||||||
|
|
||||||
|
resource: str
|
||||||
|
"""Id of the resource"""
|
||||||
|
|
||||||
|
created: datetime
|
||||||
|
"""When the comment was created"""
|
||||||
|
|
||||||
|
mod_reply: Optional[str]
|
||||||
|
"""Reply to the comment"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
"""Generate a MeiliIndexedComment from the given data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data: :class:`dict`
|
||||||
|
The dictionary to convert into a MeiliIndexedComment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self: MeiliIndexedComment = super().__new__(cls)
|
||||||
|
|
||||||
|
self.id = data.get("id")
|
||||||
|
self.rating = data.get("rating")
|
||||||
|
self.author = data.get("author")
|
||||||
|
self.content = data.get("content")
|
||||||
|
self.resource = data.get("resource")
|
||||||
|
self.mod_reply = data.get("modReply")
|
||||||
|
self.created = datetime.utcfromtimestamp(data.get("created") / 1000)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
|
@ -56,3 +56,16 @@ Users
|
||||||
:members:
|
:members:
|
||||||
:exclude-members: to_dict
|
:exclude-members: to_dict
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
|
MeiliSearch
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. autoclass:: MeiliIndexedBot
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: MeiliIndexedServer
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: MeiliIndexedComment
|
||||||
|
:members:
|
||||||
|
|
Loading…
Reference in a new issue