Skip to content

Commit b0c815f

Browse files
authored
Merge branch 'Pycord-Development:master' into master
2 parents c1ec0b1 + f5683c5 commit b0c815f

File tree

11 files changed

+149
-31
lines changed

11 files changed

+149
-31
lines changed

.github/workflows/todo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
steps:
77
- uses: actions/checkout@v4
88
- name: Run tdg-github-action
9-
uses: ribtoks/tdg-github-action@v0.4.7-beta
9+
uses: ribtoks/tdg-github-action@v0.4.10-beta
1010
with:
1111
TOKEN: ${{ secrets.GITHUB_TOKEN }}
1212
REPO: ${{ github.repository }}

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ci:
44

55
repos:
66
- repo: https://github.com/pre-commit/pre-commit-hooks
7-
rev: v4.5.0
7+
rev: v4.6.0
88
hooks:
99
- id: trailing-whitespace
1010
- id: end-of-file-fixer
@@ -19,7 +19,7 @@ repos:
1919
# - --remove-duplicate-keys
2020
# - --remove-unused-variables
2121
- repo: https://github.com/asottile/pyupgrade
22-
rev: v3.15.1
22+
rev: v3.15.2
2323
hooks:
2424
- id: pyupgrade
2525
args: [--py38-plus]
@@ -28,7 +28,7 @@ repos:
2828
hooks:
2929
- id: isort
3030
- repo: https://github.com/psf/black
31-
rev: 24.3.0
31+
rev: 24.4.0
3232
hooks:
3333
- id: black
3434
args: [--safe, --quiet]

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ These changes are available on the `master` branch, but have not yet been releas
1616
([#2396](https://github.com/Pycord-Development/pycord/pull/2396))
1717
- Added `user` argument to `Paginator.edit`.
1818
([#2390](https://github.com/Pycord-Development/pycord/pull/2390))
19+
- Added `bridge_option` decorator. Required for `bridge.Bot` in 2.7.
20+
([#2417](https://github.com/Pycord-Development/pycord/pull/2417))
21+
- Added `Guild.search_members`.
22+
([#2418](https://github.com/Pycord-Development/pycord/pull/2418))
23+
- Added `member` data to the `raw_reaction_remove` event.
24+
([#2412](https://github.com/Pycord-Development/pycord/pull/2412))
1925

2026
### Fixed
2127

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

3646
### Changed
3747

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

4359
## [2.5.0] - 2024-03-02
4460

discord/cog.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,9 @@ def _get_overridden_method(cls, method: FuncT) -> FuncT | None:
379379
)
380380

381381
@classmethod
382-
def listener(cls, name: str = MISSING) -> Callable[[FuncT], FuncT]:
382+
def listener(
383+
cls, name: str = MISSING, once: bool = False
384+
) -> Callable[[FuncT], FuncT]:
383385
"""A decorator that marks a function as a listener.
384386
385387
This is the cog equivalent of :meth:`.Bot.listen`.
@@ -389,6 +391,9 @@ def listener(cls, name: str = MISSING) -> Callable[[FuncT], FuncT]:
389391
name: :class:`str`
390392
The name of the event being listened to. If not provided, it
391393
defaults to the function's name.
394+
once: :class:`bool`
395+
If this listener should only be called once after each cog load.
396+
Defaults to false.
392397
393398
Raises
394399
------
@@ -411,6 +416,7 @@ def decorator(func: FuncT) -> FuncT:
411416
raise TypeError("Listener function must be a coroutine function.")
412417
actual.__cog_listener__ = True
413418
to_assign = name or actual.__name__
419+
actual._once = once
414420
try:
415421
actual.__cog_listener_names__.append(to_assign)
416422
except AttributeError:

discord/commands/options.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ def to_dict(self) -> dict[str, str | int | float]:
395395
return as_dict
396396

397397

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

410410
def decorator(func):
411-
nonlocal type
412-
type = type or func.__annotations__.get(name, str)
413-
if parameter := kwargs.get("parameter_name"):
414-
func.__annotations__[parameter] = Option(type, name=name, **kwargs)
415-
else:
416-
func.__annotations__[name] = Option(type, **kwargs)
411+
resolved_name = kwargs.pop("parameter_name", None) or name
412+
itype = (
413+
kwargs.pop("type", None)
414+
or input_type
415+
or func.__annotations__.get(resolved_name, str)
416+
)
417+
func.__annotations__[resolved_name] = Option(itype, name=name, **kwargs)
417418
return func
418419

419420
return decorator

discord/ext/bridge/core.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
SlashCommandOptionType,
4141
)
4242

43-
from ...utils import MISSING, find, get
43+
from ...utils import MISSING, find, get, warn_deprecated
4444
from ..commands import BadArgument
4545
from ..commands import Bot as ExtBot
4646
from ..commands import (
@@ -63,6 +63,7 @@
6363
"BridgeCommandGroup",
6464
"bridge_command",
6565
"bridge_group",
66+
"bridge_option",
6667
"BridgeExtCommand",
6768
"BridgeSlashCommand",
6869
"BridgeExtGroup",
@@ -627,3 +628,38 @@ async def convert(self, ctx, argument: str) -> Any:
627628
return converted
628629
except ValueError as exc:
629630
raise BadArgument() from exc
631+
632+
633+
def bridge_option(name, input_type=None, **kwargs):
634+
"""A decorator that can be used instead of typehinting :class:`.BridgeOption`.
635+
636+
.. versionadded:: 2.6
637+
638+
Attributes
639+
----------
640+
parameter_name: :class:`str`
641+
The name of the target parameter this option is mapped to.
642+
This allows you to have a separate UI ``name`` and parameter name.
643+
"""
644+
645+
def decorator(func):
646+
resolved_name = kwargs.pop("parameter_name", None) or name
647+
itype = (
648+
kwargs.pop("type", None)
649+
or input_type
650+
or func.__annotations__.get(resolved_name, str)
651+
)
652+
func.__annotations__[resolved_name] = BridgeOption(itype, name=name, **kwargs)
653+
return func
654+
655+
return decorator
656+
657+
658+
discord.commands.options.Option = BridgeOption
659+
discord.Option = BridgeOption
660+
warn_deprecated(
661+
"Option",
662+
"BridgeOption",
663+
"2.5",
664+
reference="https://github.com/Pycord-Development/pycord/pull/2417",
665+
)

discord/guild.py

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,36 @@ def fetch_members(
20272027

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

2030+
async def search_members(self, query: str, *, limit: int = 1000) -> list[Member]:
2031+
"""Search for guild members whose usernames or nicknames start with the query string. Unlike :meth:`fetch_members`, this does not require :meth:`Intents.members`.
2032+
2033+
.. note::
2034+
2035+
This method is an API call. For general usage, consider filtering :attr:`members` instead.
2036+
2037+
.. versionadded:: 2.6
2038+
2039+
Parameters
2040+
----------
2041+
query: :class:`str`
2042+
Searches for usernames and nicknames that start with this string, case-insensitive.
2043+
limit: :class:`int`
2044+
The maximum number of members to retrieve, up to 1000.
2045+
2046+
Returns
2047+
-------
2048+
List[:class:`Member`]
2049+
The list of members that have matched the query.
2050+
2051+
Raises
2052+
------
2053+
HTTPException
2054+
Getting the members failed.
2055+
"""
2056+
2057+
data = await self._state.http.search_members(self.id, query, limit)
2058+
return [Member(data=m, guild=self, state=self._state) for m in data]
2059+
20302060
async def fetch_member(self, member_id: int, /) -> Member:
20312061
"""|coro|
20322062
@@ -3354,7 +3384,7 @@ async def query_members(
33543384
self,
33553385
query: str | None = None,
33563386
*,
3357-
limit: int = 5,
3387+
limit: int | None = 5,
33583388
user_ids: list[int] | None = None,
33593389
presences: bool = False,
33603390
cache: bool = True,
@@ -3372,22 +3402,22 @@ async def query_members(
33723402
----------
33733403
query: Optional[:class:`str`]
33743404
The string that the username's start with.
3375-
limit: :class:`int`
3376-
The maximum number of members to send back. This must be
3377-
a number between 5 and 100.
3378-
presences: :class:`bool`
3405+
user_ids: Optional[List[:class:`int`]]
3406+
List of user IDs to search for. If the user ID is not in the guild then it won't be returned.
3407+
3408+
.. versionadded:: 1.4
3409+
limit: Optional[:class:`int`]
3410+
The maximum number of members to send back. If no query is passed, passing ``None`` returns all members.
3411+
If a ``query`` or ``user_ids`` is passed, must be between 1 and 100. Defaults to 5.
3412+
presences: Optional[:class:`bool`]
33793413
Whether to request for presences to be provided. This defaults
33803414
to ``False``.
33813415
33823416
.. versionadded:: 1.6
33833417
33843418
cache: :class:`bool`
33853419
Whether to cache the members internally. This makes operations
3386-
such as :meth:`get_member` work for those that matched.
3387-
user_ids: Optional[List[:class:`int`]]
3388-
List of user IDs to search for. If the user ID is not in the guild then it won't be returned.
3389-
3390-
.. versionadded:: 1.4
3420+
such as :meth:`get_member` work for those that matched. Defaults to ``True``.
33913421
33923422
Returns
33933423
-------
@@ -3407,20 +3437,25 @@ async def query_members(
34073437
if presences and not self._state._intents.presences:
34083438
raise ClientException("Intents.presences must be enabled to use this.")
34093439

3410-
if query is None:
3411-
if query == "":
3412-
raise ValueError("Cannot pass empty query string.")
3440+
if not limit or limit > 100 or limit < 1:
3441+
if query or user_ids:
3442+
raise ValueError(
3443+
"limit must be between 1 and 100 when using query or user_ids"
3444+
)
3445+
if not limit:
3446+
query = ""
3447+
limit = 0
34133448

3449+
if query is None:
34143450
if user_ids is None:
3415-
raise ValueError("Must pass either query or user_ids")
3451+
raise ValueError("Must pass query or user_ids, or set limit to None")
34163452

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

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

3423-
limit = min(100, limit or 5)
34243459
return await self._state.query_members(
34253460
self,
34263461
query=query,

discord/http.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,20 @@ def get_members(
16361636
r = Route("GET", "/guilds/{guild_id}/members", guild_id=guild_id)
16371637
return self.request(r, params=params)
16381638

1639+
def search_members(
1640+
self,
1641+
guild_id: Snowflake,
1642+
query: str,
1643+
limit: int,
1644+
) -> Response[list[member.MemberWithUser]]:
1645+
params: dict[str, Any] = {
1646+
"query": query,
1647+
"limit": limit,
1648+
}
1649+
1650+
r = Route("GET", "/guilds/{guild_id}/members/search", guild_id=guild_id)
1651+
return self.request(r, params=params)
1652+
16391653
def get_member(
16401654
self, guild_id: Snowflake, member_id: Snowflake
16411655
) -> Response[member.MemberWithUser]:

discord/interactions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def _from_data(self, data: InteractionPayload):
198198
self._guild: Guild | None = None
199199
self._guild_data = data.get("guild")
200200
if self.guild is None and self._guild_data:
201-
self._guild = Guild(data=self._guild_data, state=self)
201+
self._guild = Guild(data=self._guild_data, state=self._state)
202202

203203
# TODO: there's a potential data loss here
204204
if self.guild_id:

discord/raw_models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,7 @@ class RawReactionActionEvent(_RawReprMixin):
205205
emoji: :class:`PartialEmoji`
206206
The custom or unicode emoji being used.
207207
member: Optional[:class:`Member`]
208-
The member who added the reaction. Only available if `event_type` is `REACTION_ADD`
209-
and the reaction is inside a guild.
208+
The member who added the reaction. Only available if the reaction occurs within a guild.
210209
211210
.. versionadded:: 1.3
212211

0 commit comments

Comments
 (0)