From 1fccaac43fa960aa44763e29a49331333505dadb Mon Sep 17 00:00:00 2001 From: Yee Jia Rong <28086837+fourjr@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:01:31 +0800 Subject: [PATCH] Add Client.get_authorizations method and accompanying types --- .../src/telethon/_impl/client/client/auth.py | 14 +- .../telethon/_impl/client/client/client.py | 21 +++ .../telethon/_impl/client/types/__init__.py | 1 + .../_impl/client/types/authorizations.py | 144 ++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 client/src/telethon/_impl/client/types/authorizations.py diff --git a/client/src/telethon/_impl/client/client/auth.py b/client/src/telethon/_impl/client/client/auth.py index eb5bc2ec..2661a19d 100644 --- a/client/src/telethon/_impl/client/client/auth.py +++ b/client/src/telethon/_impl/client/client/auth.py @@ -9,7 +9,7 @@ from ...mtproto import RpcError from ...session import DataCenter from ...session import User as SessionUser from ...tl import abcs, functions, types -from ..types import LoginToken, PasswordToken, User +from ..types import Authorizations, LoginToken, PasswordToken, User from .net import connect_sender if TYPE_CHECKING: @@ -266,3 +266,15 @@ async def sign_out(self: Client) -> None: self._session.user = None self._session.state = None await self._storage.save(self._session) + + +async def get_authorizations(self: Client) -> Optional[Authorizations]: + try: + result = await self(functions.account.get_authorizations()) + except RpcError as e: + if e.code == 401: + return None + else: + raise + + return Authorizations._from_raw(self, result) diff --git a/client/src/telethon/_impl/client/client/client.py b/client/src/telethon/_impl/client/client/client.py index 2d434022..4f76ff4d 100644 --- a/client/src/telethon/_impl/client/client/client.py +++ b/client/src/telethon/_impl/client/client/client.py @@ -30,6 +30,7 @@ from ..types import ( AdminRight, AlbumBuilder, AsyncList, + Authorizations, Channel, ChatRestriction, Dialog, @@ -51,6 +52,7 @@ from ..types import ( from .auth import ( bot_sign_in, check_password, + get_authorizations, interactive_login, is_authorized, request_login_code, @@ -681,6 +683,25 @@ class Client: """ return get_admin_log(self, chat) + async def get_authorizations(self) -> Optional[Authorizations]: + """ + Gets all current authorizations (sessions) of the logged in user. + + :return: + The authorizations (sessions) associated with the logged-in account, or :data:`None` if the client is not authorized. + + .. rubric:: Example + + .. code-block:: python + + authorizations = await client.get_authorizations() + assert authorizations is not None, "not logged in!" + + for auth in authorizations.sessions: + print(auth.device_model, auth.platform, auth.app_name) + """ + return await get_authorizations(self) + def get_contacts(self) -> AsyncList[User]: """ Get the users in your contact list. diff --git a/client/src/telethon/_impl/client/types/__init__.py b/client/src/telethon/_impl/client/types/__init__.py index 906616f7..322941b4 100644 --- a/client/src/telethon/_impl/client/types/__init__.py +++ b/client/src/telethon/_impl/client/types/__init__.py @@ -1,6 +1,7 @@ from .admin_right import AdminRight from .album_builder import AlbumBuilder from .async_list import AsyncList +from .authorizations import Authorizations, Authorization from .callback_answer import CallbackAnswer from .chat_restriction import ChatRestriction from .dialog import Dialog diff --git a/client/src/telethon/_impl/client/types/authorizations.py b/client/src/telethon/_impl/client/types/authorizations.py new file mode 100644 index 00000000..5793d0ee --- /dev/null +++ b/client/src/telethon/_impl/client/types/authorizations.py @@ -0,0 +1,144 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from typing_extensions import Self + +from ...tl import abcs +from .meta import NoPublicConstructor + +if TYPE_CHECKING: + from ..client.client import Client + + +class Authorizations(metaclass=NoPublicConstructor): + """ + Authorizations, logged in sessions. + + You can get a user's authorizations from methods such as :meth:`telethon.Client.get_authorizations`. + """ + + def __init__( + self, + client: Client, + raw: abcs.account.Authorizations, + ) -> None: + assert isinstance(raw, abcs.account.Authorizations) + self._client = client + self._raw = raw + self._authorizations = [Authorization._from_raw(client, auth) for auth in self._raw.authorizations] + + @classmethod + def _from_raw( + cls, + client: Client, + authorizations: abcs.account.Authorizations, + ) -> Self: + return cls._create(client, authorizations) + + @property + def ttl_days(self) -> int: + return self._raw.authorization_ttl_days + + @property + def sessions(self) -> List[abcs.Authorization]: + return self._authorizations + + +class Authorization(metaclass=NoPublicConstructor): + """ + A single authorization. + + This will only be constructed as part of :meth:`telethon.Client.get_authorizations`. + """ + + def __init__( + self, + client: Client, + raw: abcs.Authorization, + ) -> None: + assert isinstance(raw, abcs.Authorization) + self._client = client + self._raw = raw + + @classmethod + def _from_raw( + cls, + client: Client, + authorization: abcs.Authorization, + ) -> Self: + return cls._create(client, authorization) + + @property + def api_id(self) -> int: + return self._raw.api_id + + @property + def app_name(self) -> str: + return self._raw.app_name + + @property + def app_version(self) -> str: + return self._raw.app_version + + @property + def call_requests_disabled(self) -> bool: + return self._raw.call_requests_disabled + + @property + def country(self) -> str: + return self._raw.country + + @property + def current(self) -> bool: + return self._raw.current + + @property + def date_active(self) -> int: + return self._raw.date_active + + @property + def date_created(self) -> int: + return self._raw.date_created + + @property + def device_model(self) -> str: + return self._raw.device_model + + @property + def encrypted_requests_disabled(self) -> bool: + return self._raw.encrypted_requests_disabled + + @property + def hash(self) -> int: + return self._raw.hash + + @property + def ip(self) -> Optional[str]: + if self._raw.ip == '': + return None + return self._raw.ip + + @property + def official_app(self) -> bool: + return self._raw.official_app + + @property + def password_pending(self) -> bool: + return self._raw.password_pending + + @property + def platform(self) -> str: + return self._raw.platform + + @property + def region(self) -> str: + return self._raw.region + + @property + def system_version(self) -> str: + return self._raw.system_version + + @property + def unconfirmed(self) -> bool: + return self._raw.unconfirmed