8000 API 9.1 `Checklist*` classes by Bibo-Joshi · Pull Request #4848 · python-telegram-bot/python-telegram-bot · GitHub
[go: up one dir, main page]

Skip to content

API 9.1 Checklist* classes #4848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 8, 2025
Merged
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
4 changes: 4 additions & 0 deletions changes/unreleased/4847.8ujbbBbaZ2VTEdRLeqirSZ.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ features = """
New filters based on Bot API 9.1:

* ``filters.StatusUpdate.DIRECT_MESSAGE_PRICE_CHANGED`` for ``Message.direct_message_price_changed``
* ``filters.StatusUpdate.CHECKLIST_TASKS_ADDED`` for ``Message.checklist_tasks_added``
* ``filters.StatusUpdate.CHECKLIST_TASKS_DONE`` for ``Message.checklist_tasks_done``
* ``filters.CHECKLIST`` for ``Message.checklist``
"""

pull_requests = [
{ uid = "4847", author_uid = "Bibo-Joshi", closes_threads = ["4845"] },
{ uid = "4848", author_uid = "Bibo-Joshi" },
{ uid = "4849", author_uid = "harshil21" },
{ uid = "4851", author_uid = "harshil21" },
]
3 changes: 3 additions & 0 deletions docs/source/telegram.at-tree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ Available Types
telegram.chat
telegram.chatadministratorrights
telegram.chatbackground
telegram.checklist
telegram.checklisttask
telegram.checklisttasksadded
telegram.checklisttasksdone
telegram.copytextbutton
telegram.backgroundtype
telegram.backgroundtypefill
Expand Down
6 changes: 6 additions & 0 deletions docs/source/telegram.checklist.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Checklist
=========

.. autoclass:: telegram.Checklist
:members:
:show-inheritance:
6 changes: 6 additions & 0 deletions docs/source/telegram.checklisttasksadded.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ChecklistTasksAdded
===================

.. autoclass:: telegram.ChecklistTasksAdded
:members:
:show-inheritance:
6 changes: 6 additions & 0 deletions docs/source/telegram.checklisttasksdone.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ChecklistTasksDone
==================

.. autoclass:: telegram.ChecklistTasksDone
:members:
:show-inheritance:
5 changes: 4 additions & 1 deletion src/telegram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@
"ChatPermissions",
"ChatPhoto",
"ChatShared",
"Checklist",
"ChecklistTask",
"ChecklistTasksAdded",
"ChecklistTasksDone",
"ChosenInlineResult",
"Contact",
"CopyTextButton",
Expand Down Expand Up @@ -383,7 +386,7 @@
)
from ._chatmemberupdated import ChatMemberUpdated
from ._chatpermissions import ChatPermissions
from ._checklists import ChecklistTask
from ._checklists import Checklist, ChecklistTask, ChecklistTasksAdded, ChecklistTasksDone
from ._choseninlineresult import ChosenInlineResult
from ._copytextbutton import CopyTextButton
from ._dice import Dice
Expand Down
236 changes: 235 additions & 1 deletion src/telegram/_checklists.py
< 10000 /tr>
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from telegram.constants import ZERO_DATE

if TYPE_CHECKING:
from telegram import Bot
from telegram import Bot, Message


class ChecklistTask(TelegramObject):
Expand Down Expand Up @@ -154,3 +154,237 @@ def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntit
the text that belongs to them, calculated based on UTF-16 codepoints.
"""
return parse_message_entities(self.text, self.text_entities, types)


class Checklist(TelegramObject):
"""
Describes a checklist.

Objects of this class are comparable in terms of equality.
Two objects of this class are considered equal, if all their :attr:`tasks` are equal.

.. versionadded:: NEXT.VERSION

Args:
title (:obj:`str`): Title of the checklist.
title_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special
entities that appear in the checklist title.
tasks (Sequence[:class:`telegram.ChecklistTask`]): List of tasks in the checklist.
others_can_add_tasks (:obj:`bool`, optional): :obj:`True` if users other than the creator
of the list can add tasks to the list
others_can_mark_tasks_as_done (:obj:`bool`, optional): :obj:`True` if users other than the
creator of the list can mark tasks as done or not done

Attributes:
title (:obj:`str`): Title of the checklist.
title_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special
entities that appear in the checklist title.
tasks (Tuple[:class:`telegram.ChecklistTask`]): List of tasks in the checklist.
others_can_add_tasks (:obj:`bool`): Optional. :obj:`True` if users other than the creator
of the list can add tasks to the list
others_can_mark_tasks_as_done (:obj:`bool`): Optional. :obj:`True` if users other than the
creator of the list can mark tasks as done or not done
"""

__slots__ = (
"others_can_add_tasks",
"others_can_mark_tasks_as_done",
"tasks",
"title",
"title_entities",
)

def __init__(
self,
title: str,
tasks: Sequence[ChecklistTask],
title_entities: Optional[Sequence[MessageEntity]] = None,
others_can_add_tasks: Optional[bool] = None,
others_can_mark_tasks_as_done: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.title: str = title
self.title_entities: tuple[MessageEntity, ...] = parse_sequence_arg(title_entities)
self.tasks: tuple[ChecklistTask, ...] = parse_sequence_arg(tasks)
self.others_can_add_tasks: Optional[bool] = others_can_add_tasks
self.others_can_mark_tasks_as_done: Optional[bool] = others_can_mark_tasks_as_done

self._id_attrs = (self.tasks,)

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Checklist":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["title_entities"] = de_list_optional(data.get("title_entities"), MessageEntity, bot)
data["tasks"] = de_list_optional(data.get("tasks"), ChecklistTask, bot)

return super().de_json(data=data, bot=bot)

def parse_entity(self, entity: MessageEntity) -> str:
"""Returns the text in :attr:`title`
from a given :class:`telegram.MessageEntity` of :attr:`title_entities`.

Note:
This method is present because Telegram calculates the offset and length in
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
(That is, you can't just slice :attr:`title` with the offset and length.)

Args:
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
be an entity that belongs to :attr:`title_entities`.

Returns:
:obj:`str`: The text of the given entity.
"""
return parse_message_entity(self.title, entity)

def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]:
"""
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
It contains entities from this checklist's title filtered by their ``type`` attribute as
the key, and the text that each entity belongs to as the value of the :obj:`dict`.

Note:
This method should always be used instead of the :attr:`title_entities`
attribute, since it calculates the correct substring from the message text based on
UTF-16 codepoints. See :attr:`parse_entity` for more info.

Args:
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.
"""
return parse_message_entities(self.title, self.title_entities, types)


class ChecklistTasksDone(TelegramObject):
"""
Describes a service message about checklist tasks marked as done or not done.

Objects of this class are comparable in terms of equality.
Two objects of this class are considered equal, if their :attr:`marked_as_done_task_ids` and
:attr:`marked_as_not_done_task_ids` are equal.

.. versionadded:: NEXT.VERSION

Args:
checklist_message (:class:`telegram.Message`, optional): Message containing the checklist
whose tasks were marked as done or not done. Note that the ~:class:`telegram.Message`
object in this field will not contain the :attr:`~telegram.Message.reply_to_message`
field even if it itself is a reply.
marked_as_done_task_ids (Sequence[:obj:`int`], optional): Identifiers of the tasks that
were marked as done
marked_as_not_done_task_ids (Sequence[:obj:`int`], optional): Identifiers of the tasks that
were marked as not done

Attributes:
checklist_message (:class:`telegram.Message`): Optional. Message containing the checklist
whose tasks were marked as done or not done. Note that the ~:class:`telegram.Message`
object in this field will not contain the :attr:`~telegram.Message.reply_to_message`
field even if it itself is a reply.
marked_as_done_task_ids (Tuple[:obj:`int`]): Optional. Identifiers of the tasks that were
marked as done
marked_as_not_done_task_ids (Tuple[:obj:`int`]): Optional. Identifiers of the tasks that
were marked as not done
"""

__slots__ = (
"checklist_message",
"marked_as_done_task_ids",
"marked_as_not_done_task_ids",
)

def __init__(
self,
checklist_message: Optional["Message"] = None,
marked_as_done_task_ids: Optional[Sequence[int]] = None,
marked_as_not_done_task_ids: Optional[Sequence[int]] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.checklist_message: Optional[Message] = checklist_message
self.marked_as_done_task_ids: tuple[int, ...] = parse_sequence_arg(marked_as_done_task_ids)
self.marked_as_not_done_task_ids: tuple[int, ...] = parse_sequence_arg(
marked_as_not_done_task_ids
)

self._id_attrs = (self.marked_as_done_task_ids, self.marked_as_not_done_task_ids)

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChecklistTasksDone":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

# needs to be imported here to avoid circular import issues
from telegram import Message # pylint: disable=import-outside-toplevel

data["checklist_message"] = de_json_optional(data.get("checklist_message"), Message, bot)

return super().de_json(data=data, bot=bot)


class ChecklistTasksAdded(TelegramObject):
"""
Describes a service message about tasks added to a checklist.

Objects of this class are comparable in terms of equality.
Two objects of this class are considered equal, if their :attr:`tasks` are equal.

.. versionadded:: NEXT.VERSION

Args:
checklist_message (:class:`telegram.Message`, optional): Message containing the checklist
to which tasks were added. Note that the ~:class:`telegram.Message`
object in this field will not contain the :attr:`~telegram.Message.reply_to_message`
field even if it itself is a reply.
tasks (Sequence[:class:`telegram.ChecklistTask`]): List of tasks added to the checklist

Attributes:
checklist_message (:class:`telegram.Message`): Optional. Message containing the checklist
to which tasks were added. Note that the ~:class:`telegram.Message`
object in this field will not contain the :attr:`~telegram.Message.reply_to_message`
field even if it itself is a reply.
tasks (Tuple[:class:`telegram.ChecklistTask`]): List of tasks added to the checklist
"""

__slots__ = ("checklist_message", "tasks")

def __init__(
self,
tasks: Sequence[ChecklistTask],
checklist_message: Optional["Message"] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.checklist_message: Optional[Message] = checklist_message
self.tasks: tuple[ChecklistTask, ...] = parse_sequence_arg(tasks)

self._id_attrs = (self.tasks,)

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChecklistTasksAdded":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

# needs to be imported here to avoid circular import issues
from telegram import Message # pylint: disable=import-outside-toplevel

data["checklist_message"] = de_json_optional(data.get("checklist_message"), Message, bot)
data["tasks"] = ChecklistTask.de_list(data.get("tasks", []), bot)

return super().de_json(data=data, bot=bot)
Loading
Loading
0