@@ -107,6 +107,8 @@ def __init__(
107107 self ._args : list [Any ] = []
108108 self ._kwargs : dict [str , Any ] = {}
109109
110+ self ._prepare_called : bool = False
111+
110112 @property
111113 def message (self ) -> ChatMessage | None :
112114 """Property returning the :class:`~twitchio.ChatMessage` that this :class:`~.commands.Context` was
@@ -343,7 +345,10 @@ async def _get_prefix(self) -> None:
343345
344346 self ._validate_prefix (potential )
345347
346- def _get_command (self ) -> None :
348+ def _get_command (self , reset : bool = False ) -> None :
349+ if self ._prepare_called :
350+ return
351+
347352 commands = self ._bot ._commands
348353
349354 if isinstance (self ._payload , (ChannelPointsRedemptionAdd , ChannelPointsRedemptionUpdate )):
@@ -361,17 +366,51 @@ def _get_command(self) -> None:
361366 self ._invoked_with = next_
362367 command = commands .get (next_ )
363368
369+ if reset :
370+ self ._view .reset ()
371+
364372 if not command :
365373 return
366374
367375 self ._command = command
368376 return
369377
370- async def _prepare (self ) -> None :
378+ async def fetch_command (self ) -> Command [Any , ...] | RewardCommand [Any , ...] | None :
379+ """|coro|
380+
381+ Method which validates the prefix and finds and returns the command that would be invoked or ``None`` if no valid
382+ command can be found.
383+
384+ If no valid ``prefix`` is found this method will always return ``None``.
385+
386+ .. note::
387+
388+ Unlike :meth:`.prepare` this method resets the state of the internal string-view used to find the ``prefix``,
389+ ``command`` and ``arguments`` and can be safely used at anytime.
390+
391+ .. versionadded:: 3.1
392+
393+ Returns
394+ -------
395+ :class:`twitchio.ext.commands.Command` | :class:`twitchio.ext.commands.RewardCommand` | None
396+ The :class:`twitchio.ext.commands.Command` or :class:`twitchio.ext.commands.RewardCommand` if found alongside
397+ a valid prefix, otherwise ``None``.
398+ """
399+ try :
400+ await self ._prepare (reset = True )
401+ except PrefixError :
402+ return
403+
404+ return self ._command
405+
406+ async def _prepare (self , reset : bool = False ) -> None :
371407 if isinstance (self ._payload , ChatMessage ):
372408 await self ._get_prefix ()
373409
374- self ._get_command ()
410+ self ._get_command (reset = reset )
411+
412+ if reset is False :
413+ self ._prepare_called = True
375414
376415 async def prepare (self ) -> None :
377416 """|coro|
@@ -381,6 +420,10 @@ async def prepare(self) -> None:
381420 This coroutine is called automatically in :meth:`.invoke` before anything else.
382421
383422 Could be overriden to add extra setup before command invocation.
423+
424+ .. note::
425+
426+ If this method is overriden you should call `await super().prepare()` to ensure correct setup of the context.
384427 """
385428 await self ._prepare ()
386429
0 commit comments