This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to parsing arguments for classes and methods.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional, Protocol, TypeVar, Union, overload
|
||||
|
||||
from telegram._linkpreviewoptions import LinkPreviewOptions
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import type_check_only
|
||||
|
||||
from telegram import Bot, FileCredentials
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def parse_sequence_arg(arg: Optional[Sequence[T]]) -> tuple[T, ...]:
|
||||
"""Parses an optional sequence into a tuple
|
||||
|
||||
Args:
|
||||
arg (:obj:`Sequence`): The sequence to parse.
|
||||
|
||||
Returns:
|
||||
:obj:`Tuple`: The sequence converted to a tuple or an empty tuple.
|
||||
"""
|
||||
return tuple(arg) if arg else ()
|
||||
|
||||
|
||||
@overload
|
||||
def to_timedelta(arg: None) -> None: ...
|
||||
|
||||
|
||||
@overload
|
||||
def to_timedelta(
|
||||
arg: Union[ # noqa: PYI041 (be more explicit about `int` and `float` arguments)
|
||||
int, float, dtm.timedelta
|
||||
],
|
||||
) -> dtm.timedelta: ...
|
||||
|
||||
|
||||
def to_timedelta(arg: Optional[Union[int, float, dtm.timedelta]]) -> Optional[dtm.timedelta]:
|
||||
"""Parses an optional time period in seconds into a timedelta
|
||||
|
||||
Args:
|
||||
arg (:obj:`int` | :class:`datetime.timedelta`, optional): The time period to parse.
|
||||
|
||||
Returns:
|
||||
:obj:`timedelta`: The time period converted to a timedelta object or :obj:`None`.
|
||||
"""
|
||||
if arg is None:
|
||||
return None
|
||||
if isinstance(arg, (int, float)):
|
||||
return dtm.timedelta(seconds=arg)
|
||||
return arg
|
||||
|
||||
|
||||
def parse_lpo_and_dwpp(
|
||||
disable_web_page_preview: Optional[bool], link_preview_options: ODVInput[LinkPreviewOptions]
|
||||
) -> ODVInput[LinkPreviewOptions]:
|
||||
"""Wrapper around warn_about_deprecated_arg_return_new_arg. Takes care of converting
|
||||
disable_web_page_preview to LinkPreviewOptions.
|
||||
"""
|
||||
if disable_web_page_preview and link_preview_options:
|
||||
raise ValueError(
|
||||
"Parameters `disable_web_page_preview` and `link_preview_options` are mutually "
|
||||
"exclusive."
|
||||
)
|
||||
|
||||
if disable_web_page_preview is not None:
|
||||
link_preview_options = LinkPreviewOptions(is_disabled=disable_web_page_preview)
|
||||
|
||||
return link_preview_options
|
||||
|
||||
|
||||
Tele_co = TypeVar("Tele_co", bound=TelegramObject, covariant=True)
|
||||
TeleCrypto_co = TypeVar("TeleCrypto_co", bound="HasDecryptMethod", covariant=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@type_check_only
|
||||
class HasDecryptMethod(Protocol):
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls: type[TeleCrypto_co],
|
||||
data: JSONDict,
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> TeleCrypto_co: ...
|
||||
|
||||
@classmethod
|
||||
def de_list_decrypted(
|
||||
cls: type[TeleCrypto_co],
|
||||
data: list[JSONDict],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple[TeleCrypto_co, ...]: ...
|
||||
|
||||
|
||||
def de_json_optional(
|
||||
data: Optional[JSONDict], cls: type[Tele_co], bot: Optional["Bot"]
|
||||
) -> Optional[Tele_co]:
|
||||
"""Wrapper around TO.de_json that returns None if data is None."""
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return cls.de_json(data, bot)
|
||||
|
||||
|
||||
def de_json_decrypted_optional(
|
||||
data: Optional[JSONDict],
|
||||
cls: type[TeleCrypto_co],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> Optional[TeleCrypto_co]:
|
||||
"""Wrapper around TO.de_json_decrypted that returns None if data is None."""
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return cls.de_json_decrypted(data, bot, credentials)
|
||||
|
||||
|
||||
def de_list_optional(
|
||||
data: Optional[list[JSONDict]], cls: type[Tele_co], bot: Optional["Bot"]
|
||||
) -> tuple[Tele_co, ...]:
|
||||
"""Wrapper around TO.de_list that returns an empty list if data is None."""
|
||||
if data is None:
|
||||
return ()
|
||||
|
||||
return cls.de_list(data, bot)
|
||||
|
||||
|
||||
def de_list_decrypted_optional(
|
||||
data: Optional[list[JSONDict]],
|
||||
cls: type[TeleCrypto_co],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple[TeleCrypto_co, ...]:
|
||||
"""Wrapper around TO.de_list_decrypted that returns an empty list if data is None."""
|
||||
if data is None:
|
||||
return ()
|
||||
|
||||
return cls.de_list_decrypted(data, bot, credentials)
|
||||
274
.venv/lib/python3.12/site-packages/telegram/_utils/datetime.py
Normal file
274
.venv/lib/python3.12/site-packages/telegram/_utils/datetime.py
Normal file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to datetime and timestamp conversations.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Previously, the contents of this module were available through the (no longer existing)
|
||||
module ``telegram._utils.helpers``.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import contextlib
|
||||
import datetime as dtm
|
||||
import os
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
UTC = dtm.timezone.utc
|
||||
try:
|
||||
import pytz
|
||||
except ImportError:
|
||||
pytz = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime:
|
||||
"""Localize the datetime, both for pytz and zoneinfo timezones."""
|
||||
if tzinfo is UTC:
|
||||
return datetime.replace(tzinfo=UTC)
|
||||
|
||||
with contextlib.suppress(AttributeError):
|
||||
# Since pytz might not be available, we need the suppress context manager
|
||||
if isinstance(tzinfo, pytz.BaseTzInfo):
|
||||
return tzinfo.localize(datetime)
|
||||
|
||||
if datetime.tzinfo is None:
|
||||
return datetime.replace(tzinfo=tzinfo)
|
||||
return datetime.astimezone(tzinfo)
|
||||
|
||||
|
||||
def to_float_timestamp(
|
||||
time_object: Union[float, dtm.timedelta, dtm.datetime, dtm.time],
|
||||
reference_timestamp: Optional[float] = None,
|
||||
tzinfo: Optional[dtm.tzinfo] = None,
|
||||
) -> float:
|
||||
"""
|
||||
Converts a given time object to a float POSIX timestamp.
|
||||
Used to convert different time specifications to a common format. The time object
|
||||
can be relative (i.e. indicate a time increment, or a time of day) or absolute.
|
||||
Objects from the :class:`datetime` module that are timezone-naive will be assumed
|
||||
to be in UTC, if ``bot`` is not passed or ``bot.defaults`` is :obj:`None`.
|
||||
|
||||
Args:
|
||||
time_object (:obj:`float` | :obj:`datetime.timedelta` | \
|
||||
:obj:`datetime.datetime` | :obj:`datetime.time`):
|
||||
Time value to convert. The semantics of this parameter will depend on its type:
|
||||
|
||||
* :obj:`float` will be interpreted as "seconds from :paramref:`reference_t`"
|
||||
* :obj:`datetime.timedelta` will be interpreted as
|
||||
"time increment from :paramref:`reference_timestamp`"
|
||||
* :obj:`datetime.datetime` will be interpreted as an absolute date/time value
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day
|
||||
|
||||
reference_timestamp (:obj:`float`, optional): POSIX timestamp that indicates the absolute
|
||||
time from which relative calculations are to be performed (e.g. when
|
||||
:paramref:`time_object` is given as an :obj:`int`, indicating "seconds from
|
||||
:paramref:`reference_time`"). Defaults to now (the time at which this function is
|
||||
called).
|
||||
|
||||
If :paramref:`time_object` is given as an absolute representation of date & time (i.e.
|
||||
a :obj:`datetime.datetime` object), :paramref:`reference_timestamp` is not relevant
|
||||
and so its value should be :obj:`None`. If this is not the case, a :exc:`ValueError`
|
||||
will be raised.
|
||||
tzinfo (:class:`datetime.tzinfo`, optional): If :paramref:`time_object` is a naive object
|
||||
from the :mod:`datetime` module, it will be interpreted as this timezone. Defaults to
|
||||
:attr:`datetime.timezone.utc` otherwise.
|
||||
|
||||
Note:
|
||||
Only to be used by ``telegram.ext``.
|
||||
|
||||
Returns:
|
||||
:obj:`float` | :obj:`None`:
|
||||
The return value depends on the type of argument :paramref:`time_object`.
|
||||
If :paramref:`time_object` is given as a time increment (i.e. as a :obj:`int`,
|
||||
:obj:`float` or :obj:`datetime.timedelta`), then the return value will be
|
||||
:paramref:`reference_timestamp` + :paramref:`time_object`.
|
||||
|
||||
Else if it is given as an absolute date/time value (i.e. a :obj:`datetime.datetime`
|
||||
object), the equivalent value as a POSIX timestamp will be returned.
|
||||
|
||||
Finally, if it is a time of the day without date (i.e. a :obj:`datetime.time`
|
||||
object), the return value is the nearest future occurrence of that time of day.
|
||||
|
||||
Raises:
|
||||
TypeError: If :paramref:`time_object` s type is not one of those described above.
|
||||
ValueError: If :paramref:`time_object` is a :obj:`datetime.datetime` and
|
||||
:paramref:`reference_timestamp` is not :obj:`None`.
|
||||
"""
|
||||
if reference_timestamp is None:
|
||||
reference_timestamp = time.time()
|
||||
elif isinstance(time_object, dtm.datetime):
|
||||
raise ValueError("t is an (absolute) datetime while reference_timestamp is not None")
|
||||
|
||||
if isinstance(time_object, dtm.timedelta):
|
||||
return reference_timestamp + time_object.total_seconds()
|
||||
if isinstance(time_object, (int, float)):
|
||||
return reference_timestamp + time_object
|
||||
|
||||
if tzinfo is None:
|
||||
# We do this here rather than in the signature to ensure that we can make calls like
|
||||
# to_float_timestamp(
|
||||
# time, tzinfo=bot.defaults.tzinfo if bot.defaults else None
|
||||
# )
|
||||
# This ensures clean separation of concerns, i.e. the default timezone should not be
|
||||
# the responsibility of the caller
|
||||
tzinfo = UTC
|
||||
|
||||
if isinstance(time_object, dtm.time):
|
||||
reference_dt = dtm.datetime.fromtimestamp(
|
||||
reference_timestamp, tz=time_object.tzinfo or tzinfo
|
||||
)
|
||||
reference_date = reference_dt.date()
|
||||
reference_time = reference_dt.timetz()
|
||||
|
||||
aware_datetime = dtm.datetime.combine(reference_date, time_object)
|
||||
if aware_datetime.tzinfo is None:
|
||||
# datetime.combine uses the tzinfo of `time_object`, which might be None
|
||||
# so we still need to localize
|
||||
aware_datetime = localize(aware_datetime, tzinfo)
|
||||
|
||||
# if the time of day has passed today, use tomorrow
|
||||
if reference_time > aware_datetime.timetz():
|
||||
aware_datetime += dtm.timedelta(days=1)
|
||||
return _datetime_to_float_timestamp(aware_datetime)
|
||||
if isinstance(time_object, dtm.datetime):
|
||||
if time_object.tzinfo is None:
|
||||
time_object = localize(time_object, tzinfo)
|
||||
return _datetime_to_float_timestamp(time_object)
|
||||
|
||||
raise TypeError(f"Unable to convert {type(time_object).__name__} object to timestamp")
|
||||
|
||||
|
||||
def to_timestamp(
|
||||
dt_obj: Union[float, dtm.timedelta, dtm.datetime, dtm.time, None],
|
||||
reference_timestamp: Optional[float] = None,
|
||||
tzinfo: Optional[dtm.tzinfo] = None,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Wrapper over :func:`to_float_timestamp` which returns an integer (the float value truncated
|
||||
down to the nearest integer).
|
||||
|
||||
See the documentation for :func:`to_float_timestamp` for more details.
|
||||
"""
|
||||
return (
|
||||
int(to_float_timestamp(dt_obj, reference_timestamp, tzinfo))
|
||||
if dt_obj is not None
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
def from_timestamp(
|
||||
unixtime: Optional[int],
|
||||
tzinfo: Optional[dtm.tzinfo] = None,
|
||||
) -> Optional[dtm.datetime]:
|
||||
"""
|
||||
Converts an (integer) unix timestamp to a timezone aware datetime object.
|
||||
:obj:`None` s are left alone (i.e. ``from_timestamp(None)`` is :obj:`None`).
|
||||
|
||||
Args:
|
||||
unixtime (:obj:`int`): Integer POSIX timestamp.
|
||||
tzinfo (:obj:`datetime.tzinfo`, optional): The timezone to which the timestamp is to be
|
||||
converted to. Defaults to :obj:`None`, in which case the returned datetime object will
|
||||
be timezone aware and in UTC.
|
||||
|
||||
Returns:
|
||||
Timezone aware equivalent :obj:`datetime.datetime` value if :paramref:`unixtime` is not
|
||||
:obj:`None`; else :obj:`None`.
|
||||
"""
|
||||
if unixtime is None:
|
||||
return None
|
||||
|
||||
return dtm.datetime.fromtimestamp(unixtime, tz=UTC if tzinfo is None else tzinfo)
|
||||
|
||||
|
||||
def extract_tzinfo_from_defaults(bot: Optional["Bot"]) -> Union[dtm.tzinfo, None]:
|
||||
"""
|
||||
Extracts the timezone info from the default values of the bot.
|
||||
If the bot has no default values, :obj:`None` is returned.
|
||||
"""
|
||||
# We don't use `ininstance(bot, ExtBot)` here so that this works
|
||||
# without the job-queue extra dependencies as well
|
||||
if bot is None:
|
||||
return None
|
||||
|
||||
if hasattr(bot, "defaults") and bot.defaults:
|
||||
return bot.defaults.tzinfo
|
||||
return None
|
||||
|
||||
|
||||
def _datetime_to_float_timestamp(dt_obj: dtm.datetime) -> float:
|
||||
"""
|
||||
Converts a datetime object to a float timestamp (with sub-second precision).
|
||||
If the datetime object is timezone-naive, it is assumed to be in UTC.
|
||||
"""
|
||||
if dt_obj.tzinfo is None:
|
||||
dt_obj = dt_obj.replace(tzinfo=dtm.timezone.utc)
|
||||
return dt_obj.timestamp()
|
||||
|
||||
|
||||
def get_timedelta_value(
|
||||
value: Optional[dtm.timedelta], attribute: str
|
||||
) -> Optional[Union[int, dtm.timedelta]]:
|
||||
"""
|
||||
Convert a `datetime.timedelta` to seconds or return it as-is, based on environment config.
|
||||
|
||||
This utility is part of the migration process from integer-based time representations
|
||||
to using `datetime.timedelta`. The behavior is controlled by the `PTB_TIMEDELTA`
|
||||
environment variable.
|
||||
|
||||
Note:
|
||||
When `PTB_TIMEDELTA` is not enabled, the function will issue a deprecation warning.
|
||||
|
||||
Args:
|
||||
value (:obj:`datetime.timedelta`): The timedelta value to process.
|
||||
attribute (:obj:`str`): The name of the attribute at the caller scope, used for
|
||||
warning messages.
|
||||
|
||||
Returns:
|
||||
- :obj:`None` if :paramref:`value` is None.
|
||||
- :obj:`datetime.timedelta` if `PTB_TIMEDELTA=true` or ``PTB_TIMEDELTA=1``.
|
||||
- :obj:`int` if the total seconds is a whole number.
|
||||
- float: otherwise.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
if os.getenv("PTB_TIMEDELTA", "false").lower().strip() in ["true", "1"]:
|
||||
return value
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
"v22.2",
|
||||
f"In a future major version attribute `{attribute}` will be of type"
|
||||
" `datetime.timedelta`. You can opt-in early by setting `PTB_TIMEDELTA=true`"
|
||||
" or ``PTB_TIMEDELTA=1`` as an environment variable.",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
return (
|
||||
int(seconds)
|
||||
if (seconds := value.total_seconds()).is_integer()
|
||||
else seconds # type: ignore[return-value]
|
||||
)
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the DefaultValue class.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Previously, the contents of this module were available through the (no longer existing)
|
||||
module ``telegram._utils.helpers``.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from typing import Generic, TypeVar, Union, overload
|
||||
|
||||
DVType = TypeVar("DVType", bound=object) # pylint: disable=invalid-name
|
||||
OT = TypeVar("OT", bound=object)
|
||||
|
||||
|
||||
class DefaultValue(Generic[DVType]):
|
||||
"""Wrapper for immutable default arguments that allows to check, if the default value was set
|
||||
explicitly. Usage::
|
||||
|
||||
default_one = DefaultValue(1)
|
||||
def f(arg=default_one):
|
||||
if arg is default_one:
|
||||
print('`arg` is the default')
|
||||
arg = arg.value
|
||||
else:
|
||||
print('`arg` was set explicitly')
|
||||
print(f'`arg` = {str(arg)}')
|
||||
|
||||
This yields::
|
||||
|
||||
>>> f()
|
||||
`arg` is the default
|
||||
`arg` = 1
|
||||
>>> f(1)
|
||||
`arg` was set explicitly
|
||||
`arg` = 1
|
||||
>>> f(2)
|
||||
`arg` was set explicitly
|
||||
`arg` = 2
|
||||
|
||||
Also allows to evaluate truthiness::
|
||||
|
||||
default = DefaultValue(value)
|
||||
if default:
|
||||
...
|
||||
|
||||
is equivalent to::
|
||||
|
||||
default = DefaultValue(value)
|
||||
if value:
|
||||
...
|
||||
|
||||
``repr(DefaultValue(value))`` returns ``repr(value)`` and ``str(DefaultValue(value))`` returns
|
||||
``f'DefaultValue({value})'``.
|
||||
|
||||
Args:
|
||||
value (:class:`object`): The value of the default argument
|
||||
Attributes:
|
||||
value (:class:`object`): The value of the default argument
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("value",)
|
||||
|
||||
def __init__(self, value: DVType):
|
||||
self.value: DVType = value
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self.value)
|
||||
|
||||
# This is mostly here for readability during debugging
|
||||
def __str__(self) -> str:
|
||||
return f"DefaultValue({self.value})"
|
||||
|
||||
# This is here to have the default instances nicely rendered in the docs
|
||||
def __repr__(self) -> str:
|
||||
return repr(self.value)
|
||||
|
||||
@overload
|
||||
@staticmethod
|
||||
def get_value(obj: "DefaultValue[OT]") -> OT: ...
|
||||
|
||||
@overload
|
||||
@staticmethod
|
||||
def get_value(obj: OT) -> OT: ...
|
||||
|
||||
@staticmethod
|
||||
def get_value(obj: Union[OT, "DefaultValue[OT]"]) -> OT:
|
||||
"""Shortcut for::
|
||||
|
||||
return obj.value if isinstance(obj, DefaultValue) else obj
|
||||
|
||||
Args:
|
||||
obj (:obj:`object`): The object to process
|
||||
|
||||
Returns:
|
||||
Same type as input, or the value of the input: The value
|
||||
"""
|
||||
return obj.value if isinstance(obj, DefaultValue) else obj
|
||||
|
||||
|
||||
DEFAULT_NONE: DefaultValue[None] = DefaultValue(None)
|
||||
""":class:`DefaultValue`: Default :obj:`None`"""
|
||||
|
||||
DEFAULT_FALSE: DefaultValue[bool] = DefaultValue(False)
|
||||
""":class:`DefaultValue`: Default :obj:`False`"""
|
||||
|
||||
DEFAULT_TRUE: DefaultValue[bool] = DefaultValue(True)
|
||||
""":class:`DefaultValue`: Default :obj:`True`
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
|
||||
|
||||
DEFAULT_20: DefaultValue[int] = DefaultValue(20)
|
||||
""":class:`DefaultValue`: Default :obj:`20`"""
|
||||
|
||||
DEFAULT_IP: DefaultValue[str] = DefaultValue("127.0.0.1")
|
||||
""":class:`DefaultValue`: Default :obj:`127.0.0.1`
|
||||
|
||||
.. versionadded:: 20.8
|
||||
"""
|
||||
|
||||
DEFAULT_80: DefaultValue[int] = DefaultValue(80)
|
||||
""":class:`DefaultValue`: Default :obj:`80`
|
||||
|
||||
.. versionadded:: 20.8
|
||||
"""
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains auxiliary functionality for parsing MessageEntity objects.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.strings import TextEncoding
|
||||
|
||||
|
||||
def parse_message_entity(text: str, entity: MessageEntity) -> str:
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): The text to extract the entity from.
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity.
|
||||
"""
|
||||
entity_text = text.encode(TextEncoding.UTF_16_LE)
|
||||
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode(TextEncoding.UTF_16_LE)
|
||||
|
||||
|
||||
def parse_message_entities(
|
||||
text: str, entities: Sequence[MessageEntity], types: Optional[Sequence[str]] = None
|
||||
) -> dict[MessageEntity, str]:
|
||||
"""
|
||||
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
|
||||
It contains entities filtered by their ``type`` attribute as
|
||||
the key, and the text that each entity belongs to as the value of the :obj:`dict`.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): The text to extract the entity from.
|
||||
entities (list[:class:`telegram.MessageEntity`]): The entities to extract the text from.
|
||||
types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
|
||||
``type`` attribute of an entity is contained in this list, it will be returned.
|
||||
Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.
|
||||
|
||||
Returns:
|
||||
dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
||||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
|
||||
return {
|
||||
entity: parse_message_entity(text, entity) for entity in entities if entity.type in types
|
||||
}
|
||||
90
.venv/lib/python3.12/site-packages/telegram/_utils/enum.py
Normal file
90
.venv/lib/python3.12/site-packages/telegram/_utils/enum.py
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to enums.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import enum as _enum
|
||||
import sys
|
||||
from typing import TypeVar, Union
|
||||
|
||||
_A = TypeVar("_A")
|
||||
_B = TypeVar("_B")
|
||||
_Enum = TypeVar("_Enum", bound=_enum.Enum)
|
||||
|
||||
|
||||
def get_member(enum_cls: type[_Enum], value: _A, default: _B) -> Union[_Enum, _A, _B]:
|
||||
"""Tries to call ``enum_cls(value)`` to convert the value into an enumeration member.
|
||||
If that fails, the ``default`` is returned.
|
||||
"""
|
||||
try:
|
||||
return enum_cls(value)
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
|
||||
# Python 3.11 and above has a different output for mixin classes for IntEnum, StrEnum and IntFlag
|
||||
# see https://docs.python.org/3.11/library/enum.html#notes. We want e.g. str(StrEnumTest.FOO) to
|
||||
# return "foo" instead of "StrEnumTest.FOO", which is not the case < py3.11
|
||||
class StringEnum(str, _enum.Enum):
|
||||
"""Helper class for string enums where ``str(member)`` prints the value, but ``repr(member)``
|
||||
gives ``EnumName.MEMBER_NAME``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}.{self.name}>"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str.__str__(self)
|
||||
|
||||
|
||||
# Apply the __repr__ modification and __str__ fix to IntEnum
|
||||
class IntEnum(_enum.IntEnum): # pylint: disable=invalid-slots
|
||||
"""Helper class for int enums where ``str(member)`` prints the value, but ``repr(member)``
|
||||
gives ``EnumName.MEMBER_NAME``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}.{self.name}>"
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
class FloatEnum(float, _enum.Enum):
|
||||
"""Helper class for float enums where ``str(member)`` prints the value, but ``repr(member)``
|
||||
gives ``EnumName.MEMBER_NAME``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}.{self.name}>"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
157
.venv/lib/python3.12/site-packages/telegram/_utils/files.py
Normal file
157
.venv/lib/python3.12/site-packages/telegram/_utils/files.py
Normal file
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to handling of files.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Previously, the contents of this module were available through the (no longer existing)
|
||||
module ``telegram._utils.helpers``.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import IO, TYPE_CHECKING, Any, Optional, TypeVar, Union, cast, overload
|
||||
|
||||
from telegram._utils.types import FileInput, FilePathInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import InputFile, TelegramObject
|
||||
|
||||
_T = TypeVar("_T", bound=Union[bytes, "InputFile", str, Path, None])
|
||||
|
||||
|
||||
@overload
|
||||
def load_file(obj: IO[bytes]) -> tuple[Optional[str], bytes]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def load_file(obj: _T) -> tuple[None, _T]: ...
|
||||
|
||||
|
||||
def load_file(
|
||||
obj: Optional[FileInput],
|
||||
) -> tuple[Optional[str], Union[bytes, "InputFile", str, Path, None]]:
|
||||
"""If the input is a file handle, read the data and name and return it. Otherwise, return
|
||||
the input unchanged.
|
||||
"""
|
||||
if obj is None:
|
||||
return None, None
|
||||
|
||||
try:
|
||||
contents = obj.read() # type: ignore[union-attr]
|
||||
except AttributeError:
|
||||
return None, cast("Union[bytes, InputFile, str, Path]", obj)
|
||||
|
||||
filename = guess_file_name(obj)
|
||||
|
||||
return filename, contents
|
||||
|
||||
|
||||
def guess_file_name(obj: FileInput) -> Optional[str]:
|
||||
"""If the input is a file handle, read name and return it. Otherwise, return
|
||||
the input unchanged.
|
||||
"""
|
||||
if hasattr(obj, "name") and not isinstance(obj.name, int):
|
||||
return Path(obj.name).name
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_local_file(obj: Optional[FilePathInput]) -> bool:
|
||||
"""
|
||||
Checks if a given string is a file on local system.
|
||||
|
||||
Args:
|
||||
obj (:obj:`str`): The string to check.
|
||||
"""
|
||||
if obj is None:
|
||||
return False
|
||||
|
||||
path = Path(obj)
|
||||
try:
|
||||
return path.is_file()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def parse_file_input( # pylint: disable=too-many-return-statements
|
||||
file_input: Union[FileInput, "TelegramObject"],
|
||||
tg_type: Optional[type["TelegramObject"]] = None,
|
||||
filename: Optional[str] = None,
|
||||
attach: bool = False,
|
||||
local_mode: bool = False,
|
||||
) -> Union[str, "InputFile", Any]:
|
||||
"""
|
||||
Parses input for sending files:
|
||||
|
||||
* For string input, if the input is an absolute path of a local file:
|
||||
|
||||
* if ``local_mode`` is ``True``, adds the ``file://`` prefix. If the input is a relative
|
||||
path of a local file, computes the absolute path and adds the ``file://`` prefix.
|
||||
* if ``local_mode`` is ``False``, loads the file as binary data and builds an
|
||||
:class:`InputFile` from that
|
||||
|
||||
Returns the input unchanged, otherwise.
|
||||
* :class:`pathlib.Path` objects are treated the same way as strings.
|
||||
* For IO and bytes input, returns an :class:`telegram.InputFile`.
|
||||
* If :attr:`tg_type` is specified and the input is of that type, returns the ``file_id``
|
||||
attribute.
|
||||
|
||||
Args:
|
||||
file_input (:obj:`str` | :obj:`bytes` | :term:`file object` | :class:`~telegram.InputFile`\
|
||||
| Telegram media object): The input to parse.
|
||||
tg_type (:obj:`type`, optional): The Telegram media type the input can be. E.g.
|
||||
:class:`telegram.Animation`.
|
||||
filename (:obj:`str`, optional): The filename. Only relevant in case an
|
||||
:class:`telegram.InputFile` is returned.
|
||||
attach (:obj:`bool`, optional): Pass :obj:`True` if the parameter this file belongs to in
|
||||
the request to Telegram should point to the multipart data via an ``attach://`` URI.
|
||||
Defaults to `False`. Only relevant if an :class:`telegram.InputFile` is returned.
|
||||
local_mode (:obj:`bool`, optional): Pass :obj:`True` if the bot is running an api server
|
||||
in ``--local`` mode.
|
||||
|
||||
Returns:
|
||||
:obj:`str` | :class:`telegram.InputFile` | :obj:`object`: The parsed input or the untouched
|
||||
:attr:`file_input`, in case it's no valid file input.
|
||||
"""
|
||||
# Importing on file-level yields cyclic Import Errors
|
||||
from telegram import InputFile # pylint: disable=import-outside-toplevel # noqa: PLC0415
|
||||
|
||||
if isinstance(file_input, str) and file_input.startswith("file://"):
|
||||
if not local_mode:
|
||||
raise ValueError("Specified file input is a file URI, but local mode is not enabled.")
|
||||
return file_input
|
||||
if isinstance(file_input, (str, Path)):
|
||||
if is_local_file(file_input):
|
||||
path = Path(file_input)
|
||||
if local_mode:
|
||||
return path.absolute().as_uri()
|
||||
return InputFile(path.open(mode="rb"), filename=filename, attach=attach)
|
||||
|
||||
return file_input
|
||||
if isinstance(file_input, bytes):
|
||||
return InputFile(file_input, filename=filename, attach=attach)
|
||||
if hasattr(file_input, "read"):
|
||||
return InputFile(cast("IO", file_input), filename=filename, attach=attach)
|
||||
if tg_type and isinstance(file_input, tg_type):
|
||||
return file_input.file_id # type: ignore[attr-defined]
|
||||
return file_input
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to logging.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_logger(file_name: str, class_name: Optional[str] = None) -> logging.Logger:
|
||||
"""Returns a logger with an appropriate name.
|
||||
Use as follows::
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
If for example `__name__` is `telegram.ext._updater`, the logger will be named
|
||||
`telegram.ext.Updater`. If `class_name` is passed, this will result in
|
||||
`telegram.ext.<class_name>`. Useful e.g. for CamelCase class names.
|
||||
|
||||
If the file name points to a utils module, the logger name will simply be `telegram(.ext)`.
|
||||
|
||||
Returns:
|
||||
:class:`logging.Logger`: The logger.
|
||||
"""
|
||||
parts = file_name.split("_")
|
||||
if parts[1].startswith("utils") and class_name is None:
|
||||
name = parts[0].rstrip(".")
|
||||
else:
|
||||
name = f"{parts[0]}{class_name or parts[1].capitalize()}"
|
||||
return logging.getLogger(name)
|
||||
49
.venv/lib/python3.12/site-packages/telegram/_utils/markup.py
Normal file
49
.venv/lib/python3.12/site-packages/telegram/_utils/markup.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains a helper function for Telegram's ReplyMarkups
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Previously, the contents of this module were available through the (no longer existing)
|
||||
class ``telegram.ReplyMarkup``.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def check_keyboard_type(keyboard: object) -> bool:
|
||||
"""Checks if the keyboard provided is of the correct type - A sequence of sequences.
|
||||
Implicitly tested in the init-tests of `{Inline, Reply}KeyboardMarkup`
|
||||
"""
|
||||
# string and bytes may actually be used for ReplyKeyboardMarkup in which case each button
|
||||
# would contain a single character. But that use case should be discouraged and we don't
|
||||
# allow it here.
|
||||
if not isinstance(keyboard, Sequence) or isinstance(keyboard, (str, bytes)):
|
||||
return False
|
||||
|
||||
for row in keyboard:
|
||||
if not isinstance(row, Sequence) or isinstance(row, (str, bytes)):
|
||||
return False
|
||||
for inner in row:
|
||||
if isinstance(inner, Sequence) and not isinstance(inner, str):
|
||||
return False
|
||||
return True
|
||||
45
.venv/lib/python3.12/site-packages/telegram/_utils/repr.py
Normal file
45
.venv/lib/python3.12/site-packages/telegram/_utils/repr.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains auxiliary functionality for building strings for __repr__ method.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
def build_repr_with_selected_attrs(obj: object, **kwargs: Any) -> str:
|
||||
"""Create ``__repr__`` string in the style ``Classname[arg1=1, arg2=2]``.
|
||||
|
||||
The square brackets emphasize the fact that an object cannot be instantiated
|
||||
from this string.
|
||||
|
||||
Attributes that are to be used in the representation, are passed as kwargs.
|
||||
"""
|
||||
return (
|
||||
f"{obj.__class__.__name__}"
|
||||
# square brackets emphasize that an object cannot be instantiated with these params
|
||||
f"[{', '.join(_stringify(name, value) for name, value in kwargs.items())}]"
|
||||
)
|
||||
|
||||
|
||||
def _stringify(key: str, val: Any) -> str:
|
||||
return f"{key}={val.__qualname__ if callable(val) else val}"
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains a helper functions related to string manipulation.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
|
||||
from telegram._utils.enum import StringEnum
|
||||
|
||||
# TODO: Remove this when https://github.com/PyCQA/pylint/issues/6887 is resolved.
|
||||
# pylint: disable=invalid-enum-extension
|
||||
|
||||
|
||||
class TextEncoding(StringEnum):
|
||||
"""This enum contains encoding schemes for text.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
UTF_8 = "utf-8"
|
||||
UTF_16_LE = "utf-16-le"
|
||||
|
||||
|
||||
def to_camel_case(snake_str: str) -> str:
|
||||
"""Converts a snake_case string to camelCase.
|
||||
|
||||
Args:
|
||||
snake_str (:obj:`str`): The string to convert.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The converted string.
|
||||
"""
|
||||
components = snake_str.split("_")
|
||||
return components[0] + "".join(x.title() for x in components[1:])
|
||||
98
.venv/lib/python3.12/site-packages/telegram/_utils/types.py
Normal file
98
.venv/lib/python3.12/site-packages/telegram/_utils/types.py
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains custom typing aliases for internal use within the library.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import datetime as dtm
|
||||
from collections.abc import Collection
|
||||
from pathlib import Path
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, Literal, Optional, TypeVar, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from telegram._utils.defaultvalue import DefaultValue
|
||||
|
||||
FileLike = Union[IO[bytes], "InputFile"]
|
||||
"""Either a bytes-stream (e.g. open file handler) or a :class:`telegram.InputFile`."""
|
||||
|
||||
FilePathInput = Union[str, Path]
|
||||
"""A filepath either as string or as :obj:`pathlib.Path` object."""
|
||||
|
||||
FileInput = Union[FilePathInput, FileLike, bytes, str]
|
||||
"""Valid input for passing files to Telegram. Either a file id as string, a file like object,
|
||||
a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes`."""
|
||||
|
||||
JSONDict = dict[str, Any]
|
||||
"""Dictionary containing response from Telegram or data to send to the API."""
|
||||
|
||||
DVValueType = TypeVar("DVValueType") # pylint: disable=invalid-name
|
||||
DVType = Union[DVValueType, "DefaultValue[DVValueType]"]
|
||||
"""Generic type for a variable which can be either `type` or `DefaultVaule[type]`."""
|
||||
ODVInput = Optional[Union["DefaultValue[DVValueType]", DVValueType, "DefaultValue[None]"]]
|
||||
"""Generic type for bot method parameters which can have defaults. ``ODVInput[type]`` is the same
|
||||
as ``Optional[Union[DefaultValue[type], type, DefaultValue[None]]``."""
|
||||
DVInput = Union["DefaultValue[DVValueType]", DVValueType, "DefaultValue[None]"]
|
||||
"""Generic type for bot method parameters which can have defaults. ``DVInput[type]`` is the same
|
||||
as ``Union[DefaultValue[type], type, DefaultValue[None]]``."""
|
||||
|
||||
RT = TypeVar("RT")
|
||||
SCT = Union[RT, Collection[RT]] # pylint: disable=invalid-name
|
||||
"""Single instance or collection of instances."""
|
||||
|
||||
ReplyMarkup = Union[
|
||||
"InlineKeyboardMarkup", "ReplyKeyboardMarkup", "ReplyKeyboardRemove", "ForceReply"
|
||||
]
|
||||
"""Type alias for reply markup objects.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
|
||||
FieldTuple = tuple[str, Union[bytes, IO[bytes]], str]
|
||||
"""Alias for return type of `InputFile.field_tuple`."""
|
||||
UploadFileDict = dict[str, FieldTuple]
|
||||
"""Dictionary containing file data to be uploaded to the API."""
|
||||
|
||||
HTTPVersion = Literal["1.1", "2.0", "2"]
|
||||
"""Allowed HTTP versions.
|
||||
|
||||
.. versionadded:: 20.4"""
|
||||
|
||||
CorrectOptionID = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
MarkdownVersion = Literal[1, 2]
|
||||
|
||||
SocketOpt = Union[
|
||||
tuple[int, int, int],
|
||||
tuple[int, int, Union[bytes, bytearray]],
|
||||
tuple[int, int, None, int],
|
||||
]
|
||||
|
||||
BaseUrl = Union[str, Callable[[str], str]]
|
||||
|
||||
TimePeriod = Union[int, dtm.timedelta]
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains helper functions related to warnings issued by the library.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import warnings
|
||||
from typing import Union
|
||||
|
||||
from telegram.warnings import PTBUserWarning
|
||||
|
||||
|
||||
def warn(
|
||||
message: Union[str, PTBUserWarning],
|
||||
category: type[Warning] = PTBUserWarning,
|
||||
stacklevel: int = 0,
|
||||
) -> None:
|
||||
"""
|
||||
Helper function used as a shortcut for warning with default values.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Args:
|
||||
message (:obj:`str` | :obj:`PTBUserWarning`): Specify the warnings message to pass to
|
||||
``warnings.warn()``.
|
||||
|
||||
.. versionchanged:: 21.2
|
||||
Now also accepts a :obj:`PTBUserWarning` instance.
|
||||
|
||||
category (:obj:`type[Warning]`, optional): Specify the Warning class to pass to
|
||||
``warnings.warn()``. Defaults to :class:`telegram.warnings.PTBUserWarning`.
|
||||
stacklevel (:obj:`int`, optional): Specify the stacklevel to pass to ``warnings.warn()``.
|
||||
Pass the same value as you'd pass directly to ``warnings.warn()``. Defaults to ``0``.
|
||||
"""
|
||||
warnings.warn(message, category=category, stacklevel=stacklevel + 1)
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains functionality used for transition warnings issued by this library.
|
||||
|
||||
It was created to prevent circular imports that would be caused by creating the warnings
|
||||
inside warnings.py.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
from typing import Any, Callable, Union
|
||||
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
|
||||
|
||||
|
||||
def build_deprecation_warning_message(
|
||||
deprecated_name: str,
|
||||
new_name: str,
|
||||
object_type: str,
|
||||
bot_api_version: str,
|
||||
) -> str:
|
||||
"""Builds a warning message for the transition in API when an object is renamed/replaced.
|
||||
|
||||
Returns a warning message that can be used in `warn` function.
|
||||
"""
|
||||
return (
|
||||
f"The {object_type} '{deprecated_name}' was replaced by '{new_name}' in Bot API "
|
||||
f"{bot_api_version}. We recommend using '{new_name}' instead of "
|
||||
f"'{deprecated_name}'."
|
||||
)
|
||||
|
||||
|
||||
# Narrower type hints will cause linting errors and/or circular imports.
|
||||
# We'll use `Any` here and put type hints in the calling code.
|
||||
def warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg: Any,
|
||||
new_arg: Any,
|
||||
deprecated_arg_name: str,
|
||||
new_arg_name: str,
|
||||
bot_api_version: str,
|
||||
ptb_version: str,
|
||||
stacklevel: int = 2,
|
||||
warn_callback: Callable[[Union[str, PTBUserWarning], type[Warning], int], None] = warn,
|
||||
) -> Any:
|
||||
"""A helper function for the transition in API when argument is renamed.
|
||||
|
||||
Checks the `deprecated_arg` and `new_arg` objects; warns if non-None `deprecated_arg` object
|
||||
was passed. Returns `new_arg` object (either the one originally passed by the user or the one
|
||||
that user passed as `deprecated_arg`).
|
||||
|
||||
Raises `ValueError` if both `deprecated_arg` and `new_arg` objects were passed, and they are
|
||||
different.
|
||||
"""
|
||||
if deprecated_arg and new_arg and deprecated_arg != new_arg:
|
||||
base_message = build_deprecation_warning_message(
|
||||
deprecated_name=deprecated_arg_name,
|
||||
new_name=new_arg_name,
|
||||
object_type="parameter",
|
||||
bot_api_version=bot_api_version,
|
||||
)
|
||||
raise ValueError(
|
||||
f"You passed different entities as '{deprecated_arg_name}' and '{new_arg_name}'. "
|
||||
f"{base_message}"
|
||||
)
|
||||
|
||||
if deprecated_arg:
|
||||
warn_callback(
|
||||
PTBDeprecationWarning(
|
||||
ptb_version,
|
||||
f"Bot API {bot_api_version} renamed the argument '{deprecated_arg_name}' to "
|
||||
f"'{new_arg_name}'.",
|
||||
),
|
||||
stacklevel=stacklevel + 1, # type: ignore[call-arg]
|
||||
)
|
||||
return deprecated_arg
|
||||
|
||||
return new_arg
|
||||
|
||||
|
||||
def warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name: str,
|
||||
new_attr_name: str,
|
||||
bot_api_version: str,
|
||||
ptb_version: str,
|
||||
stacklevel: int = 2,
|
||||
) -> None:
|
||||
"""A helper function for the transition in API when attribute is renamed. Call from properties.
|
||||
|
||||
The properties replace deprecated attributes in classes and issue these deprecation warnings.
|
||||
"""
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
ptb_version,
|
||||
f"Bot API {bot_api_version} renamed the attribute '{deprecated_attr_name}' to "
|
||||
f"'{new_attr_name}'.",
|
||||
),
|
||||
stacklevel=stacklevel + 1,
|
||||
)
|
||||
Reference in New Issue
Block a user