diff --git a/novus/enums/utils.py b/novus/enums/utils.py index 79415851..c5c94054 100644 --- a/novus/enums/utils.py +++ b/novus/enums/utils.py @@ -15,6 +15,10 @@ along with this program. If not, see . """ +from __future__ import annotations + +from typing import Any + from enum import Enum as E from enum import EnumMeta as EM @@ -25,7 +29,7 @@ class EnumMeta(EM): - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) if self.__doc__: self.__doc__ += "\n\n" diff --git a/novus/ext/client/command.py b/novus/ext/client/command.py index b0709d79..22113fa1 100644 --- a/novus/ext/client/command.py +++ b/novus/ext/client/command.py @@ -22,7 +22,7 @@ import inspect import logging from collections.abc import Callable, Coroutine, Iterable -from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Type, TypeAlias, Union, cast from typing_extensions import Self, TypeVarTuple @@ -32,7 +32,7 @@ if TYPE_CHECKING: Ts = TypeVarTuple('Ts') - CommandCallback = Union[ + CommandCallback: TypeAlias = Union[ Callable[ [Any, n.types.CommandI, *Ts], Coroutine[Any, Any, None], @@ -50,7 +50,7 @@ Coroutine[Any, Any, None], ], ] - AutocompleteCallback = Union[ + AutocompleteCallback: TypeAlias = Union[ Callable[ [ Any, @@ -68,7 +68,7 @@ ], ] - LocType = dict[str, str] | dict[n.Locale, str] | n.utils.Localization | None + LocType: TypeAlias = dict[str, str] | dict[n.Locale, str] | n.utils.Localization | None __all__ = ( diff --git a/novus/ext/client/event.py b/novus/ext/client/event.py index 48f26c7b..37e52fec 100644 --- a/novus/ext/client/event.py +++ b/novus/ext/client/event.py @@ -171,139 +171,139 @@ def autocomplete(cls, func: W[Interaction[ApplicationCommandData]]) -> EL: ) @classmethod - def raw_guild_create(cls, func: W[int]): + def raw_guild_create(cls, func: W[int]) -> EL: return EventListener("RAW_GUILD_CREATE", func) @classmethod - def guild_create(cls, func: W[Guild]): + def guild_create(cls, func: W[Guild]) -> EL: return EventListener("GUILD_CREATE", func) @classmethod - def raw_guild_update(cls, func: W[Guild]): + def raw_guild_update(cls, func: W[Guild]) -> EL: return EventListener("RAW_GUILD_UPDATE", func) @classmethod - def guild_update(cls, func: W2[Guild, Guild]): + def guild_update(cls, func: W2[Guild, Guild]) -> EL: return EventListener("GUILD_UPDATE", func) @classmethod - def raw_guild_delete(cls, func: W[int]): + def raw_guild_delete(cls, func: W[int]) -> EL: return EventListener("RAW_GUILD_DELETE", func) @classmethod - def guild_delete(cls, func: W[Guild]): + def guild_delete(cls, func: W[Guild]) -> EL: return EventListener("GUILD_DELETE", func) @classmethod - def raw_typing(cls, func: W2[int, int]): + def raw_typing(cls, func: W2[int, int]) -> EL: return EventListener("RAW_TYPING", func) @classmethod - def typing(cls, func: W2[Channel, User | GuildMember]): + def typing(cls, func: W2[Channel, User | GuildMember]) -> EL: return EventListener("TYPING", func) @classmethod - def message(cls, func: W[Message]): + def message(cls, func: W[Message]) -> EL: return EventListener("MESSAGE_CREATE", func) @classmethod - def raw_message_edit(cls, func: W[Message]): + def raw_message_edit(cls, func: W[Message]) -> EL: return EventListener("RAW_MESSAGE_UPDATE", func) @classmethod - def message_edit(cls, func: W2[Message, Message]): + def message_edit(cls, func: W2[Message, Message]) -> EL: return EventListener("MESSAGE_UPDATE", func) @classmethod - def raw_message_delete(cls, func: W2[int, int]): + def raw_message_delete(cls, func: W2[int, int]) -> EL: return EventListener("RAW_MESSAGE_DELETE", func) @classmethod - def message_delete(cls, func: W[Message]): + def message_delete(cls, func: W[Message]) -> EL: return EventListener("MESSAGE_DELETE", func) @classmethod - def channel_create(cls, func: W[Channel]): + def channel_create(cls, func: W[Channel]) -> EL: return EventListener("CHANNEL_CREATE", func) @classmethod - def raw_channel_update(cls, func: W[Channel]): + def raw_channel_update(cls, func: W[Channel]) -> EL: return EventListener("RAW_CHANNEL_UPDATE", func) @classmethod - def channel_update(cls, func: W2[Channel, Channel]): + def channel_update(cls, func: W2[Channel, Channel]) -> EL: return EventListener("CHANNEL_UPDATE", func) @classmethod - def channel_delete(cls, func: W[Channel]): + def channel_delete(cls, func: W[Channel]) -> EL: return EventListener("CHANNEL_DELETE", func) @classmethod - def guild_ban_add(cls, func: W2[BaseGuild, User | GuildMember]): + def guild_ban_add(cls, func: W2[BaseGuild, User | GuildMember]) -> EL: return EventListener("GUILD_BAN_ADD", func) @classmethod - def guild_ban_remove(cls, func: W2[BaseGuild, User]): + def guild_ban_remove(cls, func: W2[BaseGuild, User]) -> EL: return EventListener("GUILD_BAN_REMOVE", func) @classmethod - def invite_create(cls, func: W[Invite]): + def invite_create(cls, func: W[Invite]) -> EL: return EventListener("INVITE_CREATE", func) @classmethod - def invite_delete(cls, func: W2[Invite, str]): + def invite_delete(cls, func: W2[Invite, str]) -> EL: return EventListener("INVITE_DELETE", func) @classmethod - def role_create(cls, func: W[Role]): + def role_create(cls, func: W[Role]) -> EL: return EventListener("ROLE_CREATE", func) @classmethod - def raw_role_update(cls, func: W[Role]): + def raw_role_update(cls, func: W[Role]) -> EL: return EventListener("RAW_ROLE_UPDATE", func) @classmethod - def role_update(cls, func: W2[Role, Role]): + def role_update(cls, func: W2[Role, Role]) -> EL: return EventListener("ROLE_UPDATE", func) @classmethod - def raw_role_delete(cls, func: W2[int, int]): + def raw_role_delete(cls, func: W2[int, int]) -> EL: return EventListener("RAW_ROLE_DELETE", func) @classmethod - def role_delete(cls, func: W[Role]): + def role_delete(cls, func: W[Role]) -> EL: return EventListener("ROLE_DELETE", func) @classmethod - def guild_member_add(cls, func: W[GuildMember]): + def guild_member_add(cls, func: W[GuildMember]) -> EL: return EventListener("GUILD_MEMBER_ADD", func) @classmethod - def raw_guild_member_update(cls, func: W[GuildMember]): + def raw_guild_member_update(cls, func: W[GuildMember]) -> EL: return EventListener("RAW_GUILD_MEMBER_UPDATE", func) @classmethod - def guild_member_update(cls, func: W2[GuildMember, GuildMember]): + def guild_member_update(cls, func: W2[GuildMember, GuildMember]) -> EL: return EventListener("GUILD_MEMBER_UPDATE", func) @classmethod - def raw_guild_member_remove(cls, func: W2[int, int]): + def raw_guild_member_remove(cls, func: W2[int, int]) -> EL: return EventListener("RAW_GUILD_MEMBER_REMOVE", func) @classmethod - def guild_member_remove(cls, func: W2[BaseGuild, GuildMember | User]): + def guild_member_remove(cls, func: W2[BaseGuild, GuildMember | User]) -> EL: return EventListener("GUILD_MEMBER_REMOVE", func) @classmethod - def reaction_add(cls, func: W2[User | GuildMember | int, Reaction]): + def reaction_add(cls, func: W2[User | GuildMember | int, Reaction]) -> EL: return EventListener("REACTION_ADD", func) @classmethod - def reaction_remove(cls, func: W2[User | GuildMember | int, Reaction]): + def reaction_remove(cls, func: W2[User | GuildMember | int, Reaction]) -> EL: return EventListener("REACTION_REMOVE", func) @classmethod - def audit_log_entry(cls, func: W[AuditLogEntry]): + def audit_log_entry(cls, func: W[AuditLogEntry]) -> EL: return EventListener("AUDIT_LOG_ENTRY", func) diff --git a/novus/models/application_command.py b/novus/models/application_command.py index 8079f8c2..794c79aa 100644 --- a/novus/models/application_command.py +++ b/novus/models/application_command.py @@ -373,8 +373,8 @@ def __init__( self.description = description self.description_localizations = flatten_localization(description_localizations) self.options = options or [] - if default_member_permissions is MISSING: - self.default_member_permissions = None + if default_member_permissions is MISSING or default_member_permissions is None: + self.default_member_permissions = Permissions(0) else: self.default_member_permissions = default_member_permissions self.dm_permission = dm_permission @@ -382,10 +382,12 @@ def __init__( __repr__ = generate_repr(('name', 'description', 'options', 'type',)) - def __eq__(self, other: PartialApplicationCommand) -> bool: + def __eq__(self, other: object) -> bool: + if not isinstance(other, PartialApplicationCommand): + return False return self._to_data() == other._to_data() - def __ne__(self, other: PartialApplicationCommand) -> bool: + def __ne__(self, other: object) -> bool: return not self.__eq__(other) def add_option(self, option: ApplicationCommandOption) -> Self: @@ -488,7 +490,7 @@ def __init__( permissions = Permissions(p) self.state = state super().__init__( - name=data.get("name"), + name=data["name"], description=data.get("description"), type=ApplicationCommandType(data.get("type", 1)), name_localizations=data.get("name_localizations"), diff --git a/novus/models/embed.py b/novus/models/embed.py index 27f07930..4316baf1 100644 --- a/novus/models/embed.py +++ b/novus/models/embed.py @@ -23,7 +23,7 @@ from typing_extensions import Self -from ..utils import MISSING, parse_timestamp +from ..utils import MISSING, parse_timestamp, DiscordDatetime if TYPE_CHECKING: from ..payloads.embed import Embed as EmbedPayload @@ -249,14 +249,15 @@ def _to_data(self) -> EmbedPayload: @classmethod def _from_data(cls, data: EmbedPayload) -> Self: timestamp = data.get("timestamp") + timestamp_o: DiscordDatetime | None = None if timestamp is not None: - timestamp = parse_timestamp(timestamp) + timestamp_o = parse_timestamp(timestamp) embed = cls( title=data.get("title"), type=data.get("type") or "rich", description=data.get("description"), url=data.get("url"), - timestamp=timestamp, + timestamp=timestamp_o, color=data.get("color"), ) if "footer" in data: @@ -454,9 +455,10 @@ def set_author_from_user(self, user: User | GuildMember) -> Self: The user that you want to set into the embed. """ + avatar = user.avatar or user.default_avatar return self.set_author( name=str(user), - icon_url=str(user.avatar) + icon_url=avatar.get_url() ) def remove_author(self) -> Self: diff --git a/novus/models/guild_member.py b/novus/models/guild_member.py index ae1ff2f4..bf533361 100644 --- a/novus/models/guild_member.py +++ b/novus/models/guild_member.py @@ -179,6 +179,8 @@ class GuildMember(Hashable, Messageable): email: str | None flags: UserFlags premium_type: enums.UserPremiumType + avatar: Asset | None + banner: Asset | None # Member nick: str | None @@ -196,16 +198,21 @@ class GuildMember(Hashable, Messageable): def __new__(cls, **kwargs: Any) -> GuildMember: obj = super().__new__(cls) for attr in User.__slots__: - if attr.startswith("_") or attr == "state": + if (attr.startswith("_") and not attr.startswith("_cs_")) or attr == "state": continue - getter = operator.attrgetter('_user.' + attr) + nice_attr_name = attr + if attr.startswith("_cs_"): + nice_attr_name = attr[4:] + getter = operator.attrgetter('_user.' + nice_attr_name) + else: + getter = operator.attrgetter('_user.' + attr) setattr( cls, - attr, + nice_attr_name, property( getter, lambda a, b: None, # so that copy works properly - doc=f'Equivalent to :attr:`novus.User.{attr}`', + doc=f'Equivalent to :attr:`novus.User.{nice_attr_name}`', ), ) return obj @@ -277,18 +284,6 @@ def voice(self) -> VoiceState | None: return None return self.guild._voice_states.get(self.id) - @cached_slot_property('_cs_avatar') - def avatar(self) -> Asset | None: - if self.avatar_hash is None: - return None - return Asset.from_user_avatar(self) - - @cached_slot_property('_cs_banner') - def banner(self) -> Asset | None: - if self.banner_hash is None: - return None - return Asset.from_user_banner(self) - @cached_slot_property('_cs_guild_avatar') def guild_avatar(self) -> Asset | None: if self.guild_avatar_hash is None: diff --git a/novus/models/object.py b/novus/models/object.py index 2d19d983..3755f759 100644 --- a/novus/models/object.py +++ b/novus/models/object.py @@ -40,7 +40,7 @@ def __init__( self, id: int | str, *, - state: HTTPConnection = None, # pyright: ignore # Making compatible with statesnowflake + state: HTTPConnection, # Making compatible with statesnowflake guild: abc.Snowflake | None = None, guild_id: int | None = None): self.id = int(id) @@ -50,7 +50,7 @@ def __init__( self.guild = guild elif guild_id: from .guild import BaseGuild - self.guild = BaseGuild(state=self.state, data={"id": guild_id}) # pyright: ignore + self.guild = BaseGuild(state=self.state, data={"id": guild_id}) # type: ignore else: del self.guild diff --git a/novus/models/role.py b/novus/models/role.py index b984c4ac..0531785e 100644 --- a/novus/models/role.py +++ b/novus/models/role.py @@ -127,7 +127,7 @@ def __init__( self.permissions = Permissions(int(data['permissions'])) self.managed = data['managed'] self.mentionable = data['mentionable'] - self.tags = data.get('role_tags', list()) + self.tags = data.get('role_tags', []) if guild: self.guild = guild elif guild_id: diff --git a/novus/models/stage_instance.py b/novus/models/stage_instance.py index 137caa1f..5abd3081 100644 --- a/novus/models/stage_instance.py +++ b/novus/models/stage_instance.py @@ -97,14 +97,15 @@ async def create( The stage instance. """ + kwargs: dict[str, Any] = { + "channel": try_object(channel), + "topic": topic, + "privacy_level": privacy_level, + "send_start_notification": send_start_notification, + } return await state.stage_instance.create_stage_instance( reason=reason, - **{ - "channel": try_object(channel), - "topic": topic, - "privacy_level": privacy_level, - "send_start_notification": send_start_notification, - } + **kwargs ) @classmethod diff --git a/novus/models/sticker.py b/novus/models/sticker.py index de2beabc..ce58c21b 100644 --- a/novus/models/sticker.py +++ b/novus/models/sticker.py @@ -18,6 +18,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any +from typing_extensions import Self from ..enums import StickerFormat from ..utils import MISSING, cached_slot_property, try_id, try_snowflake @@ -93,13 +94,14 @@ def __init__( self.id: int = try_snowflake(data['id']) self.pack_id: int | None = try_snowflake(data.get('pack_id')) self.name: str = data['name'] - self.description: str | None = data.get('description') + self.description: str | None = data.get('description', None) # self.type: StickerType = StickerType(data['type']) self.format_type: StickerFormat = StickerFormat(data['format_type']) self.available: bool = data.get('available', True) self.guild = None - if "guild_id" in data: - self.guild = self.state.cache.get_guild(data["guild_id"]) # pyright: ignore + guild_id = data.get("guild_id") + if guild_id: + self.guild = self.state.cache.get_guild(guild_id) @cached_slot_property('_cs_asset') def asset(self) -> Asset: diff --git a/novus/models/webhook.py b/novus/models/webhook.py index 0bb6b064..765d3cec 100644 --- a/novus/models/webhook.py +++ b/novus/models/webhook.py @@ -141,7 +141,7 @@ def partial( } return cls( state=state or HTTPConnection(), - data=data, # pyright: ignore + data=data, # type: ignore ) @classmethod diff --git a/novus/utils/snowflakes.py b/novus/utils/snowflakes.py index 87e885c3..906d5ac7 100644 --- a/novus/utils/snowflakes.py +++ b/novus/utils/snowflakes.py @@ -149,5 +149,5 @@ def try_object(given: int | str | Snowflake | None) -> Snowflake | None: return None if isinstance(given, (int, str)): from ..models import Object # Circular import :( - return Object(given, state=None) # pyright: ignore + return Object(given, state=None) # type: ignore return given