From 114d381c659795b7ac3246f1ba5624470bd475f3 Mon Sep 17 00:00:00 2001 From: endercheif Date: Thu, 16 Dec 2021 21:38:49 -0800 Subject: [PATCH 1/8] :sparkles: Added UserMessage.from_id --- pincer/client.py | 154 +++++++++++++++---------- pincer/objects/message/user_message.py | 44 ++++++- 2 files changed, 129 insertions(+), 69 deletions(-) diff --git a/pincer/client.py b/pincer/client.py index d07678e0..91b8bf00 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -9,7 +9,14 @@ from importlib import import_module from inspect import isasyncgenfunction from typing import ( - Any, Dict, List, Optional, Tuple, Union, overload, AsyncIterator + Any, + Dict, + List, + Optional, + Tuple, + Union, + overload, + AsyncIterator, ) from typing import TYPE_CHECKING @@ -18,13 +25,23 @@ from .core import HTTPClient from .core.gateway import Dispatcher from .exceptions import ( - InvalidEventName, TooManySetupArguments, NoValidSetupMethod, - NoCogManagerReturnFound, CogAlreadyExists, CogNotFound + InvalidEventName, + TooManySetupArguments, + NoValidSetupMethod, + NoCogManagerReturnFound, + CogAlreadyExists, + CogNotFound, ) from .middleware import middleware from .objects import ( - Role, Channel, DefaultThrottleHandler, User, Guild, Intents, - GuildTemplate, StickerPack + Role, + Channel, + DefaultThrottleHandler, + User, + Guild, + Intents, + GuildTemplate, + StickerPack, UserMessage, ) from .utils.conversion import construct_client_dict from .utils.event_mgr import EventMgr @@ -105,7 +122,8 @@ def decorator(func: Coro): if override: _log.warning( "Middleware overriding has been enabled for `%s`." - " This might cause unexpected behavior.", call + " This might cause unexpected behavior.", + call, ) if not override and callable(_events.get(call)): @@ -118,9 +136,7 @@ async def wrapper(cls, payload: GatewayDispatch): _log.debug("`%s` middleware has been invoked", call) return await ( - func(cls, payload) - if should_pass_cls(func) - else func(payload) + func(cls, payload) if should_pass_cls(func) else func(payload) ) _events[call] = wrapper @@ -167,12 +183,13 @@ class Client(Dispatcher): """ def __init__( - self, - token: str, *, - received: str = None, - intents: Intents = None, - throttler: ThrottleInterface = DefaultThrottleHandler, - reconnect: bool = True, + self, + token: str, + *, + received: str = None, + intents: Intents = None, + throttler: ThrottleInterface = DefaultThrottleHandler, + reconnect: bool = True, ): super().__init__( token, @@ -180,7 +197,7 @@ def __init__( # Gets triggered on all events -1: self.payload_event_handler, # Use this event handler for opcode 0. - 0: self.event_handler + 0: self.event_handler, }, intents=intents or Intents.NONE, reconnect=reconnect, @@ -204,10 +221,9 @@ def chat_commands(self) -> List[str]: Get a list of chat command calls which have been registered in the :class:`~pincer.commands.ChatCommandHandler`\\. """ - return list(map( - lambda cmd: cmd.app.name, - ChatCommandHandler.register.values() - )) + return list( + map(lambda cmd: cmd.app.name, ChatCommandHandler.register.values()) + ) @staticmethod def event(coroutine: Coro): @@ -258,8 +274,9 @@ async def on_ready(self): InvalidEventName If the function name is not a valid event (on_x) """ - if not iscoroutinefunction(coroutine) \ - and not isasyncgenfunction(coroutine): + if not iscoroutinefunction(coroutine) and not isasyncgenfunction( + coroutine + ): raise TypeError( "Any event which is registered must be a coroutine function" ) @@ -291,10 +308,17 @@ def get_event_coro(name: str) -> List[Optional[Coro]]: """ calls = _events.get(name.strip().lower()) - return [] if not calls else list(filter( - lambda call: iscoroutinefunction(call) or isasyncgenfunction(call), - calls - )) + return ( + [] + if not calls + else list( + filter( + lambda call: iscoroutinefunction(call) + or isasyncgenfunction(call), + calls, + ) + ) + ) def load_cog(self, path: str, package: Optional[str] = None): """Load a cog from a string path, setup method in COG may @@ -435,7 +459,7 @@ def execute_event(calls: List[Coro], *args, **kwargs): if should_pass_cls(call): call_args = ( ChatCommandHandler.managers[call.__module__], - *(arg for arg in args if arg is not None) + *(arg for arg in args if arg is not None), ) ensure_future(call(*call_args, **kwargs)) @@ -446,15 +470,11 @@ def run(self): def __del__(self): """Ensure close of the http client.""" - if hasattr(self, 'http'): + if hasattr(self, "http"): run(self.http.close()) async def handle_middleware( - self, - payload: GatewayDispatch, - key: str, - *args, - **kwargs + self, payload: GatewayDispatch, key: str, *args, **kwargs ) -> Tuple[Optional[Coro], List[Any], Dict[str, Any]]: """|coro| @@ -506,11 +526,7 @@ async def handle_middleware( ) async def execute_error( - self, - error: Exception, - name: str = "on_error", - *args, - **kwargs + self, error: Exception, name: str = "on_error", *args, **kwargs ): """|coro| @@ -607,7 +623,7 @@ async def create_guild( afk_channel_id: Optional[Snowflake] = None, afk_timeout: Optional[int] = None, system_channel_id: Optional[Snowflake] = None, - system_channel_flags: Optional[int] = None + system_channel_flags: Optional[int] = None, ) -> Guild: """Creates a guild. @@ -648,7 +664,7 @@ async def create_guild( async def create_guild(self, name: str, **kwargs) -> Guild: g = await self.http.post("guilds", data={"name": name, **kwargs}) - return await self.get_guild(g['id']) + return await self.get_guild(g["id"]) async def get_guild_template(self, code: str) -> GuildTemplate: """|coro| @@ -666,16 +682,12 @@ async def get_guild_template(self, code: str) -> GuildTemplate: """ return GuildTemplate.from_dict( construct_client_dict( - self, - await self.http.get(f"guilds/templates/{code}") + self, await self.http.get(f"guilds/templates/{code}") ) ) async def create_guild_from_template( - self, - template: GuildTemplate, - name: str, - icon: Optional[str] = None + self, template: GuildTemplate, name: str, icon: Optional[str] = None ) -> Guild: """|coro| Creates a guild from a template. @@ -699,16 +711,16 @@ async def create_guild_from_template( self, await self.http.post( f"guilds/templates/{template.code}", - data={"name": name, "icon": icon} - ) + data={"name": name, "icon": icon}, + ), ) ) async def wait_for( - self, - event_name: str, - check: CheckFunction = None, - timeout: Optional[float] = None + self, + event_name: str, + check: CheckFunction = None, + timeout: Optional[float] = None, ): """ Parameters @@ -729,11 +741,11 @@ async def wait_for( return await self.event_mgr.wait_for(event_name, check, timeout) def loop_for( - self, - event_name: str, - check: CheckFunction = None, - iteration_timeout: Optional[float] = None, - loop_timeout: Optional[float] = None + self, + event_name: str, + check: CheckFunction = None, + iteration_timeout: Optional[float] = None, + loop_timeout: Optional[float] = None, ): """ Parameters @@ -755,10 +767,7 @@ def loop_for( What the Discord API returns for this event. """ return self.event_mgr.loop_for( - event_name, - check, - iteration_timeout, - loop_timeout + event_name, check, iteration_timeout, loop_timeout ) async def get_guild(self, guild_id: int) -> Guild: @@ -836,10 +845,27 @@ async def get_channel(self, _id: int) -> Channel: """ return await Channel.from_id(self, _id) + async def get_message(self, _id: Snowflake, channel_id: Snowflake) -> UserMessage: + """|coro| + Creates a UserMessage object + + Parameters + ---------- + _id: :class:`~pincer.utils.snowflake.Snowflake` + ID of the message that is wanted. + channel_id : int + ID of the channel the message is in. + + Returns + ------- + :class:`~pincer.objects.message.user_message.UserMessage` + The message object. + """ + + return await UserMessage.from_id(self, _id, channel_id) + async def get_webhook( - self, - id: Snowflake, - token: Optional[str] = None + self, id: Snowflake, token: Optional[str] = None ) -> Webhook: """|coro| Fetch a Webhook from its identifier. diff --git a/pincer/objects/message/user_message.py b/pincer/objects/message/user_message.py index 9bb503c1..0f94e3de 100644 --- a/pincer/objects/message/user_message.py +++ b/pincer/objects/message/user_message.py @@ -27,6 +27,7 @@ if TYPE_CHECKING: from typing import Any, List, Optional, Union, Generator + from ... import Client from ..guild.channel import Channel, ChannelMention from ...utils.types import APINullable from ...utils.timestamp import Timestamp @@ -44,6 +45,7 @@ class AllowedMentionTypes(str, Enum): EVERYONE: Controls @everyone and @here mentions """ + ROLES = "roles" USERS = "users" EVERYONE = "everyone" @@ -65,6 +67,7 @@ class AllowedMentions(APIObject): If replies should mention the author. |default| :data:`True` """ + # noqa: E501 parse: List[AllowedMentionTypes] @@ -83,7 +86,7 @@ def get_str_id(obj: Union[Snowflake, User, Role]) -> str: "parse": self.parse, "roles": list(map(get_str_id, self.roles)), "users": list(map(get_str_id, self.users)), - "replied_user": self.reply + "replied_user": self.reply, } @@ -103,6 +106,7 @@ class MessageActivityType(IntEnum): JOIN_REQUEST: Request to join. """ + JOIN = 1 SPECTATE = 2 LISTEN = 3 @@ -137,6 +141,7 @@ class MessageFlags(IntEnum): This message is an Interaction Response and the bot is "thinking" """ + CROSSPOSTED = 1 << 0 IS_CROSSPOST = 1 << 1 SUPPRESS_EMBEDS = 1 << 2 @@ -197,6 +202,7 @@ class MessageType(IntEnum): GUILD_INVITE_REMINDER: ?? """ + DEFAULT = 0 RECIPIENT_ADD = 1 RECIPIENT_REMOVE = 2 @@ -240,6 +246,7 @@ class MessageActivity(APIObject): party_id: APINullable[:class:`str`] party_id from a Rich Presence event """ + type: MessageActivityType party_id: APINullable[str] = MISSING @@ -316,6 +323,7 @@ class UserMessage(APIObject): sticker_items: APINullable[List[:class:`~pincer.objects.message.sticker.StickerItem`]] Sent if the message contains stickers """ + # noqa: E501 id: Snowflake @@ -350,6 +358,33 @@ class UserMessage(APIObject): components: APINullable[List[MessageComponent]] = MISSING sticker_items: APINullable[List[StickerItem]] = MISSING + @classmethod + async def from_id( + cls, client: Client, _id: Snowflake, channel_id: Snowflake + ) -> UserMessage: + """|coro| + + Creates a UserMessage object + It is recommended to use the ``get_message`` function from + :class:`~pincer.client.Client` most of the time. + + Parameters + ---------- + client : :class:`~pincer.client.Client` + Client object to use the HTTP class of. + _id: :class:`~pincer.utils.snowflake.Snowflake` + ID of the message that is wanted. + channel_id : int + ID of the channel the message is in. + + Returns + ------- + :class:`~pincer.objects.message.user_message.UserMessage` + The message object. + """ + msg = await client.http.get(f"channels/{channel_id}/messages/{_id}") + return cls.from_dict(construct_client_dict(client, msg)) + def __str__(self): return self.content @@ -366,7 +401,7 @@ async def get_most_recent(self): self._client, await self._http.get( f"/channels/{self.channel_id}/messages/{self.id}" - ) + ), ) ) @@ -482,7 +517,7 @@ async def edit( flags: int = None, allowed_mentions: AllowedMentions = None, attachments: List[Attachment] = None, - components: List[MessageComponent] = None + components: List[MessageComponent] = None, ): """|coro| @@ -527,8 +562,7 @@ def set_if_not_none(value: Any, name: str): set_if_not_none(components, "components") await self._http.patch( - f"/channels/{self.channel_id}/messages/{self.id}", - data=data + f"/channels/{self.channel_id}/messages/{self.id}", data=data ) async def delete(self): From feb21ec287792f0c8ac74df6dc2550961d48505a Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:35:47 -0800 Subject: [PATCH 2/8] :art: Change map to list comp Co-authored-by: trag1c <77130613+trag1c@users.noreply.github.com> --- pincer/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pincer/client.py b/pincer/client.py index 149fc3b3..3ca43ab5 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -226,9 +226,9 @@ def chat_commands(self) -> List[str]: Get a list of chat command calls which have been registered in the :class:`~pincer.commands.ChatCommandHandler`\\. """ - return list( - map(lambda cmd: cmd.app.name, ChatCommandHandler.register.values()) - ) + return [ + cmd.app.name for cmd in ChatCommandHandler.register.values() + ] @property def guild_ids(self) -> List[Snowflake]: From 2640c8b9d99c5622ab301ab72a4e0c0c20a04247 Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:36:21 -0800 Subject: [PATCH 3/8] :art: Fomatting for newlines Co-authored-by: trag1c <77130613+trag1c@users.noreply.github.com> --- pincer/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pincer/client.py b/pincer/client.py index 3ca43ab5..1ed25eac 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -290,8 +290,9 @@ async def on_ready(self): InvalidEventName If the function name is not a valid event (on_x) """ - if not iscoroutinefunction(coroutine) and not isasyncgenfunction( - coroutine + if ( + not iscoroutinefunction(coroutine) + and not isasyncgenfunction(coroutine) ): raise TypeError( "Any event which is registered must be a coroutine function" From 6aec671771dd87f0ad9db276ef787e082a02524f Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:36:39 -0800 Subject: [PATCH 4/8] :art: Consistant importing Co-authored-by: trag1c <77130613+trag1c@users.noreply.github.com> --- pincer/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pincer/client.py b/pincer/client.py index 1ed25eac..f468bd84 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -40,7 +40,8 @@ Guild, Intents, GuildTemplate, - StickerPack, UserMessage, + StickerPack, + UserMessage ) from .utils.conversion import construct_client_dict from .utils.event_mgr import EventMgr From d416e800472b06e6d30686fadf8b3cf671d4a9ae Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:42:35 -0800 Subject: [PATCH 5/8] :sparkles: Added tuple support to `remove_none` --- pincer/utils/conversion.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pincer/utils/conversion.py b/pincer/utils/conversion.py index 1e7137d7..2e94613d 100644 --- a/pincer/utils/conversion.py +++ b/pincer/utils/conversion.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from ..client import Client - from typing import Any, Callable, Dict, List, Optional, Set, Union + from typing import Any, Callable, Dict, List, Optional, Set, Union, Tuple def construct_client_dict(client: Client, data: Dict) -> Dict: @@ -29,22 +29,24 @@ def construct_client_dict(client: Client, data: Dict) -> Dict: return {**data, "_client": client} -def remove_none(obj: Union[List, Dict, Set]) -> Union[List, Dict, Set]: +def remove_none(obj: Union[List, Dict, Set, Tuple]) -> Union[List, Dict, Set, Tuple]: """ Removes all ``None`` values from a list, dict or set. Parameters ---------- - obj : Union[List, Dict, Set] - The list, dict or set to remove ``None`` values from. + obj : Union[List, Dict, Set, Tuple] + The list, dict, set or tuple to remove ``None`` values from. Returns ------- - Union[List, Dict, Set] - The list, dict or set without ``None`` values. + Union[List, Dict, Set, Tuple] + The list, dict, set or tuple, without ``None`` values. """ if isinstance(obj, list): return [i for i in obj if i is not None] + elif isinstance(obj, list): + return tuple(i for i in obj if i is not None) elif isinstance(obj, set): return obj - {None} elif isinstance(obj, dict): From 77c695a87e03f4e2e8413bd972ceffdbee01d19a Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:45:28 -0800 Subject: [PATCH 6/8] :art: Replace `None` check with `remove_none` --- pincer/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pincer/client.py b/pincer/client.py index f468bd84..6c3b41b1 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -43,7 +43,7 @@ StickerPack, UserMessage ) -from .utils.conversion import construct_client_dict +from .utils.conversion import construct_client_dict, remove_none from .utils.event_mgr import EventMgr from .utils.extraction import get_index from .utils.insertion import should_pass_cls @@ -477,7 +477,7 @@ def execute_event(calls: List[Coro], *args, **kwargs): if should_pass_cls(call): call_args = ( ChatCommandHandler.managers[call.__module__], - *(arg for arg in args if arg is not None), + *remove_none(args), ) ensure_future(call(*call_args, **kwargs)) From 82defbb7d3b5351d2f4a7ee445eb97b4d3cf5c43 Mon Sep 17 00:00:00 2001 From: Yohann Boniface Date: Fri, 17 Dec 2021 23:52:28 +0100 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=8E=A8=20Update=20pincer/client.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: trag1c <77130613+trag1c@users.noreply.github.com> --- pincer/client.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pincer/client.py b/pincer/client.py index 6c3b41b1..79c8eac3 100644 --- a/pincer/client.py +++ b/pincer/client.py @@ -327,15 +327,12 @@ def get_event_coro(name: str) -> List[Optional[Coro]]: calls = _events.get(name.strip().lower()) return ( - [] - if not calls - else list( - filter( - lambda call: iscoroutinefunction(call) - or isasyncgenfunction(call), - calls, - ) - ) + [] if not calls + else [ + call for call in calls + if iscoroutinefunction(call) + or isasyncgenfunction(call) + ] ) def load_cog(self, path: str, package: Optional[str] = None): From b52a0584b7ecd61fb2c2cee551b64d7806944dda Mon Sep 17 00:00:00 2001 From: Endercheif <45527309+Endercheif@users.noreply.github.com> Date: Tue, 21 Dec 2021 09:55:46 -0800 Subject: [PATCH 8/8] :bug: change isinstance to tuple Co-authored-by: trag1c <77130613+trag1c@users.noreply.github.com> --- pincer/utils/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pincer/utils/conversion.py b/pincer/utils/conversion.py index 2e94613d..940e28c5 100644 --- a/pincer/utils/conversion.py +++ b/pincer/utils/conversion.py @@ -45,7 +45,7 @@ def remove_none(obj: Union[List, Dict, Set, Tuple]) -> Union[List, Dict, Set, Tu """ if isinstance(obj, list): return [i for i in obj if i is not None] - elif isinstance(obj, list): + elif isinstance(obj, tuple): return tuple(i for i in obj if i is not None) elif isinstance(obj, set): return obj - {None}