mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-03-03 19:00:21 +03:00
Use an Enum for the ConnectionMode and support specifying it
This commit is contained in:
parent
62a52679f4
commit
6f0bd14c2f
|
@ -1,3 +1,4 @@
|
||||||
from .telegram_bare_client import TelegramBareClient
|
from .telegram_bare_client import TelegramBareClient
|
||||||
from .telegram_client import TelegramClient
|
from .telegram_client import TelegramClient
|
||||||
|
from .network import ConnectionMode
|
||||||
from . import tl
|
from . import tl
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .mtproto_plain_sender import MtProtoPlainSender
|
from .mtproto_plain_sender import MtProtoPlainSender
|
||||||
from .authenticator import do_authentication
|
from .authenticator import do_authentication
|
||||||
from .mtproto_sender import MtProtoSender
|
from .mtproto_sender import MtProtoSender
|
||||||
from .connection import Connection
|
from .connection import Connection, ConnectionMode
|
||||||
|
|
|
@ -1,23 +1,46 @@
|
||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from zlib import crc32
|
from zlib import crc32
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from ..crypto import AESModeCTR
|
from ..crypto import AESModeCTR
|
||||||
from ..extensions import BinaryWriter, TcpClient
|
from ..extensions import BinaryWriter, TcpClient
|
||||||
from ..errors import InvalidChecksumError
|
from ..errors import InvalidChecksumError
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionMode(Enum):
|
||||||
|
"""Represents which mode should be used to stabilise a connection.
|
||||||
|
|
||||||
|
TCP_FULL: Default Telegram mode. Sends 12 additional bytes and
|
||||||
|
needs to calculate the CRC value of the packet itself.
|
||||||
|
|
||||||
|
TCP_INTERMEDIATE: Intermediate mode between TCP_FULL and TCP_ABRIDGED.
|
||||||
|
Always sends 4 extra bytes for the packet length.
|
||||||
|
|
||||||
|
TCP_ABRIDGED: This is the mode with the lowest overhead, as it will
|
||||||
|
only require 1 byte if the packet length is less than
|
||||||
|
508 bytes (127 << 2, which is very common).
|
||||||
|
|
||||||
|
TCP_OBFUSCATED: Encodes the packet just like TCP_ABRIDGED, but encrypts
|
||||||
|
every message with a randomly generated key using the
|
||||||
|
AES-CTR mode so the packets are harder to discern.
|
||||||
|
"""
|
||||||
|
TCP_FULL = 1
|
||||||
|
TCP_INTERMEDIATE = 2
|
||||||
|
TCP_ABRIDGED = 3
|
||||||
|
TCP_OBFUSCATED = 4
|
||||||
|
|
||||||
|
|
||||||
class Connection:
|
class Connection:
|
||||||
"""Represents an abstract connection (TCP, TCP abridged...).
|
"""Represents an abstract connection (TCP, TCP abridged...).
|
||||||
'mode' may be any of:
|
'mode' must be any of the ConnectionMode enumeration.
|
||||||
'tcp_full', 'tcp_intermediate', 'tcp_abridged', 'tcp_obfuscated'
|
|
||||||
|
|
||||||
Note that '.send()' and '.recv()' refer to messages, which
|
Note that '.send()' and '.recv()' refer to messages, which
|
||||||
will be packed accordingly, whereas '.write()' and '.read()'
|
will be packed accordingly, whereas '.write()' and '.read()'
|
||||||
work on plain bytes, with no further additions.
|
work on plain bytes, with no further additions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip, port, mode='tcp_intermediate',
|
def __init__(self, ip, port, mode=ConnectionMode.TCP_FULL,
|
||||||
proxy=None, timeout=timedelta(seconds=5)):
|
proxy=None, timeout=timedelta(seconds=5)):
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.port = port
|
self.port = port
|
||||||
|
@ -30,20 +53,20 @@ class Connection:
|
||||||
self.conn = TcpClient(proxy=proxy, timeout=timeout)
|
self.conn = TcpClient(proxy=proxy, timeout=timeout)
|
||||||
|
|
||||||
# Sending messages
|
# Sending messages
|
||||||
if mode == 'tcp_full':
|
if mode == ConnectionMode.TCP_FULL:
|
||||||
setattr(self, 'send', self._send_tcp_full)
|
setattr(self, 'send', self._send_tcp_full)
|
||||||
setattr(self, 'recv', self._recv_tcp_full)
|
setattr(self, 'recv', self._recv_tcp_full)
|
||||||
|
|
||||||
elif mode == 'tcp_intermediate':
|
elif mode == ConnectionMode.TCP_INTERMEDIATE:
|
||||||
setattr(self, 'send', self._send_intermediate)
|
setattr(self, 'send', self._send_intermediate)
|
||||||
setattr(self, 'recv', self._recv_intermediate)
|
setattr(self, 'recv', self._recv_intermediate)
|
||||||
|
|
||||||
elif mode in ('tcp_abridged', 'tcp_obfuscated'):
|
elif mode in (ConnectionMode.TCP_ABRIDGED, ConnectionMode.TCP_OBFUSCATED):
|
||||||
setattr(self, 'send', self._send_abridged)
|
setattr(self, 'send', self._send_abridged)
|
||||||
setattr(self, 'recv', self._recv_abridged)
|
setattr(self, 'recv', self._recv_abridged)
|
||||||
|
|
||||||
# Writing and reading from the socket
|
# Writing and reading from the socket
|
||||||
if mode == 'tcp_obfuscated':
|
if mode == ConnectionMode.TCP_OBFUSCATED:
|
||||||
setattr(self, 'write', self._write_obfuscated)
|
setattr(self, 'write', self._write_obfuscated)
|
||||||
setattr(self, 'read', self._read_obfuscated)
|
setattr(self, 'read', self._read_obfuscated)
|
||||||
else:
|
else:
|
||||||
|
@ -54,11 +77,11 @@ class Connection:
|
||||||
self._send_counter = 0
|
self._send_counter = 0
|
||||||
self.conn.connect(self.ip, self.port)
|
self.conn.connect(self.ip, self.port)
|
||||||
|
|
||||||
if self._mode == 'tcp_abridged':
|
if self._mode == ConnectionMode.TCP_ABRIDGED:
|
||||||
self.conn.write(b'\xef')
|
self.conn.write(b'\xef')
|
||||||
elif self._mode == 'tcp_intermediate':
|
elif self._mode == ConnectionMode.TCP_INTERMEDIATE:
|
||||||
self.conn.write(b'\xee\xee\xee\xee')
|
self.conn.write(b'\xee\xee\xee\xee')
|
||||||
elif self._mode == 'tcp_obfuscated':
|
elif self._mode == ConnectionMode.TCP_OBFUSCATED:
|
||||||
self._setup_obfuscation()
|
self._setup_obfuscation()
|
||||||
|
|
||||||
def _setup_obfuscation(self):
|
def _setup_obfuscation(self):
|
||||||
|
@ -103,7 +126,7 @@ class Connection:
|
||||||
def recv(self):
|
def recv(self):
|
||||||
"""Receives and unpacks a message"""
|
"""Receives and unpacks a message"""
|
||||||
# Default implementation is just an error
|
# Default implementation is just an error
|
||||||
raise ValueError('Invalid connection mode specified: ' + self._mode)
|
raise ValueError('Invalid connection mode specified: ' + str(self._mode))
|
||||||
|
|
||||||
def _recv_tcp_full(self):
|
def _recv_tcp_full(self):
|
||||||
packet_length_bytes = self.read(4)
|
packet_length_bytes = self.read(4)
|
||||||
|
@ -138,7 +161,7 @@ class Connection:
|
||||||
def send(self, message):
|
def send(self, message):
|
||||||
"""Encapsulates and sends the given message"""
|
"""Encapsulates and sends the given message"""
|
||||||
# Default implementation is just an error
|
# Default implementation is just an error
|
||||||
raise ValueError('Invalid connection mode specified: ' + self._mode)
|
raise ValueError('Invalid connection mode specified: ' + str(self._mode))
|
||||||
|
|
||||||
def _send_tcp_full(self, message):
|
def _send_tcp_full(self, message):
|
||||||
# https://core.telegram.org/mtproto#tcp-transport
|
# https://core.telegram.org/mtproto#tcp-transport
|
||||||
|
@ -174,7 +197,7 @@ class Connection:
|
||||||
# region Read implementations
|
# region Read implementations
|
||||||
|
|
||||||
def read(self, length):
|
def read(self, length):
|
||||||
raise ValueError('Invalid connection mode specified: ' + self._mode)
|
raise ValueError('Invalid connection mode specified: ' + str(self._mode))
|
||||||
|
|
||||||
def _read_plain(self, length):
|
def _read_plain(self, length):
|
||||||
return self.conn.read(length)
|
return self.conn.read(length)
|
||||||
|
@ -189,7 +212,7 @@ class Connection:
|
||||||
# region Write implementations
|
# region Write implementations
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
raise ValueError('Invalid connection mode specified: ' + self._mode)
|
raise ValueError('Invalid connection mode specified: ' + str(self._mode))
|
||||||
|
|
||||||
def _write_plain(self, data):
|
def _write_plain(self, data):
|
||||||
self.conn.write(data)
|
self.conn.write(data)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from . import helpers as utils
|
||||||
from .errors import (
|
from .errors import (
|
||||||
RPCError, FloodWaitError, FileMigrateError, TypeNotFoundError
|
RPCError, FloodWaitError, FileMigrateError, TypeNotFoundError
|
||||||
)
|
)
|
||||||
from .network import authenticator, MtProtoSender, Connection
|
from .network import authenticator, MtProtoSender, Connection, ConnectionMode
|
||||||
from .utils import get_appropriated_part_size
|
from .utils import get_appropriated_part_size
|
||||||
from .crypto import rsa, CdnDecrypter
|
from .crypto import rsa, CdnDecrypter
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ class TelegramBareClient:
|
||||||
# region Initialization
|
# region Initialization
|
||||||
|
|
||||||
def __init__(self, session, api_id, api_hash,
|
def __init__(self, session, api_id, api_hash,
|
||||||
|
connection_mode=ConnectionMode.TCP_FULL,
|
||||||
proxy=None, timeout=timedelta(seconds=5)):
|
proxy=None, timeout=timedelta(seconds=5)):
|
||||||
"""Initializes the Telegram client with the specified API ID and Hash.
|
"""Initializes the Telegram client with the specified API ID and Hash.
|
||||||
Session must always be a Session instance, and an optional proxy
|
Session must always be a Session instance, and an optional proxy
|
||||||
|
@ -74,6 +75,7 @@ class TelegramBareClient:
|
||||||
self.session = session
|
self.session = session
|
||||||
self.api_id = int(api_id)
|
self.api_id = int(api_id)
|
||||||
self.api_hash = api_hash
|
self.api_hash = api_hash
|
||||||
|
self._connection_mode = connection_mode
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger(__name__)
|
||||||
|
@ -125,7 +127,7 @@ class TelegramBareClient:
|
||||||
|
|
||||||
connection = Connection(
|
connection = Connection(
|
||||||
self.session.server_address, self.session.port,
|
self.session.server_address, self.session.port,
|
||||||
proxy=self.proxy, timeout=self._timeout
|
mode=self._connection_mode, proxy=self.proxy, timeout=self._timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -5,6 +5,7 @@ from threading import Event, RLock, Thread
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
|
|
||||||
from . import TelegramBareClient
|
from . import TelegramBareClient
|
||||||
|
from .network import ConnectionMode
|
||||||
|
|
||||||
# Import some externalized utilities to work with the Telegram types and more
|
# Import some externalized utilities to work with the Telegram types and more
|
||||||
from . import helpers as utils
|
from . import helpers as utils
|
||||||
|
@ -62,7 +63,9 @@ class TelegramClient(TelegramBareClient):
|
||||||
|
|
||||||
# region Initialization
|
# region Initialization
|
||||||
|
|
||||||
def __init__(self, session, api_id, api_hash, proxy=None,
|
def __init__(self, session, api_id, api_hash,
|
||||||
|
connection_mode=ConnectionMode.TCP_FULL,
|
||||||
|
proxy=None,
|
||||||
timeout=timedelta(seconds=5),
|
timeout=timedelta(seconds=5),
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""Initializes the Telegram client with the specified API ID and Hash.
|
"""Initializes the Telegram client with the specified API ID and Hash.
|
||||||
|
@ -72,6 +75,10 @@ class TelegramClient(TelegramBareClient):
|
||||||
would probably not work). Pass 'None' for it to be a temporary
|
would probably not work). Pass 'None' for it to be a temporary
|
||||||
session - remember to '.log_out()'!
|
session - remember to '.log_out()'!
|
||||||
|
|
||||||
|
The 'connection_mode' should be any value under ConnectionMode.
|
||||||
|
This will only affect how messages are sent over the network
|
||||||
|
and how much processing is required before sending them.
|
||||||
|
|
||||||
If more named arguments are provided as **kwargs, they will be
|
If more named arguments are provided as **kwargs, they will be
|
||||||
used to update the Session instance. Most common settings are:
|
used to update the Session instance. Most common settings are:
|
||||||
device_model = platform.node()
|
device_model = platform.node()
|
||||||
|
@ -93,7 +100,10 @@ class TelegramClient(TelegramBareClient):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'The given session must be a str or a Session instance.')
|
'The given session must be a str or a Session instance.')
|
||||||
|
|
||||||
super().__init__(session, api_id, api_hash, proxy, timeout=timeout)
|
super().__init__(
|
||||||
|
session, api_id, api_hash,
|
||||||
|
connection_mode=connection_mode, proxy=proxy, timeout=timeout
|
||||||
|
)
|
||||||
|
|
||||||
# Safety across multiple threads (for the updates thread)
|
# Safety across multiple threads (for the updates thread)
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient, ConnectionMode
|
||||||
from telethon.errors import SessionPasswordNeededError
|
from telethon.errors import SessionPasswordNeededError
|
||||||
from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage
|
from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage
|
||||||
from telethon.utils import get_display_name
|
from telethon.utils import get_display_name
|
||||||
|
@ -49,7 +49,10 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
print_title('Initialization')
|
print_title('Initialization')
|
||||||
|
|
||||||
print('Initializing interactive example...')
|
print('Initializing interactive example...')
|
||||||
super().__init__(session_user_id, api_id, api_hash, proxy)
|
super().__init__(
|
||||||
|
session_user_id, api_id, api_hash,
|
||||||
|
connection_mode=ConnectionMode.TCP_ABRIDGED, proxy=proxy
|
||||||
|
)
|
||||||
|
|
||||||
# Store all the found media in memory here,
|
# Store all the found media in memory here,
|
||||||
# so it can be downloaded if the user wants
|
# so it can be downloaded if the user wants
|
||||||
|
|
Loading…
Reference in New Issue
Block a user