Skip to content

Commit

Permalink
Merge pull request #13 from botlabs-gg/carlbot-dev
Browse files Browse the repository at this point in the history
Carlbot dev
  • Loading branch information
amityadav-bst authored Sep 24, 2024
2 parents 2121f4c + 919656d commit 9e8a923
Show file tree
Hide file tree
Showing 17 changed files with 158 additions and 89 deletions.
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ possible (see our [Version Guarantees] for more info).

These changes are available on the `master` branch, but have not yet been released.

## [2.6.1] - 2024-09-15

### Fixed

- Fixed premature garbage collection of tasks.
([#2510](https://github.com/Pycord-Development/pycord/pull/2510))
- Fixed `EntitlementIterator` type hints and behavior with `limit > 100`.
([#2555](https://github.com/Pycord-Development/pycord/pull/2555))
- Fixed missing `stacklevel` parameter in `warn_deprecated` function call inside
`@utils.deprecated`. ([#2500](https://github.com/Pycord-Development/pycord/pull/2500))
- Fixed the type hint in `ConnectionState._polls` to reflect actual behavior, changing it
from `Guild` to `Poll`.
([#2500](https://github.com/Pycord-Development/pycord/pull/2500))
- Fixed missing `__slots__` attributes in `RawReactionClearEmojiEvent` and
`RawMessagePollVoteEvent`.
([#2500](https://github.com/Pycord-Development/pycord/pull/2500))
- Fixed the type of `ForumChannel.default_sort_order`, changing it from `int` to
`SortOrder`. ([#2500](https://github.com/Pycord-Development/pycord/pull/2500))
- Fixed `PartialMessage` causing errors when created from `PartialMessageable`.
([#2568](https://github.com/Pycord-Development/pycord/pull/2500))
- Fixed the `guild` attribute of `Member`s recieved from a `UserCommand` being `None`.
([#2573](https://github.com/Pycord-Development/pycord/pull/2573))
- Fixed `Webhook.send` not including attachment data.
([#2513](https://github.com/Pycord-Development/pycord/pull/2513))
- Fixed inverted type hints in `CheckAnyFailure`.
([#2502](https://github.com/Pycord-Development/pycord/pull/2502))

## [2.6.0] - 2024-07-09

### Added
Expand Down
3 changes: 3 additions & 0 deletions discord/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,9 @@ def _update(self, guild: Guild, data: ForumChannelPayload) -> None:
for tag in (data.get("available_tags") or [])
]
self.default_sort_order: SortOrder | None = data.get("default_sort_order", None)
if self.default_sort_order is not None:
self.default_sort_order = try_enum(SortOrder, self.default_sort_order)

reaction_emoji_ctx: dict = data.get("default_reaction_emoji")
if reaction_emoji_ctx is not None:
emoji_name = reaction_emoji_ctx.get("emoji_name")
Expand Down
11 changes: 9 additions & 2 deletions discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ def __init__(
VoiceClient.warn_nacl = False
_log.warning("PyNaCl is not installed, voice will NOT be supported")

# Used to hard-reference tasks so they don't get garbage collected (discarded with done_callbacks)
self._tasks = set()

async def __aenter__(self) -> Client:
loop = asyncio.get_running_loop()
self.loop = loop
Expand Down Expand Up @@ -423,8 +426,12 @@ def _schedule_event(
**kwargs: Any,
) -> asyncio.Task:
wrapped = self._run_event(coro, event_name, *args, **kwargs)
# Schedules the task
return asyncio.create_task(wrapped, name=f"pycord: {event_name}")

# Schedule task and store in set to avoid task garbage collection
task = asyncio.create_task(wrapped, name=f"pycord: {event_name}")
self._tasks.add(task)
task.add_done_callback(self._tasks.discard)
return task

def dispatch(self, event: str, *args: Any, **kwargs: Any) -> None:
_log.debug("Dispatching event %s", event)
Expand Down
8 changes: 2 additions & 6 deletions discord/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1786,12 +1786,8 @@ async def _invoke(self, ctx: ApplicationContext) -> None:
v["id"] = int(i)
user = v
member["user"] = user
target = Member(
data=member,
guild=ctx.interaction._state._get_guild(ctx.interaction.guild_id),
state=ctx.interaction._state,
)

cache_flag = ctx.interaction._state.member_cache_flags.interaction
target = ctx.guild._get_and_update_member(member, user["id"], cache_flag)
if self.cog is not None:
await self.callback(self.cog, ctx, target)
else:
Expand Down
2 changes: 1 addition & 1 deletion discord/commands/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def option(name, input_type=None, **kwargs):
Attributes
----------
parameter_name: :class:`str`
The name of the target parameter this option is mapped to.
The name of the target function parameter this option is mapped to.
This allows you to have a separate UI ``name`` and parameter name.
"""

Expand Down
2 changes: 1 addition & 1 deletion discord/ext/bridge/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ def bridge_option(name, input_type=None, **kwargs):
Attributes
----------
parameter_name: :class:`str`
The name of the target parameter this option is mapped to.
The name of the target function parameter this option is mapped to.
This allows you to have a separate UI ``name`` and parameter name.
"""

Expand Down
6 changes: 3 additions & 3 deletions discord/ext/commands/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ class CheckAnyFailure(CheckFailure):
"""

def __init__(
self, checks: list[CheckFailure], errors: list[Callable[[Context], bool]]
self, checks: list[Callable[[Context], bool]], errors: list[CheckFailure]
) -> None:
self.checks: list[CheckFailure] = checks
self.errors: list[Callable[[Context], bool]] = errors
self.checks: list[Callable[[Context], bool]] = checks
self.errors: list[CheckFailure] = errors
super().__init__("You do not have permission to run this command.")


Expand Down
31 changes: 2 additions & 29 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -2610,35 +2610,6 @@ def bulk_upsert_guild_commands(
)
return self.request(r, json=payload)

# Application commands (permissions)

def get_command_permissions(
self,
application_id: Snowflake,
guild_id: Snowflake,
command_id: Snowflake,
) -> Response[interactions.GuildApplicationCommandPermissions]:
r = Route(
"GET",
"/applications/{application_id}/guilds/{guild_id}/commands/{command_id}/permissions",
application_id=application_id,
guild_id=guild_id,
)
return self.request(r)

def get_guild_command_permissions(
self,
application_id: Snowflake,
guild_id: Snowflake,
) -> Response[list[interactions.GuildApplicationCommandPermissions]]:
r = Route(
"GET",
"/applications/{application_id}/guilds/{guild_id}/commands/permissions",
application_id=application_id,
guild_id=guild_id,
)
return self.request(r)

# Guild Automod Rules

def get_auto_moderation_rules(
Expand Down Expand Up @@ -2876,6 +2847,8 @@ def delete_followup_message(
)
return self.request(r)

# Application commands (permissions)

def get_guild_application_command_permissions(
self,
application_id: Snowflake,
Expand Down
74 changes: 56 additions & 18 deletions discord/iterators.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from .types.audit_log import AuditLog as AuditLogPayload
from .types.guild import Guild as GuildPayload
from .types.message import Message as MessagePayload
from .types.monetization import Entitlement as EntitlementPayload
from .types.threads import Thread as ThreadPayload
from .types.user import PartialUser as PartialUserPayload
from .user import User
Expand Down Expand Up @@ -988,11 +989,21 @@ def __init__(
self.guild_id = guild_id
self.exclude_ended = exclude_ended

self._filter = None

if self.before and self.after:
self._retrieve_entitlements = self._retrieve_entitlements_before_strategy
self._filter = lambda e: int(e["id"]) > self.after.id
elif self.after:
self._retrieve_entitlements = self._retrieve_entitlements_after_strategy
else:
self._retrieve_entitlements = self._retrieve_entitlements_before_strategy

self.state = state
self.get_entitlements = state.http.list_entitlements
self.entitlements = asyncio.Queue()

async def next(self) -> BanEntry:
async def next(self) -> Entitlement:
if self.entitlements.empty():
await self.fill_entitlements()

Expand All @@ -1014,30 +1025,57 @@ async def fill_entitlements(self):
if not self._get_retrieve():
return

data = await self._retrieve_entitlements(self.retrieve)

if self._filter:
data = list(filter(self._filter, data))

if len(data) < 100:
self.limit = 0 # terminate loop

for element in data:
await self.entitlements.put(Entitlement(data=element, state=self.state))

async def _retrieve_entitlements(self, retrieve) -> list[Entitlement]:
"""Retrieve entitlements and update next parameters."""
raise NotImplementedError

async def _retrieve_entitlements_before_strategy(
self, retrieve: int
) -> list[EntitlementPayload]:
"""Retrieve entitlements using before parameter."""
before = self.before.id if self.before else None
after = self.after.id if self.after else None
data = await self.get_entitlements(
self.state.application_id,
before=before,
after=after,
limit=self.retrieve,
limit=retrieve,
user_id=self.user_id,
guild_id=self.guild_id,
sku_ids=self.sku_ids,
exclude_ended=self.exclude_ended,
)
if data:
if self.limit is not None:
self.limit -= retrieve
self.before = Object(id=int(data[-1]["id"]))
return data

if not data:
# no data, terminate
return

if self.limit:
self.limit -= self.retrieve

if len(data) < 100:
self.limit = 0 # terminate loop

self.after = Object(id=int(data[-1]["id"]))

for element in reversed(data):
await self.entitlements.put(Entitlement(data=element, state=self.state))
async def _retrieve_entitlements_after_strategy(
self, retrieve: int
) -> list[EntitlementPayload]:
"""Retrieve entitlements using after parameter."""
after = self.after.id if self.after else None
data = await self.get_entitlements(
self.state.application_id,
after=after,
limit=retrieve,
user_id=self.user_id,
guild_id=self.guild_id,
sku_ids=self.sku_ids,
exclude_ended=self.exclude_ended,
)
if data:
if self.limit is not None:
self.limit -= retrieve
self.after = Object(id=int(data[-1]["id"]))
return data
8 changes: 5 additions & 3 deletions discord/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from urllib.parse import parse_qs, urlparse

from . import utils
from .channel import PartialMessageable
from .components import _component_factory
from .embeds import Embed
from .emoji import Emoji
Expand Down Expand Up @@ -2001,6 +2002,7 @@ class PartialMessage(Hashable):
- :meth:`DMChannel.get_partial_message`
- :meth:`VoiceChannel.get_partial_message`
- :meth:`StageChannel.get_partial_message`
- :meth:`PartialMessageable.get_partial_message`
Note that this class is trimmed down and has no rich attributes.
Expand All @@ -2022,7 +2024,7 @@ class PartialMessage(Hashable):
Attributes
----------
channel: Union[:class:`TextChannel`, :class:`Thread`, :class:`DMChannel`, :class:`VoiceChannel`, :class:`StageChannel`]
channel: Union[:class:`TextChannel`, :class:`Thread`, :class:`DMChannel`, :class:`VoiceChannel`, :class:`StageChannel`, :class:`PartialMessageable`]
The channel associated with this partial message.
id: :class:`int`
The message ID.
Expand Down Expand Up @@ -2053,9 +2055,9 @@ def __init__(self, *, channel: PartialMessageableChannel, id: int):
ChannelType.news_thread,
ChannelType.public_thread,
ChannelType.private_thread,
):
) and not isinstance(channel, PartialMessageable):
raise TypeError(
"Expected TextChannel, VoiceChannel, StageChannel, DMChannel or Thread not"
"Expected TextChannel, VoiceChannel, StageChannel, DMChannel, Thread or PartialMessageable not"
f" {type(channel)!r}"
)

Expand Down
22 changes: 20 additions & 2 deletions discord/raw_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,17 @@ class RawReactionClearEmojiEvent(_RawReprMixin):
.. versionadded:: 2.5
"""

__slots__ = ("message_id", "channel_id", "guild_id", "emoji", "burst", "data")
__slots__ = (
"message_id",
"channel_id",
"guild_id",
"emoji",
"burst",
"data",
"burst_colours",
"burst_colors",
"type",
)

def __init__(self, data: ReactionClearEmojiEvent, emoji: PartialEmoji) -> None:
self.emoji: PartialEmoji = emoji
Expand Down Expand Up @@ -807,7 +817,15 @@ class RawMessagePollVoteEvent(_RawReprMixin):
The raw data sent by the `gateway <https://discord.com/developers/docs/topics/gateway#message-poll-vote-add>`
"""

__slots__ = ("user_id", "message_id", "channel_id", "guild_id", "data", "added")
__slots__ = (
"user_id",
"message_id",
"answer_id",
"channel_id",
"guild_id",
"data",
"added",
)

def __init__(self, data: MessagePollVoteEvent, added: bool) -> None:
self.user_id: int = int(data["user_id"])
Expand Down
2 changes: 1 addition & 1 deletion discord/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def clear(self, *, views: bool = True) -> None:
self._emojis: dict[int, Emoji] = {}
self._stickers: dict[int, GuildSticker] = {}
self._guilds: dict[int, Guild] = {}
self._polls: dict[int, Guild] = {}
self._polls: dict[int, Poll] = {}
if views:
self._view_store: ViewStore = ViewStore(self)
self._modal_store: ModalStore = ModalStore(self)
Expand Down
6 changes: 3 additions & 3 deletions discord/ui/input_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class InputText:
The maximum number of characters that can be entered.
Must be between 1 and 4000.
required: Optional[:class:`bool`]
Whether the input text field is required or not. Defaults to `True`.
Whether the input text field is required or not. Defaults to ``True``.
value: Optional[:class:`str`]
Pre-fills the input text field with this value.
Must be 4000 characters or fewer.
Expand Down Expand Up @@ -151,7 +151,7 @@ def placeholder(self, value: str | None):

@property
def min_length(self) -> int | None:
"""The minimum number of characters that must be entered. Defaults to `0`."""
"""The minimum number of characters that must be entered. Defaults to 0."""
return self._underlying.min_length

@min_length.setter
Expand All @@ -177,7 +177,7 @@ def max_length(self, value: int | None):

@property
def required(self) -> bool | None:
"""Whether the input text field is required or not. Defaults to `True`."""
"""Whether the input text field is required or not. Defaults to ``True``."""
return self._underlying.required

@required.setter
Expand Down
1 change: 1 addition & 0 deletions discord/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ def decorated(*args: P.args, **kwargs: P.kwargs) -> T:
since=since,
removed=removed,
reference=reference,
stacklevel=stacklevel,
)
return func(*args, **kwargs)

Expand Down
Loading

0 comments on commit 9e8a923

Please sign in to comment.