mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-11-04 01:47:27 +03:00 
			
		
		
		
	Continue implementation
This commit is contained in:
		
							parent
							
								
									4cc6ecc39b
								
							
						
					
					
						commit
						f9435aa1f6
					
				| 
						 | 
				
			
			@ -5,6 +5,8 @@ build:
 | 
			
		|||
  os: ubuntu-22.04
 | 
			
		||||
  tools:
 | 
			
		||||
    python: "3.11"
 | 
			
		||||
  apt_packages:
 | 
			
		||||
    - graphviz
 | 
			
		||||
 | 
			
		||||
sphinx:
 | 
			
		||||
  configuration: client/doc/conf.py
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -318,7 +318,7 @@ In Telethon:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    from telethon import Client, events
 | 
			
		||||
    from telethon.events.filters import Any, Command, TextOnly
 | 
			
		||||
    from telethon.events.filters import Any, Command, Media
 | 
			
		||||
    bot = Client('bot', api_id, api_hash)
 | 
			
		||||
 | 
			
		||||
    # Handle '/start' and '/help'
 | 
			
		||||
| 
						 | 
				
			
			@ -329,8 +329,8 @@ In Telethon:
 | 
			
		|||
    I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
 | 
			
		||||
    """)
 | 
			
		||||
 | 
			
		||||
    # Handle all other messages with only 'text'
 | 
			
		||||
    @bot.on(events.NewMessage, TextOnly())
 | 
			
		||||
    # Handle all other messages without media (negating the filter using ~)
 | 
			
		||||
    @bot.on(events.NewMessage, ~Media())
 | 
			
		||||
    async def echo_message(message: NewMessage):
 | 
			
		||||
        await message.reply(message.text)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ Note that `CommonMark's markdown <https://commonmark.org/>`_ is not fully compat
 | 
			
		|||
    ```
 | 
			
		||||
 | 
			
		||||
HTML is also not fully compatible with :term:`HTTP Bot API`'s
 | 
			
		||||
`MarkdownV2 style <https://core.telegram.org/bots/api#markdownv2-style>`_,
 | 
			
		||||
`HTML style <https://core.telegram.org/bots/api#html-style>`_,
 | 
			
		||||
and instead favours more standard `HTML elements <https://developer.mozilla.org/en-US/docs/Web/HTML/Element>`_:
 | 
			
		||||
 | 
			
		||||
* ``strong`` and ``b`` for **bold**.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,6 +118,7 @@ from .updates import (
 | 
			
		|||
    set_handler_filter,
 | 
			
		||||
)
 | 
			
		||||
from .users import (
 | 
			
		||||
    get_chats,
 | 
			
		||||
    get_contacts,
 | 
			
		||||
    get_me,
 | 
			
		||||
    input_to_peer,
 | 
			
		||||
| 
						 | 
				
			
			@ -683,6 +684,40 @@ class Client:
 | 
			
		|||
        """
 | 
			
		||||
        return get_admin_log(self, chat)
 | 
			
		||||
 | 
			
		||||
    async def get_chats(self, chats: Sequence[ChatLike]) -> List[Chat]:
 | 
			
		||||
        """
 | 
			
		||||
        Get the latest basic information about the given chats.
 | 
			
		||||
 | 
			
		||||
        This method is most commonly used to turn one or more :class:`~types.PackedChat` into the original :class:`~types.Chat`.
 | 
			
		||||
        This includes users, groups and broadcast channels.
 | 
			
		||||
 | 
			
		||||
        :param chats:
 | 
			
		||||
            The users, groups or channels to fetch.
 | 
			
		||||
 | 
			
		||||
        :return: The fetched chats.
 | 
			
		||||
 | 
			
		||||
        .. rubric:: Example
 | 
			
		||||
 | 
			
		||||
        .. code-block:: python
 | 
			
		||||
 | 
			
		||||
            # Retrieve a PackedChat from somewhere
 | 
			
		||||
            packed_user = my_database.get_packed_winner()
 | 
			
		||||
 | 
			
		||||
            # Fetch it
 | 
			
		||||
            users = await client.get_chats([packed_user])
 | 
			
		||||
            user = users[0]  # user will be a User if our packed_user was a user
 | 
			
		||||
 | 
			
		||||
            # Notify the user they won, using their current full name in the message
 | 
			
		||||
            await client.send_message(packed_user, f'Congratulations {user.name}, you won!')
 | 
			
		||||
 | 
			
		||||
        .. caution::
 | 
			
		||||
 | 
			
		||||
            This method supports being called with anything that looks like a chat, like every other method.
 | 
			
		||||
            However, calling it with usernames or phone numbers will fetch the chats twice.
 | 
			
		||||
            If that's the case, consider using :meth:`resolve_username` or :meth:`get_contacts` instead.
 | 
			
		||||
        """
 | 
			
		||||
        return await get_chats(self, chats)
 | 
			
		||||
 | 
			
		||||
    def get_contacts(self) -> AsyncList[User]:
 | 
			
		||||
        """
 | 
			
		||||
        Get the users in your contact list.
 | 
			
		||||
| 
						 | 
				
			
			@ -1200,28 +1235,6 @@ class Client:
 | 
			
		|||
        """
 | 
			
		||||
        return await request_login_code(self, phone)
 | 
			
		||||
 | 
			
		||||
    async def resolve_to_packed(self, chat: ChatLike) -> PackedChat:
 | 
			
		||||
        """
 | 
			
		||||
        Resolve a :term:`chat` and return a compact, reusable reference to it.
 | 
			
		||||
 | 
			
		||||
        :param chat:
 | 
			
		||||
            The :term:`chat` to resolve.
 | 
			
		||||
 | 
			
		||||
        :return: An efficient, reusable version of the input.
 | 
			
		||||
 | 
			
		||||
        .. rubric:: Example
 | 
			
		||||
 | 
			
		||||
        .. code-block:: python
 | 
			
		||||
 | 
			
		||||
            friend = await client.resolve_to_packed('@cat')
 | 
			
		||||
            # Now you can use `friend` to get or send messages, files...
 | 
			
		||||
 | 
			
		||||
        .. seealso::
 | 
			
		||||
 | 
			
		||||
            In-depth explanation for :doc:`/concepts/chats`.
 | 
			
		||||
        """
 | 
			
		||||
        return await resolve_to_packed(self, chat)
 | 
			
		||||
 | 
			
		||||
    async def resolve_username(self, username: str) -> Chat:
 | 
			
		||||
        """
 | 
			
		||||
        Resolve a username into a :term:`chat`.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,5 +154,5 @@ async def dispatch_next(client: Client) -> None:
 | 
			
		|||
            for handler, filter in handlers:
 | 
			
		||||
                if not filter or filter(event):
 | 
			
		||||
                    ret = await handler(event)
 | 
			
		||||
                    if ret is Continue or client._shortcircuit_handlers:
 | 
			
		||||
                    if ret is not Continue or client._shortcircuit_handlers:
 | 
			
		||||
                        return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
from typing import TYPE_CHECKING, Optional
 | 
			
		||||
from typing import TYPE_CHECKING, List, Optional, Sequence
 | 
			
		||||
 | 
			
		||||
from ...mtproto import RpcError
 | 
			
		||||
from ...session import PackedChat, PackedType
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ from ..types import (
 | 
			
		|||
    Group,
 | 
			
		||||
    User,
 | 
			
		||||
    build_chat_map,
 | 
			
		||||
    expand_peer,
 | 
			
		||||
    peer_id,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,12 +74,51 @@ async def resolve_username(self: Client, username: str) -> Chat:
 | 
			
		|||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def resolve_to_packed(self: Client, chat: ChatLike) -> PackedChat:
 | 
			
		||||
async def get_chats(self: Client, chats: Sequence[ChatLike]) -> List[Chat]:
 | 
			
		||||
    packed_chats: List[PackedChat] = []
 | 
			
		||||
    input_users: List[types.InputUser] = []
 | 
			
		||||
    input_chats: List[int] = []
 | 
			
		||||
    input_channels: List[types.InputChannel] = []
 | 
			
		||||
 | 
			
		||||
    for chat in chats:
 | 
			
		||||
        packed = await resolve_to_packed(self, chat)
 | 
			
		||||
        if packed.is_user():
 | 
			
		||||
            input_users.append(packed._to_input_user())
 | 
			
		||||
        elif packed.is_chat():
 | 
			
		||||
            input_chats.append(packed.id)
 | 
			
		||||
        else:
 | 
			
		||||
            input_channels.append(packed._to_input_channel())
 | 
			
		||||
 | 
			
		||||
    users = (
 | 
			
		||||
        (await self(functions.users.get_users(id=input_users))) if input_users else []
 | 
			
		||||
    )
 | 
			
		||||
    groups = (
 | 
			
		||||
        (await self(functions.messages.get_chats(id=input_chats)))
 | 
			
		||||
        if input_chats
 | 
			
		||||
        else []
 | 
			
		||||
    )
 | 
			
		||||
    assert isinstance(groups, types.messages.Chats)
 | 
			
		||||
    channels = (
 | 
			
		||||
        (await self(functions.channels.get_channels(id=input_channels)))
 | 
			
		||||
        if input_channels
 | 
			
		||||
        else []
 | 
			
		||||
    )
 | 
			
		||||
    assert isinstance(channels, types.messages.Chats)
 | 
			
		||||
 | 
			
		||||
    chat_map = build_chat_map(self, users, groups.chats + channels.chats)
 | 
			
		||||
    return [
 | 
			
		||||
        chat_map.get(chat.id)
 | 
			
		||||
        or expand_peer(self, chat._to_peer(), broadcast=chat.ty == PackedType.BROADCAST)
 | 
			
		||||
        for chat in packed_chats
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def resolve_to_packed(client: Client, chat: ChatLike) -> PackedChat:
 | 
			
		||||
    if isinstance(chat, PackedChat):
 | 
			
		||||
        return chat
 | 
			
		||||
 | 
			
		||||
    if isinstance(chat, (User, Group, Channel)):
 | 
			
		||||
        packed = chat.pack() or self._chat_hashes.get(chat.id)
 | 
			
		||||
        packed = chat.pack() or client._chat_hashes.get(chat.id)
 | 
			
		||||
        if packed is not None:
 | 
			
		||||
            return packed
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,11 +136,11 @@ async def resolve_to_packed(self: Client, chat: ChatLike) -> PackedChat:
 | 
			
		|||
        if isinstance(chat, types.InputPeerEmpty):
 | 
			
		||||
            raise ValueError("Cannot resolve chat")
 | 
			
		||||
        elif isinstance(chat, types.InputPeerSelf):
 | 
			
		||||
            if not self._session.user:
 | 
			
		||||
            if not client._session.user:
 | 
			
		||||
                raise ValueError("Cannot resolve chat")
 | 
			
		||||
            return PackedChat(
 | 
			
		||||
                ty=PackedType.BOT if self._session.user.bot else PackedType.USER,
 | 
			
		||||
                id=self._chat_hashes.self_id,
 | 
			
		||||
                ty=PackedType.BOT if client._session.user.bot else PackedType.USER,
 | 
			
		||||
                id=client._chat_hashes.self_id,
 | 
			
		||||
                access_hash=0,
 | 
			
		||||
            )
 | 
			
		||||
        elif isinstance(chat, types.InputPeerChat):
 | 
			
		||||
| 
						 | 
				
			
			@ -130,9 +170,9 @@ async def resolve_to_packed(self: Client, chat: ChatLike) -> PackedChat:
 | 
			
		|||
 | 
			
		||||
    if isinstance(chat, str):
 | 
			
		||||
        if chat.startswith("+"):
 | 
			
		||||
            resolved = await resolve_phone(self, chat)
 | 
			
		||||
            resolved = await resolve_phone(client, chat)
 | 
			
		||||
        elif chat == "me":
 | 
			
		||||
            if me := self._session.user:
 | 
			
		||||
            if me := client._session.user:
 | 
			
		||||
                return PackedChat(
 | 
			
		||||
                    ty=PackedType.BOT if me.bot else PackedType.USER,
 | 
			
		||||
                    id=me.id,
 | 
			
		||||
| 
						 | 
				
			
			@ -141,13 +181,13 @@ async def resolve_to_packed(self: Client, chat: ChatLike) -> PackedChat:
 | 
			
		|||
            else:
 | 
			
		||||
                resolved = None
 | 
			
		||||
        else:
 | 
			
		||||
            resolved = await resolve_username(self, username=chat)
 | 
			
		||||
            resolved = await resolve_username(client, username=chat)
 | 
			
		||||
 | 
			
		||||
        if resolved and (packed := resolved.pack()) is not None:
 | 
			
		||||
            return packed
 | 
			
		||||
 | 
			
		||||
    if isinstance(chat, int):
 | 
			
		||||
        packed = self._chat_hashes.get(chat)
 | 
			
		||||
        packed = client._chat_hashes.get(chat)
 | 
			
		||||
        if packed is None:
 | 
			
		||||
            raise ValueError("Cannot resolve chat")
 | 
			
		||||
        return packed
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
from .combinators import All, Any, Filter, Not
 | 
			
		||||
from .common import Chats, Senders
 | 
			
		||||
from .messages import Command, Forward, Incoming, Media, Outgoing, Reply, Text, TextOnly
 | 
			
		||||
from .common import Chats, ChatType, Senders
 | 
			
		||||
from .messages import Command, Forward, Incoming, Media, Outgoing, Reply, Text
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "All",
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ __all__ = [
 | 
			
		|||
    "Filter",
 | 
			
		||||
    "Not",
 | 
			
		||||
    "Chats",
 | 
			
		||||
    "ChatType",
 | 
			
		||||
    "Senders",
 | 
			
		||||
    "Command",
 | 
			
		||||
    "Forward",
 | 
			
		||||
| 
						 | 
				
			
			@ -16,5 +17,4 @@ __all__ = [
 | 
			
		|||
    "Outgoing",
 | 
			
		||||
    "Reply",
 | 
			
		||||
    "Text",
 | 
			
		||||
    "TextOnly",
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
import abc
 | 
			
		||||
import typing
 | 
			
		||||
from typing import Callable, Tuple
 | 
			
		||||
from typing import Callable, Tuple, TypeAlias
 | 
			
		||||
 | 
			
		||||
from ..event import Event
 | 
			
		||||
 | 
			
		||||
Filter = Callable[[Event], bool]
 | 
			
		||||
Filter: TypeAlias = Callable[[Event], bool]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Combinable(abc.ABC):
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +48,12 @@ class Any(Combinable):
 | 
			
		|||
    """
 | 
			
		||||
    Combine multiple filters, returning :data:`True` if any of the filters pass.
 | 
			
		||||
 | 
			
		||||
    When either filter is *combinable*, you can use the ``|`` operator instead.
 | 
			
		||||
    When either filter is :class:`~telethon._impl.client.events.filters.combinators.Combinable`,
 | 
			
		||||
    you can use the ``|`` operator instead.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        from telethon.filters import Any, Command
 | 
			
		||||
        from telethon.events.filters import Any, Command
 | 
			
		||||
 | 
			
		||||
        @bot.on(events.NewMessage, Any(Command('/start'), Command('/help')))
 | 
			
		||||
        async def handler(event): ...
 | 
			
		||||
| 
						 | 
				
			
			@ -87,11 +88,12 @@ class All(Combinable):
 | 
			
		|||
    """
 | 
			
		||||
    Combine multiple filters, returning :data:`True` if all of the filters pass.
 | 
			
		||||
 | 
			
		||||
    When either filter is *combinable*, you can use the ``&`` operator instead.
 | 
			
		||||
    When either filter is :class:`~telethon._impl.client.events.filters.combinators.Combinable`,
 | 
			
		||||
    you can use the ``&`` operator instead.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        from telethon.filters import All, Command, Text
 | 
			
		||||
        from telethon.events.filters import All, Command, Text
 | 
			
		||||
 | 
			
		||||
        @bot.on(events.NewMessage, All(Command('/start'), Text(r'\bdata:\w+')))
 | 
			
		||||
        async def handler(event): ...
 | 
			
		||||
| 
						 | 
				
			
			@ -126,11 +128,12 @@ class Not(Combinable):
 | 
			
		|||
    """
 | 
			
		||||
    Negate the output of a single filter, returning :data:`True` if the nested filter does *not* pass.
 | 
			
		||||
 | 
			
		||||
    When the filter is *combinable*, you can use the ``~`` operator instead.
 | 
			
		||||
    When the filter is :class:`~telethon._impl.client.events.filters.combinators.Combinable`,
 | 
			
		||||
    you can use the ``~`` operator instead.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        from telethon.filters import All, Command
 | 
			
		||||
        from telethon.events.filters import All, Command
 | 
			
		||||
 | 
			
		||||
        @bot.on(events.NewMessage, Not(Command('/start'))
 | 
			
		||||
        async def handler(event): ...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
from typing import Literal, Sequence, Tuple, Type, Union
 | 
			
		||||
from typing import Sequence, Set, Type, Union
 | 
			
		||||
 | 
			
		||||
from ...types import Channel, Group, User
 | 
			
		||||
from ..event import Event
 | 
			
		||||
| 
						 | 
				
			
			@ -8,20 +8,21 @@ from .combinators import Combinable
 | 
			
		|||
class Chats(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.chat.id``, if the event has a chat.
 | 
			
		||||
 | 
			
		||||
    :param chat_ids: The chat identifiers to filter on.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ("_chats",)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, chat_id: Union[int, Sequence[int]], *chat_ids: int) -> None:
 | 
			
		||||
        self._chats = {chat_id} if isinstance(chat_id, int) else set(chat_id)
 | 
			
		||||
        self._chats.update(chat_ids)
 | 
			
		||||
    def __init__(self, chat_ids: Sequence[int]) -> None:
 | 
			
		||||
        self._chats = set(chat_ids)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def chat_ids(self) -> Tuple[int, ...]:
 | 
			
		||||
    def chat_ids(self) -> Set[int]:
 | 
			
		||||
        """
 | 
			
		||||
        The chat identifiers this filter is filtering on.
 | 
			
		||||
        A copy of the set of chat identifiers this filter is filtering on.
 | 
			
		||||
        """
 | 
			
		||||
        return tuple(self._chats)
 | 
			
		||||
        return set(self._chats)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, event: Event) -> bool:
 | 
			
		||||
        chat = getattr(event, "chat", None)
 | 
			
		||||
| 
						 | 
				
			
			@ -32,20 +33,21 @@ class Chats(Combinable):
 | 
			
		|||
class Senders(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.sender.id``, if the event has a sender.
 | 
			
		||||
 | 
			
		||||
    :param sender_ids: The sender identifiers to filter on.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ("_senders",)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, sender_id: Union[int, Sequence[int]], *sender_ids: int) -> None:
 | 
			
		||||
        self._senders = {sender_id} if isinstance(sender_id, int) else set(sender_id)
 | 
			
		||||
        self._senders.update(sender_ids)
 | 
			
		||||
    def __init__(self, sender_ids: Sequence[int]) -> None:
 | 
			
		||||
        self._senders = set(sender_ids)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def sender_ids(self) -> Tuple[int, ...]:
 | 
			
		||||
    def sender_ids(self) -> Set[int]:
 | 
			
		||||
        """
 | 
			
		||||
        The sender identifiers this filter is filtering on.
 | 
			
		||||
        A copy of the set of sender identifiers this filter is filtering on.
 | 
			
		||||
        """
 | 
			
		||||
        return tuple(self._senders)
 | 
			
		||||
        return set(self._senders)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, event: Event) -> bool:
 | 
			
		||||
        sender = getattr(event, "sender", None)
 | 
			
		||||
| 
						 | 
				
			
			@ -55,37 +57,38 @@ class Senders(Combinable):
 | 
			
		|||
 | 
			
		||||
class ChatType(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by chat type, either ``'user'``, ``'group'`` or ``'broadcast'``.
 | 
			
		||||
    Filter by chat type using :func:`isinstance`.
 | 
			
		||||
 | 
			
		||||
    :param type: The chat type to filter on.
 | 
			
		||||
 | 
			
		||||
    .. rubric:: Example
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        from telethon import events
 | 
			
		||||
        from telethon.events import filters
 | 
			
		||||
        from telethon.types import Channel
 | 
			
		||||
 | 
			
		||||
        # Handle only messages from broadcast channels
 | 
			
		||||
        @client.on(events.NewMessage, filters.ChatType(Channel))
 | 
			
		||||
        async def handler(event):
 | 
			
		||||
            print(event.text)
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ("_type",)
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        type: Union[Literal["user"], Literal["group"], Literal["broadcast"]],
 | 
			
		||||
        type: Type[Union[User, Group, Channel]],
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        if type == "user":
 | 
			
		||||
            self._type: Union[Type[User], Type[Group], Type[Channel]] = User
 | 
			
		||||
        elif type == "group":
 | 
			
		||||
            self._type = Group
 | 
			
		||||
        elif type == "broadcast":
 | 
			
		||||
            self._type = Channel
 | 
			
		||||
        else:
 | 
			
		||||
            raise TypeError(f"unrecognised chat type: {type}")
 | 
			
		||||
        self._type = type
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def type(self) -> Union[Literal["user"], Literal["group"], Literal["broadcast"]]:
 | 
			
		||||
    def type(self) -> Type[Union[User, Group, Channel]]:
 | 
			
		||||
        """
 | 
			
		||||
        The chat type this filter is filtering on.
 | 
			
		||||
        """
 | 
			
		||||
        if self._type == User:
 | 
			
		||||
            return "user"
 | 
			
		||||
        elif self._type == Group:
 | 
			
		||||
            return "group"
 | 
			
		||||
        elif self._type == Channel:
 | 
			
		||||
            return "broadcast"
 | 
			
		||||
        else:
 | 
			
		||||
            raise RuntimeError("unexpected case")
 | 
			
		||||
        return self._type
 | 
			
		||||
 | 
			
		||||
    def __call__(self, event: Event) -> bool:
 | 
			
		||||
        sender = getattr(event, "chat", None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,9 @@ class Text(Combinable):
 | 
			
		|||
    you need to manually perform the check inside the handler instead.
 | 
			
		||||
 | 
			
		||||
    Note that the caption text in messages with media is also searched.
 | 
			
		||||
    If you want to filter based on media, use :class:`TextOnly` or :class:`Media`.
 | 
			
		||||
    If you want to filter based on media, use :class:`Media`.
 | 
			
		||||
 | 
			
		||||
    :param regexp: The regular expression to :func:`re.search` with on the text.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ("_pattern",)
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +45,8 @@ class Command(Combinable):
 | 
			
		|||
    filter ``Command('/help')`` will match both ``"/help"`` and ``"/help@bot"``, but not
 | 
			
		||||
    ``"/list"`` or ``"/help@other"``.
 | 
			
		||||
 | 
			
		||||
    :param command: The command to match on.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        The leading forward-slash is not automatically added!
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +62,7 @@ class Command(Combinable):
 | 
			
		|||
    __slots__ = ("_cmd", "_username")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, command: str) -> None:
 | 
			
		||||
        if re.match(r"\s", command):
 | 
			
		||||
        if re.search(r"\s", command):
 | 
			
		||||
            raise ValueError(f"command cannot contain spaces: {command}")
 | 
			
		||||
 | 
			
		||||
        self._cmd = command
 | 
			
		||||
| 
						 | 
				
			
			@ -87,11 +91,7 @@ class Command(Combinable):
 | 
			
		|||
 | 
			
		||||
class Incoming(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.incoming``, that is, messages sent from others to the
 | 
			
		||||
    logged-in account.
 | 
			
		||||
 | 
			
		||||
    This is not a reliable way to check that the update was not produced by
 | 
			
		||||
    the logged-in account in broadcast channels.
 | 
			
		||||
    Filter by ``event.incoming``, that is, messages sent from others to the logged-in account.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ()
 | 
			
		||||
| 
						 | 
				
			
			@ -102,11 +102,9 @@ class Incoming(Combinable):
 | 
			
		|||
 | 
			
		||||
class Outgoing(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.outgoing``, that is, messages sent from others to the
 | 
			
		||||
    logged-in account.
 | 
			
		||||
    Filter by ``event.outgoing``, that is, messages sent from the logged-in account.
 | 
			
		||||
 | 
			
		||||
    This is not a reliable way to check that the update was not produced by
 | 
			
		||||
    the logged-in account in broadcast channels.
 | 
			
		||||
    This is not a reliable way to check that the update was not produced by the logged-in account in broadcast channels.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ()
 | 
			
		||||
| 
						 | 
				
			
			@ -117,32 +115,24 @@ class Outgoing(Combinable):
 | 
			
		|||
 | 
			
		||||
class Forward(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.forward``.
 | 
			
		||||
    Filter by ``event.forward_info``, that is, messages that have been forwarded from elsewhere.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ()
 | 
			
		||||
 | 
			
		||||
    def __call__(self, event: Event) -> bool:
 | 
			
		||||
        return getattr(event, "forward", None) is not None
 | 
			
		||||
        return getattr(event, "forward_info", None) is not None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Reply(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by ``event.reply``.
 | 
			
		||||
    Filter by ``event.replied_message_id``, that is, messages which are a reply to another message.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ()
 | 
			
		||||
 | 
			
		||||
    def __call__(self, event: Event) -> bool:
 | 
			
		||||
        return getattr(event, "reply", None) is not None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextOnly(Combinable):
 | 
			
		||||
    """
 | 
			
		||||
    Filter by messages with some text and no media.
 | 
			
		||||
 | 
			
		||||
    Note that link previews are only considered media if they have a photo or document.
 | 
			
		||||
    """
 | 
			
		||||
        return getattr(event, "replied_message_id", None) is not None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Media(Combinable):
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +146,10 @@ class Media(Combinable):
 | 
			
		|||
    When you specify one or more media types, *only* those types will be considered.
 | 
			
		||||
 | 
			
		||||
    You can use literal strings or the constants defined by the filter.
 | 
			
		||||
 | 
			
		||||
    :param types:
 | 
			
		||||
        The media types to filter on.
 | 
			
		||||
        This is all of them if none are specified.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    PHOTO = "photo"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
from typing import TYPE_CHECKING, Dict, List, Optional, Self
 | 
			
		||||
from typing import TYPE_CHECKING, Dict, List, Optional, Self, Union
 | 
			
		||||
 | 
			
		||||
from ...tl import abcs, types
 | 
			
		||||
from ..types import Chat, Message
 | 
			
		||||
from ..types import Chat, Message, expand_peer, peer_id
 | 
			
		||||
from .event import Event
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,8 @@ class NewMessage(Event, Message):
 | 
			
		|||
    """
 | 
			
		||||
    Occurs when a new message is sent or received.
 | 
			
		||||
 | 
			
		||||
    This event can be treated as the :class:`~telethon.types.Message` itself.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        Messages sent with the :class:`~telethon.Client` are also caught,
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +41,8 @@ class NewMessage(Event, Message):
 | 
			
		|||
class MessageEdited(Event, Message):
 | 
			
		||||
    """
 | 
			
		||||
    Occurs when a new message is sent or received.
 | 
			
		||||
 | 
			
		||||
    This event can be treated as the :class:`~telethon.types.Message` itself.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
| 
						 | 
				
			
			@ -80,32 +84,96 @@ class MessageDeleted(Event):
 | 
			
		|||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def message_ids(self) -> List[int]:
 | 
			
		||||
        """
 | 
			
		||||
        The message identifiers of the messages that were deleted.
 | 
			
		||||
        """
 | 
			
		||||
        return self._msg_ids
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def channel_id(self) -> Optional[int]:
 | 
			
		||||
        """
 | 
			
		||||
        The channel identifier of the supergroup or broadcast channel where the messages were deleted.
 | 
			
		||||
 | 
			
		||||
        This will be :data:`None` if the messages were deleted anywhere else.
 | 
			
		||||
        """
 | 
			
		||||
        return self._channel_id
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MessageRead(Event):
 | 
			
		||||
    """
 | 
			
		||||
    Occurs both when your messages are read by others, and when you read messages.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, peer: abcs.Peer, max_id: int, out: bool) -> None:
 | 
			
		||||
        self._peer = peer
 | 
			
		||||
        self._max_id = max_id
 | 
			
		||||
        self._out = out
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        client: Client,
 | 
			
		||||
        update: Union[
 | 
			
		||||
            types.UpdateReadHistoryInbox,
 | 
			
		||||
            types.UpdateReadHistoryOutbox,
 | 
			
		||||
            types.UpdateReadChannelInbox,
 | 
			
		||||
            types.UpdateReadChannelOutbox,
 | 
			
		||||
        ],
 | 
			
		||||
        chat_map: Dict[int, Chat],
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        self._client = client
 | 
			
		||||
        self._raw = update
 | 
			
		||||
        self._chat_map = chat_map
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _try_from_update(
 | 
			
		||||
        cls, client: Client, update: abcs.Update, chat_map: Dict[int, Chat]
 | 
			
		||||
    ) -> Optional[Self]:
 | 
			
		||||
        if isinstance(update, types.UpdateReadHistoryInbox):
 | 
			
		||||
            return cls._create(update.peer, update.max_id, False)
 | 
			
		||||
        elif isinstance(update, types.UpdateReadHistoryOutbox):
 | 
			
		||||
            return cls._create(update.peer, update.max_id, True)
 | 
			
		||||
        elif isinstance(update, types.UpdateReadChannelInbox):
 | 
			
		||||
            return cls._create(
 | 
			
		||||
                types.PeerChannel(channel_id=update.channel_id), update.max_id, False
 | 
			
		||||
            )
 | 
			
		||||
        elif isinstance(update, types.UpdateReadChannelOutbox):
 | 
			
		||||
            return cls._create(
 | 
			
		||||
                types.PeerChannel(channel_id=update.channel_id), update.max_id, True
 | 
			
		||||
            )
 | 
			
		||||
        if isinstance(
 | 
			
		||||
            update,
 | 
			
		||||
            (
 | 
			
		||||
                types.UpdateReadHistoryInbox,
 | 
			
		||||
                types.UpdateReadHistoryOutbox,
 | 
			
		||||
                types.UpdateReadChannelInbox,
 | 
			
		||||
                types.UpdateReadChannelOutbox,
 | 
			
		||||
            ),
 | 
			
		||||
        ):
 | 
			
		||||
            return cls._create(client, update, chat_map)
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def _peer(self) -> abcs.Peer:
 | 
			
		||||
        if isinstance(
 | 
			
		||||
            self._raw, (types.UpdateReadHistoryInbox, types.UpdateReadHistoryOutbox)
 | 
			
		||||
        ):
 | 
			
		||||
            return self._raw.peer
 | 
			
		||||
        else:
 | 
			
		||||
            return types.PeerChannel(channel_id=self._raw.channel_id)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def chat(self) -> Chat:
 | 
			
		||||
        """
 | 
			
		||||
        The :term:`chat` when the messages were read.
 | 
			
		||||
        """
 | 
			
		||||
        peer = self._peer()
 | 
			
		||||
        pid = peer_id(peer)
 | 
			
		||||
        if pid not in self._chat_map:
 | 
			
		||||
            self._chat_map[pid] = expand_peer(
 | 
			
		||||
                self._client, peer, broadcast=getattr(self._raw, "post", None)
 | 
			
		||||
            )
 | 
			
		||||
        return self._chat_map[pid]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def max_message_id_read(self) -> int:
 | 
			
		||||
        """
 | 
			
		||||
        The highest message identifier of the messages that have been marked as read.
 | 
			
		||||
 | 
			
		||||
        In other words, messages with an identifier below or equal (``<=``) to this value are considered read.
 | 
			
		||||
        Messages with an identifier higher (``>``) to this value are considered unread.
 | 
			
		||||
 | 
			
		||||
        .. rubric:: Example
 | 
			
		||||
 | 
			
		||||
        .. code-block:: python
 | 
			
		||||
 | 
			
		||||
            if message.id <= event.max_message_id_read:
 | 
			
		||||
                print('message is marked as read')
 | 
			
		||||
            else:
 | 
			
		||||
                print('message is not yet marked as read')
 | 
			
		||||
        """
 | 
			
		||||
        return self._raw.max_id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -163,7 +163,7 @@ class Draft(metaclass=NoPublicConstructor):
 | 
			
		|||
        if chat := self._chat_map.get(peer_id(self._peer)):
 | 
			
		||||
            packed = chat.pack()
 | 
			
		||||
        if packed is None:
 | 
			
		||||
            packed = await self._client.resolve_to_packed(peer_id(self._peer))
 | 
			
		||||
            packed = await self._client._resolve_to_packed(peer_id(self._peer))
 | 
			
		||||
        return packed
 | 
			
		||||
 | 
			
		||||
    async def send(self) -> Message:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -281,6 +281,26 @@ class Message(metaclass=NoPublicConstructor):
 | 
			
		|||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def incoming(self) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        :data:`True` if the message is incoming.
 | 
			
		||||
        This would mean another user sent it, and the currently logged-in user received it.
 | 
			
		||||
 | 
			
		||||
        This is usually the opposite of :attr:`outgoing`, although some messages can be neither.
 | 
			
		||||
        """
 | 
			
		||||
        return getattr(self._raw, "out", None) is False
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def outgoing(self) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        :data:`True` if the message is outgoing.
 | 
			
		||||
        This would mean the currently logged-in user sent it.
 | 
			
		||||
 | 
			
		||||
        This is usually the opposite of :attr:`incoming`, although some messages can be neither.
 | 
			
		||||
        """
 | 
			
		||||
        return getattr(self._raw, "out", None) is True
 | 
			
		||||
 | 
			
		||||
    async def get_replied_message(self) -> Optional[Message]:
 | 
			
		||||
        """
 | 
			
		||||
        Alias for :meth:`telethon.Client.get_messages_with_ids`.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ from .._impl.client.events.filters import (
 | 
			
		|||
    All,
 | 
			
		||||
    Any,
 | 
			
		||||
    Chats,
 | 
			
		||||
    ChatType,
 | 
			
		||||
    Command,
 | 
			
		||||
    Filter,
 | 
			
		||||
    Forward,
 | 
			
		||||
| 
						 | 
				
			
			@ -21,13 +22,13 @@ from .._impl.client.events.filters import (
 | 
			
		|||
    Reply,
 | 
			
		||||
    Senders,
 | 
			
		||||
    Text,
 | 
			
		||||
    TextOnly,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "All",
 | 
			
		||||
    "Any",
 | 
			
		||||
    "Chats",
 | 
			
		||||
    "ChatType",
 | 
			
		||||
    "Command",
 | 
			
		||||
    "Filter",
 | 
			
		||||
    "Forward",
 | 
			
		||||
| 
						 | 
				
			
			@ -38,5 +39,4 @@ __all__ = [
 | 
			
		|||
    "Reply",
 | 
			
		||||
    "Senders",
 | 
			
		||||
    "Text",
 | 
			
		||||
    "TextOnly",
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user