<commit_before>"""
This module contains the class used to communicate with Telegram's servers
encrypting every packet, and relies on a valid AuthKey in the used Session.
"""
import logging
from threading import Lock

from .. import helpers, utils
from ..errors import (
    BadMessageError, InvalidChecksumError, BrokenAuthKeyError,
    rpc_message_to_error
)
from ..extensions import BinaryReader
from ..tl import TLMessage, MessageContainer, GzipPacked
from ..tl.all_tlobjects import tlobjects
from ..tl.functions import InvokeAfterMsgRequest
from ..tl.functions.auth import LogOutRequest
from ..tl.types import (
    MsgsAck, Pong, BadServerSalt, BadMsgNotification, FutureSalts,
    MsgNewDetailedInfo, NewSessionCreated, MsgDetailedInfo
)

__log__ = logging.getLogger(__name__)


class MtProtoSender:
    """
    MTProto Mobile Protocol sender
    (https://core.telegram.org/mtproto/description).

    Note that this class is not thread-safe, and calling send/receive
    from two or more threads at the same time is undefined behaviour.
    Rationale:
        a new connection should be spawned to send/receive requests
        in parallel, so thread-safety (hence locking) isn't needed.
    """

    def __init__(self, session, connection):
        """
        Initializes a new MTProto sender.

        :param session:
            the Session to be used with this sender. Must contain the IP and
            port of the server, salt, ID, and AuthKey,
        :param connection:
            the Connection to be used.
        """
        self.session = session
        self.connection = connection

        # Message IDs that need confirmation
        self._need_confirmation = set()

        # Requests (as msg_id: Message) sent waiting to be received
        self._pending_receive = {}

        # Multithreading
        self._send_lock = Lock()

        # If  we're  invoking something from an  update thread  but we're also
        # receiving other request from the main thread (e.g. an update arrives
        # and we need to process it)  we must  ensure that only one is calling
        # receive at a given moment, since the receive step is fragile.
        self._recv_lock = Lock()

    def connect(self):
        """Connects to the server."""
        self.connection.connect(self.session.server_address, self.session.port)

    def is_connected(self):
        """
        Determines whether the sender is connected or not.

        :return: true if the sender is connected.
        """
        return self.connection.is_connected()

    def disconnect(self):
        """Disconnects from the server."""
        __log__.info('Disconnecting MtProtoSender...')
        self.connection.close()
        self._need_confirmation.clear()
        self._clear_all_pending()

    # region Send and receive

    def send(self, requests, ordered=False):
        """
        Sends the specified TLObject(s) (which must be requests),
        and acknowledging any message which needed confirmation.

        :param requests: the requests to be sent.
        :param ordered: whether the requests should be invoked in the
                        order in which they appear or they can be executed
                        in arbitrary order in the server.
        """
        if not utils.is_list_like(requests):
            requests = (requests,)

        if ordered:
            requests = iter(requests)
            messages = [TLMessage(self.session, next(requests))]
            for r in requests:
                messages.append(TLMessage(self.session, r,
                                          after_id=messages[-1].msg_id))
        else:<commit_msg>Fix lost requests on disconnect need_confirmation.clear<commit_after>"""
This module contains the class used to communicate with Telegram's servers
encrypting every packet, and relies on a valid AuthKey in the used Session.
"""
import logging
from threading import Lock

from .. import helpers, utils
from ..errors import (
    BadMessageError, InvalidChecksumError, BrokenAuthKeyError,
    rpc_message_to_error
)
from ..extensions import BinaryReader
from ..tl import TLMessage, MessageContainer, GzipPacked
from ..tl.all_tlobjects import tlobjects
from ..tl.functions import InvokeAfterMsgRequest
from ..tl.functions.auth import LogOutRequest
from ..tl.types import (
    MsgsAck, Pong, BadServerSalt, BadMsgNotification, FutureSalts,
    MsgNewDetailedInfo, NewSessionCreated, MsgDetailedInfo
)

__log__ = logging.getLogger(__name__)


class MtProtoSender:
    """
    MTProto Mobile Protocol sender
    (https://core.telegram.org/mtproto/description).

    Note that this class is not thread-safe, and calling send/receive
    from two or more threads at the same time is undefined behaviour.
    Rationale:
        a new connection should be spawned to send/receive requests
        in parallel, so thread-safety (hence locking) isn't needed.
    """

    def __init__(self, session, connection):
        """
        Initializes a new MTProto sender.

        :param session:
            the Session to be used with this sender. Must contain the IP and
            port of the server, salt, ID, and AuthKey,
        :param connection:
            the Connection to be used.
        """
        self.session = session
        self.connection = connection

        # Message IDs that need confirmation
        self._need_confirmation = set()

        # Requests (as msg_id: Message) sent waiting to be received
        self._pending_receive = {}

        # Multithreading
        self._send_lock = Lock()

        # If  we're  invoking something from an  update thread  but we're also
        # receiving other request from the main thread (e.g. an update arrives
        # and we need to process it)  we must  ensure that only one is calling
        # receive at a given moment, since the receive step is fragile.
        self._recv_lock = Lock()

    def connect(self):
        """Connects to the server."""
        self.connection.connect(self.session.server_address, self.session.port)

    def is_connected(self):
        """
        Determines whether the sender is connected or not.

        :return: true if the sender is connected.
        """
        return self.connection.is_connected()

    def disconnect(self):
        """Disconnects from the server."""
        __log__.info('Disconnecting MtProtoSender...')
        self.connection.close()
        self._clear_all_pending()

    # region Send and receive

    def send(self, requests, ordered=False):
        """
        Sends the specified TLObject(s) (which must be requests),
        and acknowledging any message which needed confirmation.

        :param requests: the requests to be sent.
        :param ordered: whether the requests should be invoked in the
                        order in which they appear or they can be executed
                        in arbitrary order in the server.
        """
        if not utils.is_list_like(requests):
            requests = (requests,)

        if ordered:
            requests = iter(requests)
            messages = [TLMessage(self.session, next(requests))]
            for r in requests:
                messages.append(TLMessage(self.session, r,
                                          after_id=messages[-1].msg_id))
        else:
{'fraction_non_alphanumeric': 0.07312739201749589, 'fraction_numerical': 0.0002733734281027884, 'mean_word_length': 2.422357343311506, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1766967', 'n_tokens_mistral': 1891, 'n_tokens_neox': 1811, 'n_words': 703}