Add datetime converter and util #54
@ -26,6 +26,7 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
import inspect
|
||||
import datetime
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
@ -823,7 +824,23 @@ class PartialEmojiConverter(Converter[discord.PartialEmoji]):
|
||||
|
||||
raise PartialEmojiConversionFailure(argument)
|
||||
|
||||
class FormattedDatetimeConverter(Converter[Tuple[datetime.datetime, Optional[str]]]):
|
||||
|
||||
"""Converts a discord style datetime to a :class:`datetime.datetime`.
|
||||
Also returns the flag used to format the timestamp if present.
|
||||
This is done by extracting the epoch from the string.
|
||||
.. versionadd:: 2.0
|
||||
Raise :exc:`.FormattedDatetimeConversionFailure` instead of generic :exc:`.BadArgument`
|
||||
"""
|
||||
|
||||
async def convert(self, ctx: Context, argument: str) -> Tuple[datetime.datetime, Optional[str]]:
|
||||
|
||||
match = discord.utils.resolve_formatted_dt(argument)
|
||||
if match:
|
||||
return match
|
||||
|
||||
raise FormattedDatetimeConversionFailure(argument)
|
||||
|
||||
class GuildStickerConverter(IDConverter[discord.GuildSticker]):
|
||||
"""Converts to a :class:`~discord.GuildSticker`.
|
||||
|
||||
|
@ -98,6 +98,7 @@ __all__ = (
|
||||
'MissingFlagArgument',
|
||||
'TooManyFlags',
|
||||
'MissingRequiredFlag',
|
||||
'FormattedDatetimeConversionFailure',
|
||||
)
|
||||
|
||||
class CommandError(DiscordException):
|
||||
@ -995,3 +996,16 @@ class MissingFlagArgument(FlagError):
|
||||
def __init__(self, flag: Flag) -> None:
|
||||
self.flag: Flag = flag
|
||||
super().__init__(f'Flag {flag.name!r} does not have an argument')
|
||||
|
||||
class FormattedDatetimeConversionFailure(BadArgument):
|
||||
"""Exception raised when the timestamp provided does not match the correct
|
||||
format.
|
||||
This inherits from :exc:`BadArgument`
|
||||
Attributes
|
||||
-----------
|
||||
argument: :class:`str`
|
||||
The emoji supplied by the caller that did not match the regex
|
||||
"""
|
||||
def __init__(self, argument):
|
||||
self.argument = argument
|
||||
super().__init__(f'Timestamp "{argument}" wasn\'t formatted correctly.')
|
||||
|
@ -1021,3 +1021,24 @@ def raise_expected_coro(coro, error: str)-> TypeError:
|
||||
if not asyncio.iscoroutinefunction(coro):
|
||||
raise TypeError(error)
|
||||
return coro
|
||||
|
||||
def resolve_formatted_dt(formatted_dt: str) -> Optional[Tuple[datetime.datetime, Optional[str]]]:
|
||||
"""A helper function that can take discord formatted datetimes like those produced by :meth:`~discord.utils.format_dt`
|
||||
and return the relevant datetime.
|
||||
If unable to parse a datetime from formatted_dt, it will return None.
|
||||
.. versionadded:: 2.0
|
||||
Parameters
|
||||
-----------
|
||||
formatted_dt: :class:`str`
|
||||
The formatted datetime string.
|
||||
Returns
|
||||
--------
|
||||
Optional[Tuple[:class:`datetime.datetime`, Optional[:class:`str`]]
|
||||
A tuple containing the datetime and the flag if present. Otherwise None.
|
||||
"""
|
||||
match = re.match(r'<t:(-?[0-9]+)(:[tTdDfFR])?>$', formatted_dt)
|
||||
if match:
|
||||
try:
|
||||
return datetime.datetime.fromtimestamp(int(match.group(1)), tz=datetime.timezone.utc), match.group(2)
|
||||
except (ValueError, OverflowError, OSError):
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user