Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ These changes are available on the `master` branch, but have not yet been releas

- Added `Member.vr_status` property.
([#3328](https://github.com/Pycord-Development/pycord/pull/3328))
- Added `Guild.fetch_voice_regions()` method to retrieve the currently available voice
regions for the guild.
([#3347](https://github.com/Pycord-Development/pycord/pull/3347))

### Changed

- The `rtc_region` parameters of channel creation and edit methods now also accept a
region ID `str` in addition to a `VoiceRegion` member.
([#3347](https://github.com/Pycord-Development/pycord/pull/3347))

### Fixed

- Fix `TypeError` when accessing `ApplicationCommand.guild_only` or
Expand All @@ -27,6 +34,10 @@ These changes are available on the `master` branch, but have not yet been releas

### Deprecated

- Deprecated the `VoiceRegion` enum in favor of the region ID `str` or
`Guild.fetch_voice_regions()`.
([#3347](https://github.com/Pycord-Development/pycord/pull/3347))

### Removed

## [2.8.1] - 2026-07-25
Expand Down
24 changes: 18 additions & 6 deletions discord/channel.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2091,7 +2091,7 @@ async def edit(
sync_permissions: int = ...,
category: CategoryChannel | None = ...,
overwrites: Mapping[Role | Member, PermissionOverwrite] = ...,
rtc_region: VoiceRegion | None = ...,
rtc_region: VoiceRegion | str | None = ...,
video_quality_mode: VideoQualityMode = ...,
slowmode_delay: int = ...,
nsfw: bool = ...,
Expand Down Expand Up @@ -2135,10 +2135,16 @@ async def edit(self, *, reason=None, **options):
The reason for editing this channel. Shows up on the audit log.
overwrites: Dict[Union[:class:`Role`, :class:`Member`, :class:`~discord.abc.Snowflake`], :class:`PermissionOverwrite`]
The overwrites to apply to channel permissions. Useful for creating secret channels.
rtc_region: Optional[:class:`VoiceRegion`]
The new region for the voice channel's voice communication.
rtc_region: Optional[Union[:class:`str`, :class:`VoiceRegion`]]
Comment thread
vmphase marked this conversation as resolved.
The new region ID for the voice channel's voice communication.
A value of ``None`` indicates automatic voice region detection.

.. versionchanged:: 2.9

A :class:`VoiceRegion` member is still accepted, but it is
deprecated in favor of the region ID :class:`str`, which
can be retrieved via :meth:`Guild.fetch_voice_regions`.

.. versionadded:: 1.7
video_quality_mode: :class:`VideoQualityMode`
The camera video quality for the voice channel's participants.
Expand Down Expand Up @@ -2778,7 +2784,7 @@ async def edit(
sync_permissions: int = ...,
category: CategoryChannel | None = ...,
overwrites: Mapping[Role | Member, PermissionOverwrite] = ...,
rtc_region: VoiceRegion | None = ...,
rtc_region: VoiceRegion | str | None = ...,
video_quality_mode: VideoQualityMode = ...,
reason: str | None = ...,
) -> StageChannel | None: ...
Expand Down Expand Up @@ -2816,9 +2822,15 @@ async def edit(self, *, reason=None, **options):
The reason for editing this channel. Shows up on the audit log.
overwrites: Dict[Union[:class:`Role`, :class:`Member`, :class:`~discord.abc.Snowflake`], :class:`PermissionOverwrite`]
The overwrites to apply to channel permissions. Useful for creating secret channels.
rtc_region: Optional[:class:`VoiceRegion`]
The new region for the stage channel's voice communication.
rtc_region: Optional[Union[:class:`str`, :class:`VoiceRegion`]]
Comment thread
vmphase marked this conversation as resolved.
The new region ID for the stage channel's voice communication.
A value of ``None`` indicates automatic voice region detection.

.. versionchanged:: 2.9

A :class:`VoiceRegion` member is still accepted, but it is
Comment thread
vmphase marked this conversation as resolved.
deprecated in favor of the region ID :class:`str`, which
can be retrieved via :meth:`Guild.fetch_voice_regions`.
video_quality_mode: :class:`VideoQualityMode`
The camera video quality for the stage channel's participants.

Expand Down
88 changes: 67 additions & 21 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,23 @@

import types
from collections import namedtuple
from collections.abc import Callable
from enum import IntEnum
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, Union

from .utils import warn_deprecated

if TYPE_CHECKING:
from typing_extensions import deprecated
else:

def deprecated(message: str) -> Callable[[T], T]:
def decorator(value: T) -> T:
return value

return decorator


__all__ = (
"Enum",
"ChannelType",
Expand Down Expand Up @@ -286,32 +300,64 @@ class MessageType(Enum):
poll_result = 46


class VoiceRegion(Enum):
"""Voice region"""
class _VoiceRegionMeta(Enum.__class__):
def _warn(self, label: str) -> None:
warn_deprecated(
label,
instead="the region ID string or Guild.fetch_voice_regions()",
since="2.9",
removed="3.0",
stacklevel=4,
)

def __getattribute__(cls, name: str) -> Any:
members = super().__getattribute__("_enum_member_map_")
if name in members:
cls._warn(f"VoiceRegion.{name}")
return super().__getattribute__(name)

def __getitem__(cls, name: str) -> Any:
member = super().__getitem__(name)
cls._warn(f"VoiceRegion[{name!r}]")
return member

def __call__(cls, *args: Any, **kwargs: Any) -> Any:
cls._warn(f"VoiceRegion({', '.join(map(repr, args))})")
return super().__call__(*args, **kwargs)

def __getattr__(cls, name: str) -> Any:
if name.startswith("_"):
raise AttributeError(name)
cls._warn(f"VoiceRegion.{name}")
return create_unknown_value(cls, name)


@deprecated(
"VoiceRegion is deprecated in favour of the region ID str or "
"Guild.fetch_voice_regions() since version 2.9, and will be removed in version 3.0."
)
class VoiceRegion(Enum, metaclass=_VoiceRegionMeta):
Comment thread
vmphase marked this conversation as resolved.
"""Specifies the region a voice server belongs to.

.. deprecated:: 2.9
The list of voice regions is dynamic, so this enum is deprecated in favor
of the region ID :class:`str`, which can be retrieved via
:meth:`Guild.fetch_voice_regions`, and will be removed in version 3.0.
"""

us_west = "us-west"
us_east = "us-east"
us_south = "us-south"
us_central = "us-central"
eu_west = "eu-west"
eu_central = "eu-central"
singapore = "singapore"
london = "london"
sydney = "sydney"
amsterdam = "amsterdam"
frankfurt = "frankfurt"
brazil = "brazil"
hongkong = "hongkong"
russia = "russia"
india = "india"
japan = "japan"
southafrica = "southafrica"
rotterdam = "rotterdam"
singapore = "singapore"
south_korea = "south-korea"
india = "india"
europe = "europe"
dubai = "dubai"
vip_us_east = "vip-us-east"
vip_us_west = "vip-us-west"
vip_amsterdam = "vip-amsterdam"
southafrica = "southafrica"
sydney = "sydney"
us_central = "us-central"
us_east = "us-east"
us_south = "us-south"
us_west = "us-west"

Comment thread
vmphase marked this conversation as resolved.
def __str__(self):
return self.value
Expand Down
78 changes: 72 additions & 6 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import datetime
import unicodedata
from collections.abc import Sequence
from dataclasses import dataclass
from typing import (
TYPE_CHECKING,
Any,
Expand Down Expand Up @@ -147,6 +148,22 @@ class _GuildLimit(NamedTuple):
filesize: int


@dataclass(frozen=True, slots=True)
class VoiceServerRegion:
"""Represents a voice region a guild can use for voice channels.

This is returned by :meth:`Guild.fetch_voice_regions`.
Comment thread
vmphase marked this conversation as resolved.

.. versionadded:: 2.9
"""

id: str
name: str
optimal: bool
deprecated: bool
custom: bool


class GuildRoleCounts(dict[int, int]):
"""A dictionary subclass that maps role IDs to their member counts.

Expand Down Expand Up @@ -1602,7 +1619,7 @@ async def create_voice_channel(
position: int = MISSING,
bitrate: int = MISSING,
user_limit: int = MISSING,
rtc_region: VoiceRegion | None = MISSING,
rtc_region: VoiceRegion | str | None = MISSING,
video_quality_mode: VideoQualityMode = MISSING,
overwrites: dict[Role | Member, PermissionOverwrite] = MISSING,
slowmode_delay: int = MISSING,
Expand All @@ -1629,10 +1646,16 @@ async def create_voice_channel(
The channel's preferred audio bitrate in bits per second.
user_limit: :class:`int`
The channel's limit for number of members that can be in a voice channel.
rtc_region: Optional[:class:`VoiceRegion`]
The region for the voice channel's voice communication.
rtc_region: Optional[Union[:class:`str`, :class:`VoiceRegion`]]
Comment thread
vmphase marked this conversation as resolved.
The region ID for the voice channel's voice communication.
A value of ``None`` indicates automatic voice region detection.

.. versionchanged:: 2.9
Comment thread
vmphase marked this conversation as resolved.

A :class:`VoiceRegion` member is still accepted, but it is
deprecated in favor of the region ID :class:`str`, which
can be retrieved via :meth:`Guild.fetch_voice_regions`.

.. versionadded:: 1.7
video_quality_mode: :class:`VideoQualityMode`
The camera video quality for the voice channel's participants.
Expand Down Expand Up @@ -1713,7 +1736,7 @@ async def create_stage_channel(
reason: str | None = None,
bitrate: int = MISSING,
user_limit: int = MISSING,
rtc_region: VoiceRegion | None = MISSING,
rtc_region: VoiceRegion | str | None = MISSING,
video_quality_mode: VideoQualityMode = MISSING,
slowmode_delay: int = MISSING,
nsfw: bool = MISSING,
Expand Down Expand Up @@ -1752,10 +1775,16 @@ async def create_stage_channel(

.. versionadded:: 2.7

rtc_region: Optional[:class:`VoiceRegion`]
The region for the voice channel's voice communication.
rtc_region: Optional[Union[:class:`str`, :class:`VoiceRegion`]]
The region ID for the voice channel's voice communication.
A value of ``None`` indicates automatic voice region detection.

.. versionchanged:: 2.9

A :class:`VoiceRegion` member is still accepted, but it is
deprecated in favor of the region ID :class:`str`, which
can be retrieved via :meth:`Guild.fetch_voice_regions`.

Comment thread
vmphase marked this conversation as resolved.
.. versionadded:: 2.7

video_quality_mode: :class:`VideoQualityMode`
Expand Down Expand Up @@ -3786,6 +3815,43 @@ async def vanity_invite(self) -> Invite | None:
payload["uses"] = payload.get("uses", 0)
return Invite(state=self._state, data=payload, guild=self, channel=channel)

async def fetch_voice_regions(self) -> list[VoiceServerRegion]:
"""|coro|

Retrieves the voice regions that the guild has access to.

The list of voice regions is dynamic, so this method is the
recommended way to get the currently available regions.

.. versionadded:: 2.9

Each :class:`~discord.guild.VoiceServerRegion` has the following attributes:

:attr:`~discord.guild.VoiceServerRegion.id`
The region ID, e.g. ``"us-west"``. Use this as the
:attr:`~discord.VoiceChannel.rtc_region` of a voice channel.
:attr:`~discord.guild.VoiceServerRegion.name`
The region's display name, e.g. ``"US West"``.
:attr:`~discord.guild.VoiceServerRegion.optimal`
Whether the region is optimal for the guild's members.
:attr:`~discord.guild.VoiceServerRegion.deprecated`
Whether the region is deprecated.
:attr:`~discord.guild.VoiceServerRegion.custom`
Whether the region is a custom region.

Returns
-------
List[:class:`~discord.guild.VoiceServerRegion`]
The list of voice regions the guild has access to.

Raises
------
HTTPException
Retrieving the voice regions failed.
"""
regions = await self._state.http.get_guild_voice_regions(self.id)
return [VoiceServerRegion(**region) for region in regions]

# TODO: use MISSING when async iterators get refactored
def audit_logs(
self,
Expand Down
12 changes: 12 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
)
from .types.snowflake import Snowflake, SnowflakeList
from .types.soundboard import SoundboardSound as SoundboardSoundPayload
from .types.voice import VoiceRegion as VoiceRegionPayload

T = TypeVar("T")
BE = TypeVar("BE", bound=BaseException)
Expand Down Expand Up @@ -1043,6 +1044,17 @@ def guild_voice_state(

return self.request(r, json=payload, reason=reason)

def get_guild_voice_regions(
self,
guild_id: Snowflake,
) -> Response[list[VoiceRegionPayload]]:
return self.request(
Route("GET", "/guilds/{guild_id}/regions", guild_id=guild_id)
)

def get_voice_regions(self) -> Response[list[VoiceRegionPayload]]:
return self.request(Route("GET", "/voice/regions"))

def edit_profile(self, payload: dict[str, Any]) -> Response[user.User]:
return self.request(Route("PATCH", "/users/@me"), json=payload)

Expand Down
Loading