Skip to content

Commit

Permalink
Merge branch 'Pycord-Development:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
plun1331 authored Apr 21, 2024
2 parents c1ec0b1 + f5683c5 commit b0c815f
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Run tdg-github-action
uses: ribtoks/[email protected].7-beta
uses: ribtoks/[email protected].10-beta
with:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ci:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -19,7 +19,7 @@ repos:
# - --remove-duplicate-keys
# - --remove-unused-variables
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.1
rev: v3.15.2
hooks:
- id: pyupgrade
args: [--py38-plus]
Expand All @@ -28,7 +28,7 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 24.3.0
rev: 24.4.0
hooks:
- id: black
args: [--safe, --quiet]
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ These changes are available on the `master` branch, but have not yet been releas
([#2396](https://github.com/Pycord-Development/pycord/pull/2396))
- Added `user` argument to `Paginator.edit`.
([#2390](https://github.com/Pycord-Development/pycord/pull/2390))
- Added `bridge_option` decorator. Required for `bridge.Bot` in 2.7.
([#2417](https://github.com/Pycord-Development/pycord/pull/2417))
- Added `Guild.search_members`.
([#2418](https://github.com/Pycord-Development/pycord/pull/2418))
- Added `member` data to the `raw_reaction_remove` event.
([#2412](https://github.com/Pycord-Development/pycord/pull/2412))

### Fixed

Expand All @@ -32,13 +38,23 @@ These changes are available on the `master` branch, but have not yet been releas
([#2400](https://github.com/Pycord-Development/pycord/pull/2400))
- Fixed `ScheduledEvent.subscribers` behavior with `limit=None`.
([#2407](https://github.com/Pycord-Development/pycord/pull/2407))
- Fixed invalid data being passed to `Interaction._guild` in certain cases.
([#2411](https://github.com/Pycord-Development/pycord/pull/2411))
- Fixed option typehints being ignored when using `parameter_name`.
([#2417](https://github.com/Pycord-Development/pycord/pull/2417))

### Changed

- Changed the type of `Guild.bitrate_limit` to `int`.
([#2387](https://github.com/Pycord-Development/pycord/pull/2387))
- HTTP requests that fail with a 503 status are now re-tried.
([#2395](https://github.com/Pycord-Development/pycord/pull/2395))
- `option` decorator now accepts `input_type`.
([#2417](https://github.com/Pycord-Development/pycord/pull/2417))
- `Option` may be used instead of `BridgeOption` until 2.7.
([#2417](https://github.com/Pycord-Development/pycord/pull/2417))
- `Guild.query_members` now accepts `limit=None` to retrieve all members.
([#2419](https://github.com/Pycord-Development/pycord/pull/2419))

## [2.5.0] - 2024-03-02

Expand Down
8 changes: 7 additions & 1 deletion discord/cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ def _get_overridden_method(cls, method: FuncT) -> FuncT | None:
)

@classmethod
def listener(cls, name: str = MISSING) -> Callable[[FuncT], FuncT]:
def listener(
cls, name: str = MISSING, once: bool = False
) -> Callable[[FuncT], FuncT]:
"""A decorator that marks a function as a listener.
This is the cog equivalent of :meth:`.Bot.listen`.
Expand All @@ -389,6 +391,9 @@ def listener(cls, name: str = MISSING) -> Callable[[FuncT], FuncT]:
name: :class:`str`
The name of the event being listened to. If not provided, it
defaults to the function's name.
once: :class:`bool`
If this listener should only be called once after each cog load.
Defaults to false.
Raises
------
Expand All @@ -411,6 +416,7 @@ def decorator(func: FuncT) -> FuncT:
raise TypeError("Listener function must be a coroutine function.")
actual.__cog_listener__ = True
to_assign = name or actual.__name__
actual._once = once
try:
actual.__cog_listener_names__.append(to_assign)
except AttributeError:
Expand Down
15 changes: 8 additions & 7 deletions discord/commands/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def to_dict(self) -> dict[str, str | int | float]:
return as_dict


def option(name, type=None, **kwargs):
def option(name, input_type=None, **kwargs):
"""A decorator that can be used instead of typehinting :class:`.Option`.
.. versionadded:: 2.0
Expand All @@ -408,12 +408,13 @@ def option(name, type=None, **kwargs):
"""

def decorator(func):
nonlocal type
type = type or func.__annotations__.get(name, str)
if parameter := kwargs.get("parameter_name"):
func.__annotations__[parameter] = Option(type, name=name, **kwargs)
else:
func.__annotations__[name] = Option(type, **kwargs)
resolved_name = kwargs.pop("parameter_name", None) or name
itype = (
kwargs.pop("type", None)
or input_type
or func.__annotations__.get(resolved_name, str)
)
func.__annotations__[resolved_name] = Option(itype, name=name, **kwargs)
return func

return decorator
38 changes: 37 additions & 1 deletion discord/ext/bridge/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
SlashCommandOptionType,
)

from ...utils import MISSING, find, get
from ...utils import MISSING, find, get, warn_deprecated
from ..commands import BadArgument
from ..commands import Bot as ExtBot
from ..commands import (
Expand All @@ -63,6 +63,7 @@
"BridgeCommandGroup",
"bridge_command",
"bridge_group",
"bridge_option",
"BridgeExtCommand",
"BridgeSlashCommand",
"BridgeExtGroup",
Expand Down Expand Up @@ -627,3 +628,38 @@ async def convert(self, ctx, argument: str) -> Any:
return converted
except ValueError as exc:
raise BadArgument() from exc


def bridge_option(name, input_type=None, **kwargs):
"""A decorator that can be used instead of typehinting :class:`.BridgeOption`.
.. versionadded:: 2.6
Attributes
----------
parameter_name: :class:`str`
The name of the target parameter this option is mapped to.
This allows you to have a separate UI ``name`` and parameter name.
"""

def decorator(func):
resolved_name = kwargs.pop("parameter_name", None) or name
itype = (
kwargs.pop("type", None)
or input_type
or func.__annotations__.get(resolved_name, str)
)
func.__annotations__[resolved_name] = BridgeOption(itype, name=name, **kwargs)
return func

return decorator


discord.commands.options.Option = BridgeOption
discord.Option = BridgeOption
warn_deprecated(
"Option",
"BridgeOption",
"2.5",
reference="https://github.com/Pycord-Development/pycord/pull/2417",
)
65 changes: 50 additions & 15 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,36 @@ def fetch_members(

return MemberIterator(self, limit=limit, after=after)

async def search_members(self, query: str, *, limit: int = 1000) -> list[Member]:
"""Search for guild members whose usernames or nicknames start with the query string. Unlike :meth:`fetch_members`, this does not require :meth:`Intents.members`.
.. note::
This method is an API call. For general usage, consider filtering :attr:`members` instead.
.. versionadded:: 2.6
Parameters
----------
query: :class:`str`
Searches for usernames and nicknames that start with this string, case-insensitive.
limit: :class:`int`
The maximum number of members to retrieve, up to 1000.
Returns
-------
List[:class:`Member`]
The list of members that have matched the query.
Raises
------
HTTPException
Getting the members failed.
"""

data = await self._state.http.search_members(self.id, query, limit)
return [Member(data=m, guild=self, state=self._state) for m in data]

async def fetch_member(self, member_id: int, /) -> Member:
"""|coro|
Expand Down Expand Up @@ -3354,7 +3384,7 @@ async def query_members(
self,
query: str | None = None,
*,
limit: int = 5,
limit: int | None = 5,
user_ids: list[int] | None = None,
presences: bool = False,
cache: bool = True,
Expand All @@ -3372,22 +3402,22 @@ async def query_members(
----------
query: Optional[:class:`str`]
The string that the username's start with.
limit: :class:`int`
The maximum number of members to send back. This must be
a number between 5 and 100.
presences: :class:`bool`
user_ids: Optional[List[:class:`int`]]
List of user IDs to search for. If the user ID is not in the guild then it won't be returned.
.. versionadded:: 1.4
limit: Optional[:class:`int`]
The maximum number of members to send back. If no query is passed, passing ``None`` returns all members.
If a ``query`` or ``user_ids`` is passed, must be between 1 and 100. Defaults to 5.
presences: Optional[:class:`bool`]
Whether to request for presences to be provided. This defaults
to ``False``.
.. versionadded:: 1.6
cache: :class:`bool`
Whether to cache the members internally. This makes operations
such as :meth:`get_member` work for those that matched.
user_ids: Optional[List[:class:`int`]]
List of user IDs to search for. If the user ID is not in the guild then it won't be returned.
.. versionadded:: 1.4
such as :meth:`get_member` work for those that matched. Defaults to ``True``.
Returns
-------
Expand All @@ -3407,20 +3437,25 @@ async def query_members(
if presences and not self._state._intents.presences:
raise ClientException("Intents.presences must be enabled to use this.")

if query is None:
if query == "":
raise ValueError("Cannot pass empty query string.")
if not limit or limit > 100 or limit < 1:
if query or user_ids:
raise ValueError(
"limit must be between 1 and 100 when using query or user_ids"
)
if not limit:
query = ""
limit = 0

if query is None:
if user_ids is None:
raise ValueError("Must pass either query or user_ids")
raise ValueError("Must pass query or user_ids, or set limit to None")

if user_ids is not None and query is not None:
raise ValueError("Cannot pass both query and user_ids")

if user_ids is not None and not user_ids:
raise ValueError("user_ids must contain at least 1 value")

limit = min(100, limit or 5)
return await self._state.query_members(
self,
query=query,
Expand Down
14 changes: 14 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,20 @@ def get_members(
r = Route("GET", "/guilds/{guild_id}/members", guild_id=guild_id)
return self.request(r, params=params)

def search_members(
self,
guild_id: Snowflake,
query: str,
limit: int,
) -> Response[list[member.MemberWithUser]]:
params: dict[str, Any] = {
"query": query,
"limit": limit,
}

r = Route("GET", "/guilds/{guild_id}/members/search", guild_id=guild_id)
return self.request(r, params=params)

def get_member(
self, guild_id: Snowflake, member_id: Snowflake
) -> Response[member.MemberWithUser]:
Expand Down
2 changes: 1 addition & 1 deletion discord/interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def _from_data(self, data: InteractionPayload):
self._guild: Guild | None = None
self._guild_data = data.get("guild")
if self.guild is None and self._guild_data:
self._guild = Guild(data=self._guild_data, state=self)
self._guild = Guild(data=self._guild_data, state=self._state)

# TODO: there's a potential data loss here
if self.guild_id:
Expand Down
3 changes: 1 addition & 2 deletions discord/raw_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,7 @@ class RawReactionActionEvent(_RawReprMixin):
emoji: :class:`PartialEmoji`
The custom or unicode emoji being used.
member: Optional[:class:`Member`]
The member who added the reaction. Only available if `event_type` is `REACTION_ADD`
and the reaction is inside a guild.
The member who added the reaction. Only available if the reaction occurs within a guild.
.. versionadded:: 1.3
Expand Down
11 changes: 11 additions & 0 deletions discord/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,17 @@ def parse_message_reaction_remove(self, data) -> None:
emoji_id = utils._get_as_snowflake(emoji, "id")
emoji = PartialEmoji.with_state(self, id=emoji_id, name=emoji["name"])
raw = RawReactionActionEvent(data, emoji, "REACTION_REMOVE")

member_data = data.get("member")
if member_data:
guild = self._get_guild(raw.guild_id)
if guild is not None:
raw.member = Member(data=member_data, guild=guild, state=self)
else:
raw.member = None
else:
raw.member = None

self.dispatch("raw_reaction_remove", raw)

message = self._get_message(raw.message_id)
Expand Down

0 comments on commit b0c815f

Please sign in to comment.