mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-03 11:40:11 +03:00
Merge branch 'master' into asyncio
This commit is contained in:
commit
cb75092ba1
|
@ -11,10 +11,10 @@ Accessing the Full API
|
||||||
reason not to, like a method not existing or you wanting more control.
|
reason not to, like a method not existing or you wanting more control.
|
||||||
|
|
||||||
|
|
||||||
The ``TelegramClient`` doesn't offer a method for every single request
|
The `telethon.telegram_client.TelegramClient` doesn't offer a method for every
|
||||||
the Telegram API supports. However, it's very simple to *call* or *invoke*
|
single request the Telegram API supports. However, it's very simple to *call*
|
||||||
any request. Whenever you need something, don't forget to `check the
|
or *invoke* any request. Whenever you need something, don't forget to `check
|
||||||
documentation`__ and look for the `method you need`__. There you can go
|
the documentation`__ and look for the `method you need`__. There you can go
|
||||||
through a sorted list of everything you can do.
|
through a sorted list of everything you can do.
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@ You should also refer to the documentation to see what the objects
|
||||||
(constructors) Telegram returns look like. Every constructor inherits
|
(constructors) Telegram returns look like. Every constructor inherits
|
||||||
from a common type, and that's the reason for this distinction.
|
from a common type, and that's the reason for this distinction.
|
||||||
|
|
||||||
Say ``client.send_message()`` didn't exist, we could use the `search`__
|
Say `telethon.telegram_client.TelegramClient.send_message` didn't exist,
|
||||||
to look for "message". There we would find :tl:`SendMessageRequest`,
|
we could use the `search`__ to look for "message". There we would find
|
||||||
which we can work with.
|
:tl:`SendMessageRequest`, which we can work with.
|
||||||
|
|
||||||
Every request is a Python class, and has the parameters needed for you
|
Every request is a Python class, and has the parameters needed for you
|
||||||
to invoke it. You can also call ``help(request)`` for information on
|
to invoke it. You can also call ``help(request)`` for information on
|
||||||
|
@ -63,7 +63,7 @@ construct one, for instance:
|
||||||
|
|
||||||
peer = InputPeerUser(user_id, user_hash)
|
peer = InputPeerUser(user_id, user_hash)
|
||||||
|
|
||||||
Or we call ``.get_input_entity()``:
|
Or we call `telethon.telegram_client.TelegramClient.get_input_entity()`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ When you're going to invoke an API method, most require you to pass an
|
||||||
``.get_input_entity()`` is more straightforward (and often
|
``.get_input_entity()`` is more straightforward (and often
|
||||||
immediate, if you've seen the user before, know their ID, etc.).
|
immediate, if you've seen the user before, know their ID, etc.).
|
||||||
If you also need to have information about the whole user, use
|
If you also need to have information about the whole user, use
|
||||||
``.get_entity()`` instead:
|
`telethon.telegram_client.TelegramClient.get_entity()` instead:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ If you also need to have information about the whole user, use
|
||||||
In the later case, when you use the entity, the library will cast it to
|
In the later case, when you use the entity, the library will cast it to
|
||||||
its "input" version for you. If you already have the complete user and
|
its "input" version for you. If you already have the complete user and
|
||||||
want to cache its input version so the library doesn't have to do this
|
want to cache its input version so the library doesn't have to do this
|
||||||
every time its used, simply call ``.get_input_peer``:
|
every time its used, simply call `telethon.utils.get_input_peer`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ Session Files
|
||||||
The first parameter you pass to the constructor of the ``TelegramClient`` is
|
The first parameter you pass to the constructor of the ``TelegramClient`` is
|
||||||
the ``session``, and defaults to be the session name (or full path). That is,
|
the ``session``, and defaults to be the session name (or full path). That is,
|
||||||
if you create a ``TelegramClient('anon')`` instance and connect, an
|
if you create a ``TelegramClient('anon')`` instance and connect, an
|
||||||
``anon.session`` file will be created on the working directory.
|
``anon.session`` file will be created in the working directory.
|
||||||
|
|
||||||
Note that if you pass a string it will be a file in the current working
|
Note that if you pass a string it will be a file in the current working
|
||||||
directory, although you can also pass absolute paths.
|
directory, although you can also pass absolute paths.
|
||||||
|
|
|
@ -26,7 +26,7 @@ That's it! This is the old way to listen for raw updates, with no further
|
||||||
processing. If this feels annoying for you, remember that you can always
|
processing. If this feels annoying for you, remember that you can always
|
||||||
use :ref:`working-with-updates` but maybe use this for some other cases.
|
use :ref:`working-with-updates` but maybe use this for some other cases.
|
||||||
|
|
||||||
Now let's do something more interesting. Every time an user talks to use,
|
Now let's do something more interesting. Every time an user talks to us,
|
||||||
let's reply to them with the same text reversed:
|
let's reply to them with the same text reversed:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
|
@ -58,8 +58,7 @@ Manual Installation
|
||||||
|
|
||||||
5. Done!
|
5. Done!
|
||||||
|
|
||||||
To generate the `method documentation`__, ``cd docs`` and then
|
To generate the `method documentation`__, ``python3 setup.py gen docs``.
|
||||||
``python3 generate.py`` (if some pages render bad do it twice).
|
|
||||||
|
|
||||||
|
|
||||||
Optional dependencies
|
Optional dependencies
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
Deleted, Limited or Deactivated Accounts
|
Deleted, Limited or Deactivated Accounts
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
If you're from Iran or Russian, we have bad news for you.
|
If you're from Iran or Russia, we have bad news for you. Telegram is much more
|
||||||
Telegram is much more likely to ban these numbers,
|
likely to ban these numbers, as they are often used to spam other accounts,
|
||||||
as they are often used to spam other accounts,
|
likely through the use of libraries like this one. The best advice we can
|
||||||
likely through the use of libraries like this one.
|
give you is to not abuse the API, like calling many requests really quickly,
|
||||||
The best advice we can give you is to not abuse the API,
|
|
||||||
like calling many requests really quickly,
|
|
||||||
and to sign up with these phones through an official application.
|
and to sign up with these phones through an official application.
|
||||||
|
|
||||||
|
We have also had reports from Kazakhstan and China, where connecting
|
||||||
|
would fail. To solve these connection problems, you should use a proxy.
|
||||||
|
|
||||||
Telegram may also ban virtual (VoIP) phone numbers,
|
Telegram may also ban virtual (VoIP) phone numbers,
|
||||||
as again, they're likely to be used for spam.
|
as again, they're likely to be used for spam.
|
||||||
|
|
||||||
|
|
|
@ -29,5 +29,12 @@ class MessageDeleted(EventBuilder):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
chat_peer=peer, msg_id=(deleted_ids or [0])[0]
|
chat_peer=peer, msg_id=(deleted_ids or [0])[0]
|
||||||
)
|
)
|
||||||
|
if peer is None:
|
||||||
|
# If it's not a channel ID, then it was private/small group.
|
||||||
|
# We can't know which one was exactly unless we logged all
|
||||||
|
# messages, but we can indicate that it was maybe either of
|
||||||
|
# both by setting them both to True.
|
||||||
|
self.is_private = self.is_group = True
|
||||||
|
|
||||||
self.deleted_id = None if not deleted_ids else deleted_ids[0]
|
self.deleted_id = None if not deleted_ids else deleted_ids[0]
|
||||||
self.deleted_ids = deleted_ids
|
self.deleted_ids = deleted_ids
|
||||||
|
|
|
@ -28,8 +28,16 @@ class NewMessage(EventBuilder):
|
||||||
"""
|
"""
|
||||||
def __init__(self, incoming=None, outgoing=None,
|
def __init__(self, incoming=None, outgoing=None,
|
||||||
chats=None, blacklist_chats=False, pattern=None):
|
chats=None, blacklist_chats=False, pattern=None):
|
||||||
|
if incoming is not None and outgoing is None:
|
||||||
|
outgoing = not incoming
|
||||||
|
elif outgoing is not None and incoming is None:
|
||||||
|
incoming = not incoming
|
||||||
|
|
||||||
if incoming and outgoing:
|
if incoming and outgoing:
|
||||||
raise ValueError('Can only set either incoming or outgoing')
|
self.incoming = self.outgoing = None # Same as no filter
|
||||||
|
elif all(x is not None and not x for x in (incoming, outgoing)):
|
||||||
|
raise ValueError("Don't create an event handler if you "
|
||||||
|
"don't want neither incoming or outgoing!")
|
||||||
|
|
||||||
super().__init__(chats=chats, blacklist_chats=blacklist_chats)
|
super().__init__(chats=chats, blacklist_chats=blacklist_chats)
|
||||||
self.incoming = incoming
|
self.incoming = incoming
|
||||||
|
|
|
@ -11,8 +11,7 @@ from ..tl.types import (
|
||||||
ServerDHInnerData, ClientDHInnerData, DhGenOk, DhGenRetry, DhGenFail
|
ServerDHInnerData, ClientDHInnerData, DhGenOk, DhGenRetry, DhGenFail
|
||||||
)
|
)
|
||||||
from .. import helpers as utils
|
from .. import helpers as utils
|
||||||
from ..crypto import AES, AuthKey, Factorization
|
from ..crypto import AES, AuthKey, Factorization, rsa
|
||||||
from ..crypto import rsa
|
|
||||||
from ..errors import SecurityError
|
from ..errors import SecurityError
|
||||||
from ..extensions import BinaryReader
|
from ..extensions import BinaryReader
|
||||||
from ..network import MtProtoPlainSender
|
from ..network import MtProtoPlainSender
|
||||||
|
|
|
@ -963,7 +963,7 @@ class TelegramClient(TelegramBareClient):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
``MessageAuthorRequiredError`` if you're not the author of the
|
``MessageAuthorRequiredError`` if you're not the author of the
|
||||||
message but try editing it anyway.
|
message but tried editing it anyway.
|
||||||
|
|
||||||
``MessageNotModifiedError`` if the contents of the message were
|
``MessageNotModifiedError`` if the contents of the message were
|
||||||
not modified at all.
|
not modified at all.
|
||||||
|
@ -1031,7 +1031,8 @@ class TelegramClient(TelegramBareClient):
|
||||||
async def iter_messages(self, entity, limit=20, offset_date=None,
|
async def iter_messages(self, entity, limit=20, offset_date=None,
|
||||||
offset_id=0, max_id=0, min_id=0, add_offset=0,
|
offset_id=0, max_id=0, min_id=0, add_offset=0,
|
||||||
search=None, filter=None, from_user=None,
|
search=None, filter=None, from_user=None,
|
||||||
batch_size=100, wait_time=None, _total=None):
|
batch_size=100, wait_time=None, ids=None,
|
||||||
|
_total=None):
|
||||||
"""
|
"""
|
||||||
Iterator over the message history for the specified entity.
|
Iterator over the message history for the specified entity.
|
||||||
|
|
||||||
|
@ -1059,7 +1060,7 @@ class TelegramClient(TelegramBareClient):
|
||||||
|
|
||||||
max_id (`int`):
|
max_id (`int`):
|
||||||
All the messages with a higher (newer) ID or equal to this will
|
All the messages with a higher (newer) ID or equal to this will
|
||||||
be excluded
|
be excluded.
|
||||||
|
|
||||||
min_id (`int`):
|
min_id (`int`):
|
||||||
All the messages with a lower (older) ID or equal to this will
|
All the messages with a lower (older) ID or equal to this will
|
||||||
|
@ -1091,6 +1092,15 @@ class TelegramClient(TelegramBareClient):
|
||||||
If left to ``None``, it will default to 1 second only if
|
If left to ``None``, it will default to 1 second only if
|
||||||
the limit is higher than 3000.
|
the limit is higher than 3000.
|
||||||
|
|
||||||
|
ids (`int`, `list`):
|
||||||
|
A single integer ID (or several IDs) for the message that
|
||||||
|
should be returned. This parameter takes precedence over
|
||||||
|
the rest (which will be ignored if this is set). This can
|
||||||
|
for instance be used to get the message with ID 123 from
|
||||||
|
a channel. Note that if the message doesn't exist, ``None``
|
||||||
|
will appear in its place, so that zipping the list of IDs
|
||||||
|
with the messages can match one-to-one.
|
||||||
|
|
||||||
_total (`list`, optional):
|
_total (`list`, optional):
|
||||||
A single-item list to pass the total parameter by reference.
|
A single-item list to pass the total parameter by reference.
|
||||||
|
|
||||||
|
@ -1110,6 +1120,23 @@ class TelegramClient(TelegramBareClient):
|
||||||
you think may be good.
|
you think may be good.
|
||||||
"""
|
"""
|
||||||
entity = await self.get_input_entity(entity)
|
entity = await self.get_input_entity(entity)
|
||||||
|
if ids:
|
||||||
|
if not utils.is_list_like(ids):
|
||||||
|
ids = (ids,)
|
||||||
|
async for x in self._iter_ids(entity, ids, total=_total):
|
||||||
|
await yield_(x)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Telegram doesn't like min_id/max_id. If these IDs are low enough
|
||||||
|
# (starting from last_id - 100), the request will return nothing.
|
||||||
|
#
|
||||||
|
# We can emulate their behaviour locally by setting offset = max_id
|
||||||
|
# and simply stopping once we hit a message with ID <= min_id.
|
||||||
|
offset_id = max(offset_id, max_id)
|
||||||
|
if offset_id and min_id:
|
||||||
|
if offset_id - min_id <= 1:
|
||||||
|
return
|
||||||
|
|
||||||
limit = float('inf') if limit is None else int(limit)
|
limit = float('inf') if limit is None else int(limit)
|
||||||
if search is not None or filter or from_user:
|
if search is not None or filter or from_user:
|
||||||
if filter is None:
|
if filter is None:
|
||||||
|
@ -1123,8 +1150,8 @@ class TelegramClient(TelegramBareClient):
|
||||||
offset_id=offset_id,
|
offset_id=offset_id,
|
||||||
add_offset=add_offset,
|
add_offset=add_offset,
|
||||||
limit=1,
|
limit=1,
|
||||||
max_id=max_id,
|
max_id=0,
|
||||||
min_id=min_id,
|
min_id=0,
|
||||||
hash=0,
|
hash=0,
|
||||||
from_id=self.get_input_entity(from_user) if from_user else None
|
from_id=self.get_input_entity(from_user) if from_user else None
|
||||||
)
|
)
|
||||||
|
@ -1134,8 +1161,8 @@ class TelegramClient(TelegramBareClient):
|
||||||
limit=1,
|
limit=1,
|
||||||
offset_date=offset_date,
|
offset_date=offset_date,
|
||||||
offset_id=offset_id,
|
offset_id=offset_id,
|
||||||
min_id=min_id,
|
min_id=0,
|
||||||
max_id=max_id,
|
max_id=0,
|
||||||
add_offset=add_offset,
|
add_offset=add_offset,
|
||||||
hash=0
|
hash=0
|
||||||
)
|
)
|
||||||
|
@ -1166,6 +1193,9 @@ class TelegramClient(TelegramBareClient):
|
||||||
for x in itertools.chain(r.users, r.chats)}
|
for x in itertools.chain(r.users, r.chats)}
|
||||||
|
|
||||||
for message in r.messages:
|
for message in r.messages:
|
||||||
|
if message.id <= min_id:
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(message, MessageEmpty) or message.id >= last_id:
|
if isinstance(message, MessageEmpty) or message.id >= last_id:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -1175,9 +1205,30 @@ class TelegramClient(TelegramBareClient):
|
||||||
# IDs are returned in descending order.
|
# IDs are returned in descending order.
|
||||||
last_id = message.id
|
last_id = message.id
|
||||||
|
|
||||||
# Add a few extra attributes to the Message to be friendlier.
|
self._make_message_friendly(message, entities)
|
||||||
# To make messages more friendly, always add message
|
await yield_(message)
|
||||||
# to service messages, and action to normal messages.
|
have += 1
|
||||||
|
|
||||||
|
if len(r.messages) < request.limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
request.offset_id = r.messages[-1].id
|
||||||
|
if isinstance(request, GetHistoryRequest):
|
||||||
|
request.offset_date = r.messages[-1].date
|
||||||
|
else:
|
||||||
|
request.max_date = r.messages[-1].date
|
||||||
|
|
||||||
|
await asyncio.sleep(max(wait_time - (time.time() - start), 0))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_message_friendly(message, entities):
|
||||||
|
"""
|
||||||
|
Add a few extra attributes to the :tl:`Message` to be friendlier.
|
||||||
|
|
||||||
|
To make messages more friendly, always add message
|
||||||
|
to service messages, and action to normal messages.
|
||||||
|
"""
|
||||||
|
# TODO Create an actual friendlier class
|
||||||
message.message = getattr(message, 'message', None)
|
message.message = getattr(message, 'message', None)
|
||||||
message.action = getattr(message, 'action', None)
|
message.action = getattr(message, 'action', None)
|
||||||
message.to = entities[utils.get_peer_id(message.to_id)]
|
message.to = entities[utils.get_peer_id(message.to_id)]
|
||||||
|
@ -1196,32 +1247,65 @@ class TelegramClient(TelegramBareClient):
|
||||||
PeerChannel(message.fwd_from.channel_id)
|
PeerChannel(message.fwd_from.channel_id)
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
await yield_(message)
|
|
||||||
have += 1
|
|
||||||
|
|
||||||
if len(r.messages) < request.limit:
|
@async_generator
|
||||||
break
|
async def _iter_ids(self, entity, ids, total):
|
||||||
|
"""
|
||||||
|
Special case for `iter_messages` when it should only fetch some IDs.
|
||||||
|
"""
|
||||||
|
if total:
|
||||||
|
total[0] = len(ids)
|
||||||
|
|
||||||
request.offset_id = r.messages[-1].id
|
if isinstance(entity, InputPeerChannel):
|
||||||
if isinstance(request, GetHistoryRequest):
|
r = await self(channels.GetMessagesRequest(entity, ids))
|
||||||
request.offset_date = r.messages[-1].date
|
|
||||||
else:
|
else:
|
||||||
request.max_date = r.messages[-1].date
|
r = await self(messages.GetMessagesRequest(ids))
|
||||||
|
|
||||||
await asyncio.sleep(max(wait_time - (time.time() - start), 0))
|
entities = {utils.get_peer_id(x): x
|
||||||
|
for x in itertools.chain(r.users, r.chats)}
|
||||||
|
|
||||||
|
# Telegram seems to return the messages in the order in which
|
||||||
|
# we asked them for, so we don't need to check it ourselves.
|
||||||
|
for message in r.messages:
|
||||||
|
if isinstance(message, MessageEmpty):
|
||||||
|
await yield_(None)
|
||||||
|
else:
|
||||||
|
self._make_message_friendly(message, entities)
|
||||||
|
await yield_(message)
|
||||||
|
|
||||||
async def get_messages(self, *args, **kwargs):
|
async def get_messages(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Same as :meth:`iter_messages`, but returns a list instead
|
Same as :meth:`iter_messages`, but returns a list instead
|
||||||
with an additional ``.total`` attribute on the list.
|
with an additional ``.total`` attribute on the list.
|
||||||
|
|
||||||
|
If the `limit` is not set, it will be 1 by default unless both
|
||||||
|
`min_id` **and** `max_id` are set (as *named* arguments), in
|
||||||
|
which case the entire range will be returned.
|
||||||
|
|
||||||
|
This is so because any integer limit would be rather arbitrary and
|
||||||
|
it's common to only want to fetch one message, but if a range is
|
||||||
|
specified it makes sense that it should return the entirety of it.
|
||||||
|
|
||||||
|
If `ids` is present in the *named* arguments and is not a list,
|
||||||
|
a single :tl:`Message` will be returned for convenience instead
|
||||||
|
of a list.
|
||||||
"""
|
"""
|
||||||
total = [0]
|
total = [0]
|
||||||
kwargs['_total'] = total
|
kwargs['_total'] = total
|
||||||
|
if len(args) == 1 and 'limit' not in kwargs:
|
||||||
|
if 'min_id' in kwargs and 'max_id' in kwargs:
|
||||||
|
kwargs['limit'] = None
|
||||||
|
else:
|
||||||
|
kwargs['limit'] = 1
|
||||||
|
|
||||||
msgs = UserList()
|
msgs = UserList()
|
||||||
async for msg in self.iter_messages(*args, **kwargs):
|
async for msg in self.iter_messages(*args, **kwargs):
|
||||||
msgs.append(msg)
|
msgs.append(msg)
|
||||||
|
|
||||||
msgs.total = total[0]
|
msgs.total = total[0]
|
||||||
|
if 'ids' in kwargs and not utils.is_list_like(kwargs['ids']):
|
||||||
|
return msgs[0]
|
||||||
|
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
async def get_message_history(self, *args, **kwargs):
|
async def get_message_history(self, *args, **kwargs):
|
||||||
|
@ -1666,7 +1750,7 @@ class TelegramClient(TelegramBareClient):
|
||||||
if m.has('duration') else 0)
|
if m.has('duration') else 0)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
doc = DocumentAttributeVideo(0, 0, 0,
|
doc = DocumentAttributeVideo(0, 1, 1,
|
||||||
round_message=video_note)
|
round_message=video_note)
|
||||||
|
|
||||||
attr_dict[DocumentAttributeVideo] = doc
|
attr_dict[DocumentAttributeVideo] = doc
|
||||||
|
@ -2448,7 +2532,7 @@ class TelegramClient(TelegramBareClient):
|
||||||
|
|
||||||
async def catch_up(self):
|
async def catch_up(self):
|
||||||
state = self.session.get_update_state(0)
|
state = self.session.get_update_state(0)
|
||||||
if not state:
|
if not state or not state.pts:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.session.catching_up = True
|
self.session.catching_up = True
|
||||||
|
|
|
@ -552,8 +552,13 @@ def resolve_id(marked_id):
|
||||||
if marked_id >= 0:
|
if marked_id >= 0:
|
||||||
return marked_id, PeerUser
|
return marked_id, PeerUser
|
||||||
|
|
||||||
if str(marked_id).startswith('-100'):
|
# There have been report of chat IDs being 10000xyz, which means their
|
||||||
return int(str(marked_id)[4:]), PeerChannel
|
# marked version is -10000xyz, which in turn looks like a channel but
|
||||||
|
# it becomes 00xyz (= xyz). Hence, we must assert that there are only
|
||||||
|
# two zeroes.
|
||||||
|
m = re.match(r'-100([^0]\d*)', str(marked_id))
|
||||||
|
if m:
|
||||||
|
return int(m.group(1)), PeerChannel
|
||||||
|
|
||||||
return -marked_id, PeerChat
|
return -marked_id, PeerChat
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ from getpass import getpass
|
||||||
|
|
||||||
from telethon.utils import get_display_name
|
from telethon.utils import get_display_name
|
||||||
|
|
||||||
from telethon import ConnectionMode, TelegramClient
|
from telethon import TelegramClient, events
|
||||||
|
from telethon.network import ConnectionTcpAbridged
|
||||||
from telethon.errors import SessionPasswordNeededError
|
from telethon.errors import SessionPasswordNeededError
|
||||||
from telethon.tl.types import (
|
from telethon.tl.types import (
|
||||||
PeerChat, UpdateShortChatMessage, UpdateShortMessage
|
PeerChat, UpdateShortChatMessage, UpdateShortMessage
|
||||||
|
@ -70,11 +71,11 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
# These parameters should be passed always, session name and API
|
# These parameters should be passed always, session name and API
|
||||||
session_user_id, api_id, api_hash,
|
session_user_id, api_id, api_hash,
|
||||||
|
|
||||||
# You can optionally change the connection mode by using this enum.
|
# You can optionally change the connection mode by passing a
|
||||||
# This changes how much data will be sent over the network with
|
# type or an instance of it. This changes how the sent packets
|
||||||
# every request, and how it will be formatted. Default is
|
# look (low-level concept you normally shouldn't worry about).
|
||||||
# ConnectionMode.TCP_FULL, and smallest is TCP_TCP_ABRIDGED.
|
# Default is ConnectionTcpFull, smallest is ConnectionTcpAbridged.
|
||||||
connection_mode=ConnectionMode.TCP_ABRIDGED,
|
connection=ConnectionTcpAbridged,
|
||||||
|
|
||||||
# If you're using a proxy, set it here.
|
# If you're using a proxy, set it here.
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
|
@ -126,10 +127,11 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Main loop of the TelegramClient, will wait for user action"""
|
"""Main loop of the TelegramClient, will wait for user action"""
|
||||||
|
|
||||||
# Once everything is ready, we can add an update handler. Every
|
# Once everything is ready, we can add an event handler.
|
||||||
# update object will be passed to the self.update_handler method,
|
#
|
||||||
# where we can process it as we need.
|
# Events are an abstraction over Telegram's "Updates" and
|
||||||
self.add_update_handler(self.update_handler)
|
# are much easier to use.
|
||||||
|
self.add_event_handler(self.message_handler, events.NewMessage)
|
||||||
|
|
||||||
# Enter a while loop to chat as long as the user wants
|
# Enter a while loop to chat as long as the user wants
|
||||||
while True:
|
while True:
|
||||||
|
@ -334,31 +336,29 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
bytes_to_string(total_bytes), downloaded_bytes / total_bytes)
|
bytes_to_string(total_bytes), downloaded_bytes / total_bytes)
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_handler(self, update):
|
def message_handler(self, event):
|
||||||
"""Callback method for received Updates"""
|
"""Callback method for received events.NewMessage"""
|
||||||
|
|
||||||
# We have full control over what we want to do with the updates.
|
# Note that accessing ``.sender`` and ``.chat`` may be slow since
|
||||||
# In our case we only want to react to chat messages, so we use
|
# these are not cached and must be queried always! However it lets
|
||||||
# isinstance() to behave accordingly on these cases.
|
# us access the chat title and user name.
|
||||||
if isinstance(update, UpdateShortMessage):
|
if event.is_group:
|
||||||
who = self.get_entity(update.user_id)
|
if event.out:
|
||||||
if update.out:
|
sprint('>> sent "{}" to chat {}'.format(
|
||||||
|
event.text, get_display_name(event.chat)
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
sprint('<< {} @ {} sent "{}"'.format(
|
||||||
|
get_display_name(event.sender),
|
||||||
|
get_display_name(event.chat),
|
||||||
|
event.text
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
if event.out:
|
||||||
sprint('>> "{}" to user {}'.format(
|
sprint('>> "{}" to user {}'.format(
|
||||||
update.message, get_display_name(who)
|
event.text, get_display_name(event.chat)
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
sprint('<< {} sent "{}"'.format(
|
sprint('<< {} sent "{}"'.format(
|
||||||
get_display_name(who), update.message
|
get_display_name(event.chat), event.text
|
||||||
))
|
|
||||||
|
|
||||||
elif isinstance(update, UpdateShortChatMessage):
|
|
||||||
which = self.get_entity(PeerChat(update.chat_id))
|
|
||||||
if update.out:
|
|
||||||
sprint('>> sent "{}" to chat {}'.format(
|
|
||||||
update.message, get_display_name(which)
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
who = self.get_entity(update.from_id)
|
|
||||||
sprint('<< {} @ {} sent "{}"'.format(
|
|
||||||
get_display_name(which), get_display_name(who), update.message
|
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# A simple script to print all updates received
|
# A simple script to print all updates received
|
||||||
|
#
|
||||||
|
# NOTE: To run this script you MUST have 'TG_API_ID' and 'TG_API_HASH' in
|
||||||
|
# your environment variables. This is a good way to use these private
|
||||||
|
# values. See https://superuser.com/q/284342.
|
||||||
|
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
|
@ -23,7 +27,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
client.start()
|
client.start()
|
||||||
|
|
||||||
client.add_update_handler(update_handler)
|
client.add_event_handler(update_handler)
|
||||||
print('(Press Ctrl+C to stop this)')
|
print('(Press Ctrl+C to stop this)')
|
||||||
client.idle()
|
client.idle()
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
"""
|
"""
|
||||||
A example script to automatically send messages based on certain triggers.
|
A example script to automatically send messages based on certain triggers.
|
||||||
|
|
||||||
The script makes uses of environment variables to determine the API ID,
|
NOTE: To run this script you MUST have 'TG_API_ID' and 'TG_API_HASH' in
|
||||||
hash, phone and such to be used. You may want to add these to your .bashrc
|
your environment variables. This is a good way to use these private
|
||||||
file, including TG_API_ID, TG_API_HASH, TG_PHONE and optionally TG_SESSION.
|
values. See https://superuser.com/q/284342.
|
||||||
|
|
||||||
This script assumes that you have certain files on the working directory,
|
This script assumes that you have certain files on the working directory,
|
||||||
such as "xfiles.m4a" or "anytime.png" for some of the automated replies.
|
such as "xfiles.m4a" or "anytime.png" for some of the automated replies.
|
||||||
|
|
|
@ -4,7 +4,36 @@
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<title>Telethon API</title>
|
<title>Telethon API</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="css/docs.css" rel="stylesheet">
|
<link id="style" href="css/docs.light.css" rel="stylesheet">
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var style = document.getElementById('style');
|
||||||
|
|
||||||
|
// setTheme(<link />, 'light' / 'dark')
|
||||||
|
function setTheme(theme) {
|
||||||
|
document.cookie = 'css=' + theme + '; path=/';
|
||||||
|
return style.href = 'css/docs.' + theme + '.css';
|
||||||
|
}
|
||||||
|
|
||||||
|
// setThemeOnClick(<link />, 'light' / 'dark', <a />)
|
||||||
|
function setThemeOnClick(theme, button) {
|
||||||
|
return button.addEventListener('click', function (e) {
|
||||||
|
setTheme(theme);
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setTheme(document.cookie
|
||||||
|
.split(';')[0]
|
||||||
|
.split('=')[1] || 'light');
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
setThemeOnClick('light', document.getElementById('themeLight'));
|
||||||
|
setThemeOnClick('dark', document.getElementById('themeDark'));
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Nunito|Source+Code+Pro" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Nunito|Source+Code+Pro" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
@ -21,7 +50,10 @@
|
||||||
on what the methods, constructors and types mean. Nevertheless, this
|
on what the methods, constructors and types mean. Nevertheless, this
|
||||||
page aims to provide easy access to all the available methods, their
|
page aims to provide easy access to all the available methods, their
|
||||||
definition and parameters.</p>
|
definition and parameters.</p>
|
||||||
|
<p id="themeSelect">
|
||||||
|
<a href="#" id="themeLight">light</a> /
|
||||||
|
<a href="#" id="themeDark">dark</a> theme.
|
||||||
|
</p>
|
||||||
<p>Please note that when you see this:</p>
|
<p>Please note that when you see this:</p>
|
||||||
<pre>---functions---
|
<pre>---functions---
|
||||||
users.getUsers#0d91a548 id:Vector<InputUser> = Vector<User></pre>
|
users.getUsers#0d91a548 id:Vector<InputUser> = Vector<User></pre>
|
||||||
|
|
185
telethon_generator/data/html/css/docs.dark.css
Normal file
185
telethon_generator/data/html/css/docs.dark.css
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
body {
|
||||||
|
font-family: 'Nunito', sans-serif;
|
||||||
|
color: #bbb;
|
||||||
|
background-color:#000;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #42aaed;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: 'Source Code Pro', monospace;
|
||||||
|
padding: 8px;
|
||||||
|
color: #567;
|
||||||
|
background: #080a0c;
|
||||||
|
border-radius: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #64bbdd;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
border-top: 1px solid #111;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
list-style: none;
|
||||||
|
background: #080a0c;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal img {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 8px -2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, summary.title {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_div {
|
||||||
|
padding: 20px 0;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar {
|
||||||
|
visibility: visible;
|
||||||
|
display: block;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar-track:horizontal {
|
||||||
|
background: #222;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar-thumb:horizontal {
|
||||||
|
background: #444;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:target {
|
||||||
|
border: 2px solid #149;
|
||||||
|
background: #246;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'sh' stands for Syntax Highlight */
|
||||||
|
span.sh1 {
|
||||||
|
color: #f93;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.tooltip {
|
||||||
|
border-bottom: 1px dashed #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchBox {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
height: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 2px solid #222;
|
||||||
|
background: #000;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchBox:placeholder-shown {
|
||||||
|
color: #bbb;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #bbb;
|
||||||
|
background-color: #111;
|
||||||
|
border: 2px solid #146;
|
||||||
|
transition-duration: 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #146;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://www.w3schools.com/css/css_navbar.asp */
|
||||||
|
ul.together {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.together li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.together li a {
|
||||||
|
display: block;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #111;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://stackoverflow.com/a/30810322 */
|
||||||
|
.invisible {
|
||||||
|
left: 0;
|
||||||
|
top: -99px;
|
||||||
|
padding: 0;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: none;
|
||||||
|
color: transparent;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
h1, summary.title {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev_page_content_wrap {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev_page_title {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
229
telethon_generator/data/html/css/docs.h4x0r.css
Normal file
229
telethon_generator/data/html/css/docs.h4x0r.css
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/* Begin of https://cdn.jsdelivr.net/npm/hack-font@3/build/web/hack.css
|
||||||
|
*
|
||||||
|
* Hack typeface https://github.com/source-foundry/Hack
|
||||||
|
* License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-regular.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-regular.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-bold.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-bold.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-italic.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-italic.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-bolditalic.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-bolditalic.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End of https://cdn.jsdelivr.net/npm/hack-font@3/build/web/hack.css */
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Hack', monospace;
|
||||||
|
color: #0a0;
|
||||||
|
background-color: #000;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
color: #000;
|
||||||
|
background: #0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: #000;
|
||||||
|
background: #0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 8px;
|
||||||
|
color: #0c0;
|
||||||
|
background: #010;
|
||||||
|
border-radius: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #0f0;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
border-top: 1px solid #111;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
list-style: none;
|
||||||
|
background: #010;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal img {
|
||||||
|
opacity: 0;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 8px -2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, summary.title {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_div {
|
||||||
|
padding: 20px 0;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar {
|
||||||
|
visibility: visible;
|
||||||
|
display: block;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar-track:horizontal {
|
||||||
|
background: #222;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre::-webkit-scrollbar-thumb:horizontal {
|
||||||
|
background: #444;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:target {
|
||||||
|
border: 2px solid #0f0;
|
||||||
|
background: #010;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'sh' stands for Syntax Highlight */
|
||||||
|
span.sh1 {
|
||||||
|
color: #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.tooltip {
|
||||||
|
border-bottom: 1px dashed #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchBox {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
height: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 2px solid #222;
|
||||||
|
background: #000;
|
||||||
|
color: #0e0;
|
||||||
|
font-family: 'Hack', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchBox:placeholder-shown {
|
||||||
|
color: #0b0;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #0f0;
|
||||||
|
background-color: #071007;
|
||||||
|
border: 2px solid #131;
|
||||||
|
transition-duration: 300ms;
|
||||||
|
font-family: 'Hack', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #131;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://www.w3schools.com/css/css_navbar.asp */
|
||||||
|
ul.together {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.together li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.together li a {
|
||||||
|
display: block;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #121;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://stackoverflow.com/a/30810322 */
|
||||||
|
.invisible {
|
||||||
|
left: 0;
|
||||||
|
top: -99px;
|
||||||
|
padding: 0;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: none;
|
||||||
|
color: transparent;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
h1, summary.title {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev_page_content_wrap {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev_page_title {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,19 +95,6 @@ span.sh1 {
|
||||||
color: #f70;
|
color: #f70;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.sh2 {
|
|
||||||
color: #0c7;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.sh3 {
|
|
||||||
color: #aaa;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.sh4 {
|
|
||||||
color: #06c;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.tooltip {
|
span.tooltip {
|
||||||
border-bottom: 1px dashed #444;
|
border-bottom: 1px dashed #444;
|
||||||
}
|
}
|
|
@ -31,29 +31,32 @@ class DocsWriter:
|
||||||
self._script = ''
|
self._script = ''
|
||||||
|
|
||||||
# High level writing
|
# High level writing
|
||||||
def write_head(self, title, relative_css_path):
|
def write_head(self, title, relative_css_path, default_css):
|
||||||
"""Writes the head part for the generated document,
|
"""Writes the head part for the generated document,
|
||||||
with the given title and CSS
|
with the given title and CSS
|
||||||
"""
|
"""
|
||||||
self.write('''<!DOCTYPE html>
|
self.write(
|
||||||
|
'''<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<title>''')
|
<title>{title}</title>
|
||||||
|
|
||||||
self.write(title)
|
|
||||||
|
|
||||||
self.write('''</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="''')
|
<link id="style" href="{rel_css}/docs.{def_css}.css" rel="stylesheet">
|
||||||
|
<script>
|
||||||
self.write(relative_css_path)
|
document.getElementById("style").href = "{rel_css}/docs."
|
||||||
|
+ (document.cookie.split(";")[0].split("=")[1] || "{def_css}")
|
||||||
self.write('''" rel="stylesheet">
|
+ ".css";
|
||||||
<link href="https://fonts.googleapis.com/css?family=Nunito|Source+Code+Pro" rel="stylesheet">
|
</script>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Nunito|Source+Code+Pro"
|
||||||
|
rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main_div">''')
|
<div id="main_div">''',
|
||||||
|
title=title,
|
||||||
|
rel_css=relative_css_path.rstrip('/'),
|
||||||
|
def_css=default_css
|
||||||
|
)
|
||||||
|
|
||||||
def set_menu_separator(self, relative_image_path):
|
def set_menu_separator(self, relative_image_path):
|
||||||
"""Sets the menu separator.
|
"""Sets the menu separator.
|
||||||
|
@ -77,9 +80,7 @@ class DocsWriter:
|
||||||
|
|
||||||
self.write('<li>')
|
self.write('<li>')
|
||||||
if link:
|
if link:
|
||||||
self.write('<a href="')
|
self.write('<a href="{}">', link)
|
||||||
self.write(link)
|
|
||||||
self.write('">')
|
|
||||||
|
|
||||||
# Write the real menu entry text
|
# Write the real menu entry text
|
||||||
self.write(name)
|
self.write(name)
|
||||||
|
@ -98,26 +99,21 @@ class DocsWriter:
|
||||||
"""Writes a title header in the document body,
|
"""Writes a title header in the document body,
|
||||||
with an optional depth level
|
with an optional depth level
|
||||||
"""
|
"""
|
||||||
self.write('<h%d>' % level)
|
self.write('<h{level}>{title}</h{level}>', title=title, level=level)
|
||||||
self.write(title)
|
|
||||||
self.write('</h%d>' % level)
|
|
||||||
|
|
||||||
def write_code(self, tlobject):
|
def write_code(self, tlobject):
|
||||||
"""Writes the code for the given 'tlobject' properly
|
"""Writes the code for the given 'tlobject' properly
|
||||||
formatted with hyperlinks
|
formatted with hyperlinks
|
||||||
"""
|
"""
|
||||||
self.write('<pre>---')
|
self.write('<pre>---{}---\n',
|
||||||
self.write('functions' if tlobject.is_function else 'types')
|
'functions' if tlobject.is_function else 'types')
|
||||||
self.write('---\n')
|
|
||||||
|
|
||||||
# Write the function or type and its ID
|
# Write the function or type and its ID
|
||||||
if tlobject.namespace:
|
if tlobject.namespace:
|
||||||
self.write(tlobject.namespace)
|
self.write(tlobject.namespace)
|
||||||
self.write('.')
|
self.write('.')
|
||||||
|
|
||||||
self.write(tlobject.name)
|
self.write('{}#{:08x}', tlobject.name, tlobject.id)
|
||||||
self.write('#')
|
|
||||||
self.write(hex(tlobject.id)[2:].rjust(8, '0'))
|
|
||||||
|
|
||||||
# Write all the arguments (or do nothing if there's none)
|
# Write all the arguments (or do nothing if there's none)
|
||||||
for arg in tlobject.args:
|
for arg in tlobject.args:
|
||||||
|
@ -134,20 +130,19 @@ class DocsWriter:
|
||||||
|
|
||||||
# "Opening" modifiers
|
# "Opening" modifiers
|
||||||
if arg.is_flag:
|
if arg.is_flag:
|
||||||
self.write('flags.%d?' % arg.flag_index)
|
self.write('flags.{}?', arg.flag_index)
|
||||||
|
|
||||||
if arg.is_generic:
|
if arg.is_generic:
|
||||||
self.write('!')
|
self.write('!')
|
||||||
|
|
||||||
if arg.is_vector:
|
if arg.is_vector:
|
||||||
self.write(
|
self.write('<a href="{}">Vector</a><',
|
||||||
'<a href="%s">Vector</a><' % self.type_to_path('vector')
|
self.type_to_path('vector'))
|
||||||
)
|
|
||||||
|
|
||||||
# Argument type
|
# Argument type
|
||||||
if arg.type:
|
if arg.type:
|
||||||
if add_link:
|
if add_link:
|
||||||
self.write('<a href="%s">' % self.type_to_path(arg.type))
|
self.write('<a href="{}">', self.type_to_path(arg.type))
|
||||||
self.write(arg.type)
|
self.write(arg.type)
|
||||||
if add_link:
|
if add_link:
|
||||||
self.write('</a>')
|
self.write('</a>')
|
||||||
|
@ -176,19 +171,14 @@ class DocsWriter:
|
||||||
# use a lower type name for it (see #81)
|
# use a lower type name for it (see #81)
|
||||||
vector, inner = tlobject.result.split('<')
|
vector, inner = tlobject.result.split('<')
|
||||||
inner = inner.strip('>')
|
inner = inner.strip('>')
|
||||||
self.write('<a href="')
|
self.write('<a href="{}">{}</a><',
|
||||||
self.write(self.type_to_path(vector))
|
self.type_to_path(vector), vector)
|
||||||
self.write('">%s</a><' % vector)
|
|
||||||
|
|
||||||
self.write('<a href="')
|
self.write('<a href="{}">{}</a>>',
|
||||||
self.write(self.type_to_path(inner))
|
self.type_to_path(inner), inner)
|
||||||
self.write('">%s</a>' % inner)
|
|
||||||
|
|
||||||
self.write('>')
|
|
||||||
else:
|
else:
|
||||||
self.write('<a href="')
|
self.write('<a href="{}">{}</a>',
|
||||||
self.write(self.type_to_path(tlobject.result))
|
self.type_to_path(tlobject.result), tlobject.result)
|
||||||
self.write('">%s</a>' % tlobject.result)
|
|
||||||
|
|
||||||
self.write('</pre>')
|
self.write('</pre>')
|
||||||
|
|
||||||
|
@ -209,17 +199,13 @@ class DocsWriter:
|
||||||
|
|
||||||
self.write('<td')
|
self.write('<td')
|
||||||
if align:
|
if align:
|
||||||
self.write(' style="text-align:')
|
self.write(' style="text-align:{}"', align)
|
||||||
self.write(align)
|
|
||||||
self.write('"')
|
|
||||||
self.write('>')
|
self.write('>')
|
||||||
|
|
||||||
if bold:
|
if bold:
|
||||||
self.write('<b>')
|
self.write('<b>')
|
||||||
if link:
|
if link:
|
||||||
self.write('<a href="')
|
self.write('<a href="{}">', link)
|
||||||
self.write(link)
|
|
||||||
self.write('">')
|
|
||||||
|
|
||||||
# Finally write the real table data, the given text
|
# Finally write the real table data, the given text
|
||||||
self.write(text)
|
self.write(text)
|
||||||
|
@ -244,9 +230,7 @@ class DocsWriter:
|
||||||
|
|
||||||
def write_text(self, text):
|
def write_text(self, text):
|
||||||
"""Writes a paragraph of text"""
|
"""Writes a paragraph of text"""
|
||||||
self.write('<p>')
|
self.write('<p>{}</p>', text)
|
||||||
self.write(text)
|
|
||||||
self.write('</p>')
|
|
||||||
|
|
||||||
def write_copy_button(self, text, text_to_copy):
|
def write_copy_button(self, text, text_to_copy):
|
||||||
"""Writes a button with 'text' which can be used
|
"""Writes a button with 'text' which can be used
|
||||||
|
@ -273,15 +257,17 @@ class DocsWriter:
|
||||||
'c.select();'
|
'c.select();'
|
||||||
'try{document.execCommand("copy")}'
|
'try{document.execCommand("copy")}'
|
||||||
'catch(e){}}'
|
'catch(e){}}'
|
||||||
'</script>')
|
'</script>'
|
||||||
|
)
|
||||||
|
|
||||||
self.write('</div>')
|
self.write('</div>{}</body></html>', self._script)
|
||||||
self.write(self._script)
|
|
||||||
self.write('</body></html>')
|
|
||||||
|
|
||||||
# "Low" level writing
|
# "Low" level writing
|
||||||
def write(self, s):
|
def write(self, s, *args, **kwargs):
|
||||||
"""Wrapper around handle.write"""
|
"""Wrapper around handle.write"""
|
||||||
|
if args or kwargs:
|
||||||
|
self.handle.write(s.format(*args, **kwargs))
|
||||||
|
else:
|
||||||
self.handle.write(s)
|
self.handle.write(s)
|
||||||
|
|
||||||
# With block
|
# With block
|
||||||
|
|
|
@ -33,13 +33,14 @@ def get_import_code(tlobject):
|
||||||
.format(kind, ns, tlobject.class_name)
|
.format(kind, ns, tlobject.class_name)
|
||||||
|
|
||||||
|
|
||||||
def _get_create_path_for(root, tlobject):
|
def _get_create_path_for(root, tlobject, make=True):
|
||||||
"""Creates and returns the path for the given TLObject at root."""
|
"""Creates and returns the path for the given TLObject at root."""
|
||||||
out_dir = 'methods' if tlobject.is_function else 'constructors'
|
out_dir = 'methods' if tlobject.is_function else 'constructors'
|
||||||
if tlobject.namespace:
|
if tlobject.namespace:
|
||||||
out_dir = os.path.join(out_dir, tlobject.namespace)
|
out_dir = os.path.join(out_dir, tlobject.namespace)
|
||||||
|
|
||||||
out_dir = os.path.join(root, out_dir)
|
out_dir = os.path.join(root, out_dir)
|
||||||
|
if make:
|
||||||
os.makedirs(out_dir, exist_ok=True)
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
return os.path.join(out_dir, _get_file_name(tlobject))
|
return os.path.join(out_dir, _get_file_name(tlobject))
|
||||||
|
|
||||||
|
@ -114,7 +115,9 @@ def _generate_index(folder, original_paths, root):
|
||||||
filename = os.path.join(folder, 'index.html')
|
filename = os.path.join(folder, 'index.html')
|
||||||
with DocsWriter(filename, type_to_path=_get_path_for_type) as docs:
|
with DocsWriter(filename, type_to_path=_get_path_for_type) as docs:
|
||||||
# Title should be the current folder name
|
# Title should be the current folder name
|
||||||
docs.write_head(folder.title(), relative_css_path=paths['css'])
|
docs.write_head(folder.title(),
|
||||||
|
relative_css_path=paths['css'],
|
||||||
|
default_css=original_paths['default_css'])
|
||||||
|
|
||||||
docs.set_menu_separator(paths['arrow'])
|
docs.set_menu_separator(paths['arrow'])
|
||||||
_build_menu(docs, filename, root,
|
_build_menu(docs, filename, root,
|
||||||
|
@ -206,7 +209,7 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
# * Generating the types documentation, showing available constructors.
|
# * Generating the types documentation, showing available constructors.
|
||||||
# TODO Tried using 'defaultdict(list)' with strange results, make it work.
|
# TODO Tried using 'defaultdict(list)' with strange results, make it work.
|
||||||
original_paths = {
|
original_paths = {
|
||||||
'css': 'css/docs.css',
|
'css': 'css',
|
||||||
'arrow': 'img/arrow.svg',
|
'arrow': 'img/arrow.svg',
|
||||||
'search.js': 'js/search.js',
|
'search.js': 'js/search.js',
|
||||||
'404': '404.html',
|
'404': '404.html',
|
||||||
|
@ -218,6 +221,7 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
original_paths = {k: os.path.join(output_dir, v)
|
original_paths = {k: os.path.join(output_dir, v)
|
||||||
for k, v in original_paths.items()}
|
for k, v in original_paths.items()}
|
||||||
|
|
||||||
|
original_paths['default_css'] = 'light' # docs.<name>.css, local path
|
||||||
type_to_constructors = {}
|
type_to_constructors = {}
|
||||||
type_to_functions = {}
|
type_to_functions = {}
|
||||||
for tlobject in tlobjects:
|
for tlobject in tlobjects:
|
||||||
|
@ -251,7 +255,8 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
|
|
||||||
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
||||||
docs.write_head(title=tlobject.class_name,
|
docs.write_head(title=tlobject.class_name,
|
||||||
relative_css_path=paths['css'])
|
relative_css_path=paths['css'],
|
||||||
|
default_css=original_paths['default_css'])
|
||||||
|
|
||||||
# Create the menu (path to the current TLObject)
|
# Create the menu (path to the current TLObject)
|
||||||
docs.set_menu_separator(paths['arrow'])
|
docs.set_menu_separator(paths['arrow'])
|
||||||
|
@ -392,9 +397,9 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
for k, v in original_paths.items()}
|
for k, v in original_paths.items()}
|
||||||
|
|
||||||
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
||||||
docs.write_head(
|
docs.write_head(title=snake_to_camel_case(name),
|
||||||
title=snake_to_camel_case(name),
|
relative_css_path=paths['css'],
|
||||||
relative_css_path=paths['css'])
|
default_css=original_paths['default_css'])
|
||||||
|
|
||||||
docs.set_menu_separator(paths['arrow'])
|
docs.set_menu_separator(paths['arrow'])
|
||||||
_build_menu(docs, filename, output_dir,
|
_build_menu(docs, filename, output_dir,
|
||||||
|
@ -549,7 +554,7 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
type_names = fmt(types, formatter=lambda x: x)
|
type_names = fmt(types, formatter=lambda x: x)
|
||||||
|
|
||||||
# Local URLs shouldn't rely on the output's root, so set empty root
|
# Local URLs shouldn't rely on the output's root, so set empty root
|
||||||
create_path_for = functools.partial(_get_create_path_for, '')
|
create_path_for = functools.partial(_get_create_path_for, '', make=False)
|
||||||
path_for_type = functools.partial(_get_path_for_type, '')
|
path_for_type = functools.partial(_get_path_for_type, '')
|
||||||
request_urls = fmt(methods, create_path_for)
|
request_urls = fmt(methods, create_path_for)
|
||||||
type_urls = fmt(types, path_for_type)
|
type_urls = fmt(types, path_for_type)
|
||||||
|
@ -570,7 +575,8 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir):
|
||||||
|
|
||||||
|
|
||||||
def _copy_resources(res_dir, out_dir):
|
def _copy_resources(res_dir, out_dir):
|
||||||
for dirname, files in [('css', ['docs.css']), ('img', ['arrow.svg'])]:
|
for dirname, files in [('css', ['docs.light.css', 'docs.dark.css']),
|
||||||
|
('img', ['arrow.svg'])]:
|
||||||
dirpath = os.path.join(out_dir, dirname)
|
dirpath = os.path.join(out_dir, dirname)
|
||||||
os.makedirs(dirpath, exist_ok=True)
|
os.makedirs(dirpath, exist_ok=True)
|
||||||
for file in files:
|
for file in files:
|
||||||
|
|
|
@ -477,6 +477,12 @@ def _write_arg_to_bytes(builder, arg, args, name=None):
|
||||||
# Else it may be a custom type
|
# Else it may be a custom type
|
||||||
builder.write('bytes({})', name)
|
builder.write('bytes({})', name)
|
||||||
|
|
||||||
|
# If the type is not boxed (i.e. starts with lowercase) we should
|
||||||
|
# not serialize the constructor ID (so remove its first 4 bytes).
|
||||||
|
boxed = arg.type[arg.type.find('.') + 1].isupper()
|
||||||
|
if not boxed:
|
||||||
|
builder.write('[4:]')
|
||||||
|
|
||||||
if arg.is_flag:
|
if arg.is_flag:
|
||||||
builder.write(')')
|
builder.write(')')
|
||||||
if arg.is_vector:
|
if arg.is_vector:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user