Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3c61693
feat: deprecate VoiceRegion enum in favour of dynamic voice regions
vmphase Aug 11, 2026
ddd0d1b
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2026
b9e9c6c
feat(enums): deprecate VoiceRegion enum for LSP; warn on VoiceRegion("")
vmphase Aug 11, 2026
19dfe2a
feat(enums): unknown attribute fallback
vmphase Aug 11, 2026
53eb128
feat: make rtc_region paramater accept str (region id) for channel
vmphase Aug 11, 2026
12acd61
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2026
2e474dc
refactor(enums): import ordering
vmphase Aug 11, 2026
0a47380
Update discord/guild.py
vmphase Aug 11, 2026
9faa308
refactor(types): make VoiceRegion a dataclass
vmphase Aug 11, 2026
0927a51
refactor(regions): apply review suggestions
vmphase Aug 11, 2026
fe66309
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2026
fd2b819
chore: rename VoiceRegion to VoiceRegionType for consistency with
vmphase Aug 11, 2026
74d6623
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2026
defeffb
refactor: change dataclass naming
vmphase Aug 11, 2026
82bf07f
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2026
3278b89
refactor: apply code review suggestions
vmphase Aug 12, 2026
4cc7b32
Merge branch 'master' into feat/dynamic-voice-regions
vmphase Aug 31, 2026
db041ba
chore: address wording review
vmphase Sep 11, 2026
bbdbba5
Merge branch 'master' into feat/dynamic-voice-regions
vmphase Sep 12, 2026
62f1497
Merge branch 'master' into feat/dynamic-voice-regions
vmphase Sep 18, 2026
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 @@ -16,9 +16,16 @@ These changes are available on the `master` branch, but have not yet been releas
([#3328](https://github.com/Pycord-Development/pycord/pull/3328))
- Added `SlashCommandGroup.add_command`.
([#3346](https://github.com/Pycord-Development/pycord/pull/3346))
- 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 @@ -32,6 +39,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
20 changes: 14 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,14 @@ 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 now accepted.

.. versionadded:: 1.7
video_quality_mode: :class:`VideoQualityMode`
The camera video quality for the voice channel's participants.
Expand Down Expand Up @@ -2778,7 +2782,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 +2820,13 @@ 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 now accepted.
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 since version 2.9 and will be removed in version 3.0. "
"Use Guild.fetch_voice_regions() to retrieve available regions as a list of VoiceServerRegion objects."
)
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.
Use :meth:`Guild.fetch_voice_regions` to retrieve available regions
as a list of :class:`VoiceServerRegion` objects. This enum 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
74 changes: 68 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,14 @@ 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 now accepted.

.. versionadded:: 1.7
video_quality_mode: :class:`VideoQualityMode`
The camera video quality for the voice channel's participants.
Expand Down Expand Up @@ -1713,7 +1734,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 +1773,14 @@ 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 now accepted.

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

video_quality_mode: :class:`VideoQualityMode`
Expand Down Expand Up @@ -3786,6 +3811,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
Loading