mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-11-04 09:57:29 +03:00 
			
		
		
		
	Create a more upgrade-resistant JSON-based Session class
This commit is contained in:
		
							parent
							
								
									8e3f3063f4
								
							
						
					
					
						commit
						49ca5c00c7
					
				| 
						 | 
					@ -16,7 +16,7 @@ from .network import authenticator, MtProtoSender, TcpTransport
 | 
				
			||||||
from .parser.markdown_parser import parse_message_entities
 | 
					from .parser.markdown_parser import parse_message_entities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For sending and receiving requests
 | 
					# For sending and receiving requests
 | 
				
			||||||
from .tl import MTProtoRequest, Session
 | 
					from .tl import MTProtoRequest, Session, JsonSession
 | 
				
			||||||
from .tl.all_tlobjects import layer
 | 
					from .tl.all_tlobjects import layer
 | 
				
			||||||
from .tl.functions import (InitConnectionRequest, InvokeWithLayerRequest,
 | 
					from .tl.functions import (InitConnectionRequest, InvokeWithLayerRequest,
 | 
				
			||||||
                           PingRequest)
 | 
					                           PingRequest)
 | 
				
			||||||
| 
						 | 
					@ -85,8 +85,9 @@ class TelegramClient:
 | 
				
			||||||
        self.api_hash = api_hash
 | 
					        self.api_hash = api_hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Determine what session object we have
 | 
					        # Determine what session object we have
 | 
				
			||||||
 | 
					        # TODO JsonSession until migration is complete (by v1.0)
 | 
				
			||||||
        if isinstance(session, str) or session is None:
 | 
					        if isinstance(session, str) or session is None:
 | 
				
			||||||
            self.session = Session.try_load_or_create_new(session)
 | 
					            self.session = JsonSession.try_load_or_create_new(session)
 | 
				
			||||||
        elif isinstance(session, Session):
 | 
					        elif isinstance(session, Session):
 | 
				
			||||||
            self.session = session
 | 
					            self.session = session
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +1,2 @@
 | 
				
			||||||
from .mtproto_request import MTProtoRequest
 | 
					from .mtproto_request import MTProtoRequest
 | 
				
			||||||
from .session import Session
 | 
					from .session import Session, JsonSession
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import pickle
 | 
					import pickle
 | 
				
			||||||
import random
 | 
					import random
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					from base64 import b64encode, b64decode
 | 
				
			||||||
from os.path import isfile as file_exists
 | 
					from os.path import isfile as file_exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .. import helpers as utils
 | 
					from .. import helpers as utils
 | 
				
			||||||
| 
						 | 
					@ -72,3 +74,115 @@ class Session:
 | 
				
			||||||
        now = int(time.time())
 | 
					        now = int(time.time())
 | 
				
			||||||
        correct = correct_msg_id >> 32
 | 
					        correct = correct_msg_id >> 32
 | 
				
			||||||
        self.time_offset = correct - now
 | 
					        self.time_offset = correct - now
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Until migration is complete, we need the original 'Session' class
 | 
				
			||||||
 | 
					# for Pickle to keep working. TODO Replace 'Session' by 'JsonSession' by v1.0
 | 
				
			||||||
 | 
					class JsonSession:
 | 
				
			||||||
 | 
					    """This session contains the required information to login into your
 | 
				
			||||||
 | 
					       Telegram account. NEVER give the saved JSON file to anyone, since
 | 
				
			||||||
 | 
					       they would gain instant access to all your messages and contacts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       If you think the session has been compromised, close all the sessions
 | 
				
			||||||
 | 
					       through an official Telegram client to revoke the authorization.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, session_user_id):
 | 
				
			||||||
 | 
					        self.session_user_id = session_user_id
 | 
				
			||||||
 | 
					        self.server_address = '91.108.56.165'
 | 
				
			||||||
 | 
					        self.port = 443
 | 
				
			||||||
 | 
					        self.auth_key = None
 | 
				
			||||||
 | 
					        self.id = utils.generate_random_long(signed=False)
 | 
				
			||||||
 | 
					        self.sequence = 0
 | 
				
			||||||
 | 
					        self.salt = 0  # Unsigned long
 | 
				
			||||||
 | 
					        self.time_offset = 0
 | 
				
			||||||
 | 
					        self.last_message_id = 0  # Long
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self):
 | 
				
			||||||
 | 
					        """Saves the current session object as session_user_id.session"""
 | 
				
			||||||
 | 
					        if self.session_user_id:
 | 
				
			||||||
 | 
					            with open('{}.session'.format(self.session_user_id), 'w') as file:
 | 
				
			||||||
 | 
					                json.dump({
 | 
				
			||||||
 | 
					                    'id': self.id,
 | 
				
			||||||
 | 
					                    'port': self.port,
 | 
				
			||||||
 | 
					                    'salt': self.salt,
 | 
				
			||||||
 | 
					                    'sequence': self.sequence,
 | 
				
			||||||
 | 
					                    'time_offset': self.time_offset,
 | 
				
			||||||
 | 
					                    'server_address': self.server_address,
 | 
				
			||||||
 | 
					                    'auth_key_data':
 | 
				
			||||||
 | 
					                        b64encode(self.auth_key.key).decode('ascii')
 | 
				
			||||||
 | 
					                }, file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete(self):
 | 
				
			||||||
 | 
					        """Deletes the current session file"""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            os.remove('{}.session'.format(self.session_user_id))
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        except OSError:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def try_load_or_create_new(session_user_id):
 | 
				
			||||||
 | 
					        """Loads a saved session_user_id.session or creates a new one.
 | 
				
			||||||
 | 
					           If session_user_id=None, later .save()'s will have no effect.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if session_user_id is None:
 | 
				
			||||||
 | 
					            return JsonSession(None)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            path = '{}.session'.format(session_user_id)
 | 
				
			||||||
 | 
					            result = JsonSession(session_user_id)
 | 
				
			||||||
 | 
					            if not file_exists(path):
 | 
				
			||||||
 | 
					                return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                with open(path, 'r') as file:
 | 
				
			||||||
 | 
					                    data = json.load(file)
 | 
				
			||||||
 | 
					                    result.id = data['id']
 | 
				
			||||||
 | 
					                    result.port = data['port']
 | 
				
			||||||
 | 
					                    result.salt = data['salt']
 | 
				
			||||||
 | 
					                    result.sequence = data['sequence']
 | 
				
			||||||
 | 
					                    result.time_offset = data['time_offset']
 | 
				
			||||||
 | 
					                    result.server_address = data['server_address']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # FIXME We need to import the AuthKey here or otherwise
 | 
				
			||||||
 | 
					                    # we get cyclic dependencies.
 | 
				
			||||||
 | 
					                    from ..crypto import AuthKey
 | 
				
			||||||
 | 
					                    key = b64decode(data['auth_key_data'])
 | 
				
			||||||
 | 
					                    result.auth_key = AuthKey(data=key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            except (json.decoder.JSONDecodeError, UnicodeDecodeError):
 | 
				
			||||||
 | 
					                # TODO Backwards-compatibility code
 | 
				
			||||||
 | 
					                old = Session.try_load_or_create_new(session_user_id)
 | 
				
			||||||
 | 
					                result.id = old.id
 | 
				
			||||||
 | 
					                result.port = old.port
 | 
				
			||||||
 | 
					                result.salt = old.salt
 | 
				
			||||||
 | 
					                result.sequence = old.sequence
 | 
				
			||||||
 | 
					                result.time_offset = old.time_offset
 | 
				
			||||||
 | 
					                result.server_address = old.server_address
 | 
				
			||||||
 | 
					                result.auth_key = old.auth_key
 | 
				
			||||||
 | 
					                result.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_new_msg_id(self):
 | 
				
			||||||
 | 
					        """Generates a new unique message ID based on the current
 | 
				
			||||||
 | 
					           time (in ms) since epoch"""
 | 
				
			||||||
 | 
					        # Refer to mtproto_plain_sender.py for the original method,
 | 
				
			||||||
 | 
					        ms_time = int(time.time() * 1000)
 | 
				
			||||||
 | 
					        new_msg_id = (((ms_time // 1000 + self.time_offset) << 32)
 | 
				
			||||||
 | 
					                      |  # "must approximately equal unix time*2^32"
 | 
				
			||||||
 | 
					                      ((ms_time % 1000) << 22)
 | 
				
			||||||
 | 
					                      |  # "approximate moment in time the message was created"
 | 
				
			||||||
 | 
					                      random.randint(0, 524288)
 | 
				
			||||||
 | 
					                      << 2)  # "message identifiers are divisible by 4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.last_message_id >= new_msg_id:
 | 
				
			||||||
 | 
					            new_msg_id = self.last_message_id + 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.last_message_id = new_msg_id
 | 
				
			||||||
 | 
					        return new_msg_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_time_offset(self, correct_msg_id):
 | 
				
			||||||
 | 
					        """Updates the time offset based on a known correct message ID"""
 | 
				
			||||||
 | 
					        now = int(time.time())
 | 
				
			||||||
 | 
					        correct = correct_msg_id >> 32
 | 
				
			||||||
 | 
					        self.time_offset = correct - now
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user