mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-02 03:00:15 +03:00
Add type-hinting (ChatGetter, Events) and fix bugs
This commit is contained in:
parent
b754d4fbd5
commit
2073da83d4
|
@ -1,14 +1,17 @@
|
||||||
import abc
|
import abc
|
||||||
import asyncio
|
import asyncio
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Optional, Sequence, Callable
|
from typing import Optional, Sequence, Callable, TYPE_CHECKING
|
||||||
|
|
||||||
from .. import utils, TelegramClient, hints
|
from .. import utils, hints
|
||||||
from ..tl import TLObject, types
|
from ..tl import TLObject, types
|
||||||
from ..tl.custom.chatgetter import ChatGetter
|
from ..tl.custom.chatgetter import ChatGetter
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .. import TelegramClient
|
||||||
|
|
||||||
async def _into_id_set(client, chats):
|
|
||||||
|
async def _into_id_set(client: 'TelegramClient', chats):
|
||||||
"""Helper util to turn the input chat or chats into a set of IDs."""
|
"""Helper util to turn the input chat or chats into a set of IDs."""
|
||||||
if chats is None:
|
if chats is None:
|
||||||
return None
|
return None
|
||||||
|
@ -66,8 +69,11 @@ class EventBuilder(abc.ABC):
|
||||||
async def handler(event):
|
async def handler(event):
|
||||||
pass # code here
|
pass # code here
|
||||||
"""
|
"""
|
||||||
def __init__(self, chats: Optional[Sequence[hints.Entity]] = None, *,
|
def __init__(self,
|
||||||
blacklist_chats: bool = False, func: Optional[Callable[['EventCommon'], None]] = None):
|
chats: Optional[Sequence[hints.Entity]] = None,
|
||||||
|
*,
|
||||||
|
blacklist_chats: bool = False,
|
||||||
|
func: Optional[Callable[['EventCommon'], None]] = None):
|
||||||
self.chats = chats
|
self.chats = chats
|
||||||
self.blacklist_chats = bool(blacklist_chats)
|
self.blacklist_chats = bool(blacklist_chats)
|
||||||
self.resolved = False
|
self.resolved = False
|
||||||
|
@ -76,7 +82,7 @@ class EventBuilder(abc.ABC):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def build(cls, update, others=None, self_id=None):
|
def build(cls, update: types.TypeUpdate, others=None, self_id=None):
|
||||||
"""
|
"""
|
||||||
Builds an event for the given update if possible, or returns None.
|
Builds an event for the given update if possible, or returns None.
|
||||||
|
|
||||||
|
@ -88,7 +94,7 @@ class EventBuilder(abc.ABC):
|
||||||
"""
|
"""
|
||||||
# TODO So many parameters specific to only some update types seems dirty
|
# TODO So many parameters specific to only some update types seems dirty
|
||||||
|
|
||||||
async def resolve(self, client):
|
async def resolve(self, client: 'TelegramClient'):
|
||||||
"""Helper method to allow event builders to be resolved before usage"""
|
"""Helper method to allow event builders to be resolved before usage"""
|
||||||
if self.resolved:
|
if self.resolved:
|
||||||
return
|
return
|
||||||
|
@ -101,7 +107,7 @@ class EventBuilder(abc.ABC):
|
||||||
await self._resolve(client)
|
await self._resolve(client)
|
||||||
self.resolved = True
|
self.resolved = True
|
||||||
|
|
||||||
async def _resolve(self, client):
|
async def _resolve(self, client: 'TelegramClient'):
|
||||||
self.chats = await _into_id_set(client, self.chats)
|
self.chats = await _into_id_set(client, self.chats)
|
||||||
|
|
||||||
def filter(self, event: 'EventCommon'):
|
def filter(self, event: 'EventCommon'):
|
||||||
|
@ -142,14 +148,17 @@ class EventCommon(ChatGetter, abc.ABC):
|
||||||
"""
|
"""
|
||||||
_event_name = 'Event'
|
_event_name = 'Event'
|
||||||
|
|
||||||
def __init__(self, chat_peer=None, msg_id=None, broadcast=None):
|
def __init__(self,
|
||||||
|
chat_peer: Optional[types.TypePeer] = None,
|
||||||
|
msg_id: Optional[int] = None,
|
||||||
|
broadcast: Optional[bool] = None):
|
||||||
super().__init__(chat_peer, broadcast=broadcast)
|
super().__init__(chat_peer, broadcast=broadcast)
|
||||||
self._entities = {}
|
self._entities = {}
|
||||||
self._client = None
|
self._client = None
|
||||||
self._message_id = msg_id
|
self._message_id = msg_id
|
||||||
self.original_update = None # type: Optional[types.TypeUpdate]
|
self.original_update = None # type: Optional[types.TypeUpdate]
|
||||||
|
|
||||||
def _set_client(self, client: TelegramClient):
|
def _set_client(self, client: 'TelegramClient'):
|
||||||
"""
|
"""
|
||||||
Setter so subclasses can act accordingly when the client is set.
|
Setter so subclasses can act accordingly when the client is set.
|
||||||
"""
|
"""
|
||||||
|
@ -161,7 +170,7 @@ class EventCommon(ChatGetter, abc.ABC):
|
||||||
self._chat = self._input_chat = None
|
self._chat = self._input_chat = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self) -> TelegramClient:
|
def client(self) -> 'TelegramClient':
|
||||||
"""
|
"""
|
||||||
The `telethon.TelegramClient` that created this event.
|
The `telethon.TelegramClient` that created this event.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -49,9 +49,11 @@ class InlineQuery(EventBuilder):
|
||||||
])
|
])
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, users: Optional[Sequence[hints.EntityLike]] = None, *,
|
self,
|
||||||
blacklist_users: bool = False, func: Optional[Callable[['InlineQuery.Event'], None]] = None,
|
users: Optional[Sequence[hints.EntityLike]] = None, *,
|
||||||
pattern: Union[str, Callable, Pattern, Optional] = None):
|
blacklist_users: bool = False,
|
||||||
|
func: Optional[Callable[['InlineQuery.Event'], None]] = None,
|
||||||
|
pattern: Union[str, Callable, Pattern, None] = None):
|
||||||
super().__init__(users, blacklist_chats=blacklist_users, func=func)
|
super().__init__(users, blacklist_chats=blacklist_users, func=func)
|
||||||
|
|
||||||
if isinstance(pattern, str):
|
if isinstance(pattern, str):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Sequence, Optional
|
||||||
|
|
||||||
from .common import EventBuilder, EventCommon, name_inner_event
|
from .common import EventBuilder, EventCommon, name_inner_event
|
||||||
from ..tl import types
|
from ..tl import types
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ class MessageDeleted(EventBuilder):
|
||||||
print('Message', msg_id, 'was deleted in', event.chat_id)
|
print('Message', msg_id, 'was deleted in', event.chat_id)
|
||||||
"""
|
"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, update, others=None, self_id=None):
|
def build(cls, update: types.TypeUpdate, others=None, self_id=None):
|
||||||
if isinstance(update, types.UpdateDeleteMessages):
|
if isinstance(update, types.UpdateDeleteMessages):
|
||||||
return cls.Event(
|
return cls.Event(
|
||||||
deleted_ids=update.messages,
|
deleted_ids=update.messages,
|
||||||
|
@ -49,7 +51,9 @@ class MessageDeleted(EventBuilder):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Event(EventCommon):
|
class Event(EventCommon):
|
||||||
def __init__(self, deleted_ids, peer):
|
def __init__(self,
|
||||||
|
deleted_ids: Optional[Sequence[int]],
|
||||||
|
peer: Optional[types.TypePeer]):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
chat_peer=peer, msg_id=(deleted_ids or [0])[0]
|
chat_peer=peer, msg_id=(deleted_ids or [0])[0]
|
||||||
)
|
)
|
||||||
|
|
|
@ -43,7 +43,7 @@ class MessageEdited(NewMessage):
|
||||||
print('Message', event.id, 'changed at', event.date)
|
print('Message', event.id, 'changed at', event.date)
|
||||||
"""
|
"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, update, others=None, self_id=None):
|
def build(cls, update: types.TypeUpdate, others=None, self_id=None):
|
||||||
if isinstance(update, (types.UpdateEditMessage,
|
if isinstance(update, (types.UpdateEditMessage,
|
||||||
types.UpdateEditChannelMessage)):
|
types.UpdateEditChannelMessage)):
|
||||||
return cls.Event(update.message)
|
return cls.Event(update.message)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
from typing import Optional, Sequence, Callable
|
||||||
|
|
||||||
from .common import EventBuilder, EventCommon, name_inner_event
|
from .common import EventBuilder, EventCommon, name_inner_event
|
||||||
from .. import utils
|
from .. import utils, hints
|
||||||
from ..tl import types
|
from ..tl import types
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,13 +31,16 @@ class MessageRead(EventBuilder):
|
||||||
# Log when you read message in a chat (from your "inbox")
|
# Log when you read message in a chat (from your "inbox")
|
||||||
print('You have read messages until', event.max_id)
|
print('You have read messages until', event.max_id)
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(self,
|
||||||
self, chats=None, *, blacklist_chats=False, func=None, inbox=False):
|
chats: Optional[Sequence[hints.Entity]] = None, *,
|
||||||
|
blacklist_chats: bool = False,
|
||||||
|
func: Optional[Callable[['MessageRead.Event'], None]] = None,
|
||||||
|
inbox: bool = False):
|
||||||
super().__init__(chats, blacklist_chats=blacklist_chats, func=func)
|
super().__init__(chats, blacklist_chats=blacklist_chats, func=func)
|
||||||
self.inbox = inbox
|
self.inbox = inbox
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, update, others=None, self_id=None):
|
def build(cls, update: types.TypeUpdate, others=None, self_id=None):
|
||||||
if isinstance(update, types.UpdateReadHistoryInbox):
|
if isinstance(update, types.UpdateReadHistoryInbox):
|
||||||
return cls.Event(update.peer, update.max_id, False)
|
return cls.Event(update.peer, update.max_id, False)
|
||||||
elif isinstance(update, types.UpdateReadHistoryOutbox):
|
elif isinstance(update, types.UpdateReadHistoryOutbox):
|
||||||
|
@ -54,7 +59,7 @@ class MessageRead(EventBuilder):
|
||||||
message_ids=update.messages,
|
message_ids=update.messages,
|
||||||
contents=True)
|
contents=True)
|
||||||
|
|
||||||
def filter(self, event):
|
def filter(self, event: 'MessageRead.Event'):
|
||||||
if self.inbox == event.outbox:
|
if self.inbox == event.outbox:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -77,8 +82,12 @@ class MessageRead(EventBuilder):
|
||||||
This will be the case when e.g. you play a voice note.
|
This will be the case when e.g. you play a voice note.
|
||||||
It may only be set on ``inbox`` events.
|
It may only be set on ``inbox`` events.
|
||||||
"""
|
"""
|
||||||
def __init__(self, peer=None, max_id=None, out=False, contents=False,
|
def __init__(self,
|
||||||
message_ids=None):
|
peer: Optional[types.TypePeer] = None,
|
||||||
|
max_id: Optional[int] = None,
|
||||||
|
out: bool = False,
|
||||||
|
contents: bool = False,
|
||||||
|
message_ids: Optional[Sequence[int]] = None):
|
||||||
self.outbox = out
|
self.outbox = out
|
||||||
self.contents = contents
|
self.contents = contents
|
||||||
self._message_ids = message_ids or []
|
self._message_ids = message_ids or []
|
||||||
|
|
|
@ -61,7 +61,7 @@ class NewMessage(EventBuilder):
|
||||||
func: Optional[Callable[['NewMessage.Event'], None]] = None,
|
func: Optional[Callable[['NewMessage.Event'], None]] = None,
|
||||||
incoming: Optional[bool] = None, outgoing: Optional[bool] = None,
|
incoming: Optional[bool] = None, outgoing: Optional[bool] = None,
|
||||||
from_users: Optional[hints.Entity] = None, forwards: Optional[bool] = None,
|
from_users: Optional[hints.Entity] = None, forwards: Optional[bool] = None,
|
||||||
pattern: Union[str, Callable, Pattern, Optional] = None):
|
pattern: Union[str, Callable, Pattern, None] = None):
|
||||||
if incoming and outgoing:
|
if incoming and outgoing:
|
||||||
incoming = outgoing = None # Same as no filter
|
incoming = outgoing = None # Same as no filter
|
||||||
elif incoming is not None and outgoing is None:
|
elif incoming is not None and outgoing is None:
|
||||||
|
@ -170,7 +170,7 @@ class NewMessage(EventBuilder):
|
||||||
|
|
||||||
return super().filter(event)
|
return super().filter(event)
|
||||||
|
|
||||||
class Event(EventCommon, types.TypeMessage):
|
class Event(EventCommon):
|
||||||
"""
|
"""
|
||||||
Represents the event of a new message. This event can be treated
|
Represents the event of a new message. This event can be treated
|
||||||
to all effects as a `Message <telethon.tl.custom.message.Message>`,
|
to all effects as a `Message <telethon.tl.custom.message.Message>`,
|
||||||
|
|
6
telethon/events/newmessage.pyi
Normal file
6
telethon/events/newmessage.pyi
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from telethon.events.common import EventCommon
|
||||||
|
from telethon.tl.custom import Message
|
||||||
|
|
||||||
|
|
||||||
|
class NewMessage:
|
||||||
|
class Event(EventCommon, Message): ...
|
|
@ -1,8 +1,12 @@
|
||||||
import abc
|
import abc
|
||||||
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from ... import errors, utils
|
from ... import errors, utils
|
||||||
from ...tl import types
|
from ...tl import types
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ... import hints
|
||||||
|
|
||||||
|
|
||||||
class ChatGetter(abc.ABC):
|
class ChatGetter(abc.ABC):
|
||||||
"""
|
"""
|
||||||
|
@ -10,7 +14,12 @@ class ChatGetter(abc.ABC):
|
||||||
and `chat_id` properties and `get_chat` and `get_input_chat`
|
and `chat_id` properties and `get_chat` and `get_input_chat`
|
||||||
methods.
|
methods.
|
||||||
"""
|
"""
|
||||||
def __init__(self, chat_peer=None, *, input_chat=None, chat=None, broadcast=None):
|
def __init__(self,
|
||||||
|
chat_peer: Optional[types.TypePeer] = None,
|
||||||
|
*,
|
||||||
|
input_chat: Optional[types.Chat] = None,
|
||||||
|
chat: Optional[types.Chat] = None,
|
||||||
|
broadcast: Optional[bool] = None):
|
||||||
self._chat_peer = chat_peer
|
self._chat_peer = chat_peer
|
||||||
self._input_chat = input_chat
|
self._input_chat = input_chat
|
||||||
self._chat = chat
|
self._chat = chat
|
||||||
|
@ -18,7 +27,7 @@ class ChatGetter(abc.ABC):
|
||||||
self._client = None
|
self._client = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chat(self):
|
def chat(self) -> Optional['hints.Entity']:
|
||||||
"""
|
"""
|
||||||
Returns the :tl:`User`, :tl:`Chat` or :tl:`Channel` where this object
|
Returns the :tl:`User`, :tl:`Chat` or :tl:`Channel` where this object
|
||||||
belongs to. It may be `None` if Telegram didn't send the chat.
|
belongs to. It may be `None` if Telegram didn't send the chat.
|
||||||
|
@ -32,7 +41,7 @@ class ChatGetter(abc.ABC):
|
||||||
"""
|
"""
|
||||||
return self._chat
|
return self._chat
|
||||||
|
|
||||||
async def get_chat(self):
|
async def get_chat(self) -> Optional['hints.Entity']:
|
||||||
"""
|
"""
|
||||||
Returns `chat`, but will make an API call to find the
|
Returns `chat`, but will make an API call to find the
|
||||||
chat unless it's already cached.
|
chat unless it's already cached.
|
||||||
|
@ -53,7 +62,7 @@ class ChatGetter(abc.ABC):
|
||||||
return self._chat
|
return self._chat
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def input_chat(self):
|
def input_chat(self) -> Optional[types.TypeInputPeer]:
|
||||||
"""
|
"""
|
||||||
This :tl:`InputPeer` is the input version of the chat where the
|
This :tl:`InputPeer` is the input version of the chat where the
|
||||||
message was sent. Similarly to `input_sender
|
message was sent. Similarly to `input_sender
|
||||||
|
@ -72,7 +81,7 @@ class ChatGetter(abc.ABC):
|
||||||
|
|
||||||
return self._input_chat
|
return self._input_chat
|
||||||
|
|
||||||
async def get_input_chat(self):
|
async def get_input_chat(self) -> Optional[types.TypeInputPeer]:
|
||||||
"""
|
"""
|
||||||
Returns `input_chat`, but will make an API call to find the
|
Returns `input_chat`, but will make an API call to find the
|
||||||
input chat unless it's already cached.
|
input chat unless it's already cached.
|
||||||
|
@ -92,7 +101,7 @@ class ChatGetter(abc.ABC):
|
||||||
return self._input_chat
|
return self._input_chat
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chat_id(self):
|
def chat_id(self) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Returns the marked chat integer ID. Note that this value **will
|
Returns the marked chat integer ID. Note that this value **will
|
||||||
be different** from ``peer_id`` for incoming private messages, since
|
be different** from ``peer_id`` for incoming private messages, since
|
||||||
|
@ -107,7 +116,7 @@ class ChatGetter(abc.ABC):
|
||||||
return utils.get_peer_id(self._chat_peer) if self._chat_peer else None
|
return utils.get_peer_id(self._chat_peer) if self._chat_peer else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_private(self):
|
def is_private(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
`True` if the message was sent as a private message.
|
`True` if the message was sent as a private message.
|
||||||
|
|
||||||
|
@ -117,7 +126,7 @@ class ChatGetter(abc.ABC):
|
||||||
return isinstance(self._chat_peer, types.PeerUser) if self._chat_peer else None
|
return isinstance(self._chat_peer, types.PeerUser) if self._chat_peer else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_group(self):
|
def is_group(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
True if the message was sent on a group or megagroup.
|
True if the message was sent on a group or megagroup.
|
||||||
|
|
||||||
|
@ -137,7 +146,7 @@ class ChatGetter(abc.ABC):
|
||||||
return isinstance(self._chat_peer, types.PeerChat)
|
return isinstance(self._chat_peer, types.PeerChat)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_channel(self):
|
def is_channel(self) -> bool:
|
||||||
"""`True` if the message was sent on a megagroup or channel."""
|
"""`True` if the message was sent on a megagroup or channel."""
|
||||||
# The only case where chat peer could be none is in MessageDeleted,
|
# The only case where chat peer could be none is in MessageDeleted,
|
||||||
# however those always have the peer in channels.
|
# however those always have the peer in channels.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user