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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ These changes are available on the `master` branch, but have not yet been releas

### Added

- Add `typing` context manager for Bridge `Context`s.
([#3251](https://github.com/Pycord-Development/pycord/pull/3251))

### Changed

### Fixed
Expand Down
65 changes: 64 additions & 1 deletion discord/ext/bridge/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,66 @@

from __future__ import annotations

import asyncio
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Union, overload

from typing_extensions import Self

from discord.commands import ApplicationContext
from discord.context_managers import Typing
from discord.interactions import Interaction, InteractionMessage
from discord.message import Message
from discord.webhook import WebhookMessage

from ..commands import Context

if TYPE_CHECKING:
from types import TracebackType

from .core import BridgeExtCommand, BridgeSlashCommand


__all__ = ("BridgeContext", "BridgeExtContext", "BridgeApplicationContext", "Context")
__all__ = (
"BridgeContext",
"BridgeExtContext",
"BridgeApplicationContext",
"Context",
"DeferTyping",
)


class DeferTyping:
def __init__(self, ctx: BridgeApplicationContext, ephemeral: bool) -> None:
self.loop: asyncio.AbstractEventLoop = ctx._state.loop
self.ctx = ctx
self.ephemeral = ephemeral

async def do_defer(self) -> None:
await self.ctx.defer(ephemeral=self.ephemeral)

def __enter__(self) -> Self:
self.task: asyncio.Task = self.loop.create_task(self.do_defer())
return self

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.task.cancel()

async def __aenter__(self) -> Self:
return self.__enter__()

async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
return self.__exit__(exc_type, exc_value, traceback)


class BridgeContext(ABC):
Expand Down Expand Up @@ -74,6 +119,9 @@ async def _respond(
@abstractmethod
async def _defer(self, *args, **kwargs) -> None: ...

@abstractmethod
def _typing(self, *args, **kwargs) -> Typing | DeferTyping: ...

@abstractmethod
async def _edit(self, *args, **kwargs) -> InteractionMessage | Message: ...

Expand Down Expand Up @@ -111,6 +159,14 @@ async def defer(self, *args, **kwargs) -> None:
"""
return await self._defer(*args, **kwargs)

def typing(self, *args, **kwargs) -> Typing | DeferTyping:
"""
Returns a context manager that allows you to type for an indefinite period of time.
In :class:`BridgeExtContext`, this will be :meth:`~Context.typing` while in :class:`BridgeApplicationContext`,
this is equivalent to a defer() call and does not do any typing calls.
"""
return self._typing(*args, **kwargs)
Comment on lines +162 to +168

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need both typing and _typing ?

@vmphase vmphase Jun 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the template method pattern already used by respond, defer and others throughout the file.


async def edit(self, *args, **kwargs) -> InteractionMessage | Message:
"""|coro|

Expand Down Expand Up @@ -147,6 +203,9 @@ async def _respond(self, *args, **kwargs) -> Interaction | WebhookMessage:
async def _defer(self, *args, **kwargs) -> None:
return await self._get_super("defer")(*args, **kwargs)

def _typing(self, *args, **kwargs) -> DeferTyping:
return DeferTyping(self, *args, **kwargs)

async def _edit(self, *args, **kwargs) -> InteractionMessage:
return await self._get_super("edit")(*args, **kwargs)

Expand Down Expand Up @@ -174,6 +233,10 @@ async def _defer(self, *args, **kwargs) -> None:
kwargs.pop("ephemeral", None)
return await self._get_super("trigger_typing")(*args, **kwargs)

def _typing(self, *args, **kwargs) -> Typing:
kwargs.pop("ephemeral", None)
return self._get_super("typing")(*args, **kwargs)

async def _edit(self, *args, **kwargs) -> Message | None:
if self._original_response_message:
return await self._original_response_message.edit(*args, **kwargs)
Expand Down
5 changes: 4 additions & 1 deletion docs/ext/bridge/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ BridgeContext

.. autoclass:: discord.ext.bridge.BridgeContext
:members:
:exclude-members: _respond, _defer, _edit, _get_super
:exclude-members: _respond, _defer, _typing, _edit, _get_super, typing

.. automethod:: typing
:async-with:

BridgeContext Subclasses
~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Loading