pararamio-aio 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. pararamio_aio/__init__.py +26 -0
  2. pararamio_aio/_core/__init__.py +125 -0
  3. pararamio_aio/_core/_types.py +120 -0
  4. pararamio_aio/_core/base.py +143 -0
  5. pararamio_aio/_core/client_protocol.py +90 -0
  6. pararamio_aio/_core/constants/__init__.py +7 -0
  7. pararamio_aio/_core/constants/base.py +9 -0
  8. pararamio_aio/_core/constants/endpoints.py +84 -0
  9. pararamio_aio/_core/cookie_decorator.py +208 -0
  10. pararamio_aio/_core/cookie_manager.py +1222 -0
  11. pararamio_aio/_core/endpoints.py +67 -0
  12. pararamio_aio/_core/exceptions/__init__.py +6 -0
  13. pararamio_aio/_core/exceptions/auth.py +91 -0
  14. pararamio_aio/_core/exceptions/base.py +124 -0
  15. pararamio_aio/_core/models/__init__.py +17 -0
  16. pararamio_aio/_core/models/base.py +66 -0
  17. pararamio_aio/_core/models/chat.py +92 -0
  18. pararamio_aio/_core/models/post.py +65 -0
  19. pararamio_aio/_core/models/user.py +54 -0
  20. pararamio_aio/_core/py.typed +2 -0
  21. pararamio_aio/_core/utils/__init__.py +73 -0
  22. pararamio_aio/_core/utils/async_requests.py +417 -0
  23. pararamio_aio/_core/utils/auth_flow.py +202 -0
  24. pararamio_aio/_core/utils/authentication.py +235 -0
  25. pararamio_aio/_core/utils/captcha.py +92 -0
  26. pararamio_aio/_core/utils/helpers.py +336 -0
  27. pararamio_aio/_core/utils/http_client.py +199 -0
  28. pararamio_aio/_core/utils/requests.py +424 -0
  29. pararamio_aio/_core/validators.py +78 -0
  30. pararamio_aio/_types.py +29 -0
  31. pararamio_aio/client.py +989 -0
  32. pararamio_aio/constants/__init__.py +16 -0
  33. pararamio_aio/exceptions/__init__.py +31 -0
  34. pararamio_aio/exceptions/base.py +1 -0
  35. pararamio_aio/file_operations.py +232 -0
  36. pararamio_aio/models/__init__.py +31 -0
  37. pararamio_aio/models/activity.py +127 -0
  38. pararamio_aio/models/attachment.py +141 -0
  39. pararamio_aio/models/base.py +83 -0
  40. pararamio_aio/models/bot.py +274 -0
  41. pararamio_aio/models/chat.py +722 -0
  42. pararamio_aio/models/deferred_post.py +174 -0
  43. pararamio_aio/models/file.py +103 -0
  44. pararamio_aio/models/group.py +361 -0
  45. pararamio_aio/models/poll.py +275 -0
  46. pararamio_aio/models/post.py +643 -0
  47. pararamio_aio/models/team.py +403 -0
  48. pararamio_aio/models/user.py +239 -0
  49. pararamio_aio/py.typed +2 -0
  50. pararamio_aio/utils/__init__.py +18 -0
  51. pararamio_aio/utils/authentication.py +383 -0
  52. pararamio_aio/utils/requests.py +75 -0
  53. pararamio_aio-3.0.0.dist-info/METADATA +269 -0
  54. pararamio_aio-3.0.0.dist-info/RECORD +56 -0
  55. pararamio_aio-3.0.0.dist-info/WHEEL +5 -0
  56. pararamio_aio-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,67 @@
1
+ """API endpoints constants for pararamio."""
2
+
3
+ __all__ = [
4
+ 'AUTH_ENDPOINTS',
5
+ 'CHAT_ENDPOINTS',
6
+ 'FILE_ENDPOINTS',
7
+ 'POST_ENDPOINTS',
8
+ 'USER_ENDPOINTS',
9
+ ]
10
+
11
+
12
+ # User-related endpoints
13
+ USER_ENDPOINTS = {
14
+ 'search': '/user/search',
15
+ 'list': '/user/list',
16
+ 'profile': '/user/profile',
17
+ 'activity': '/activity',
18
+ }
19
+
20
+ # Chat-related endpoints
21
+ CHAT_ENDPOINTS = {
22
+ 'core': '/core/chat',
23
+ 'list': '/core/chat',
24
+ 'create': '/core/chat',
25
+ 'edit': '/core/chat/{chat_id}',
26
+ 'delete': '/core/chat/{chat_id}',
27
+ 'transfer': '/core/chat/{chat_id}/transfer/{org_id}',
28
+ 'hide': '/core/chat/{chat_id}/hide',
29
+ 'show': '/core/chat/{chat_id}/show',
30
+ 'favorite': '/core/chat/{chat_id}/favorite',
31
+ 'unfavorite': '/core/chat/{chat_id}/unfavorite',
32
+ 'enter': '/core/chat/{chat_id}/enter',
33
+ 'quit': '/core/chat/{chat_id}/quit',
34
+ 'custom_title': '/core/chat/{chat_id}/custom_title',
35
+ 'add_users': '/core/chat/{chat_id}/user/{ids}',
36
+ 'delete_users': '/core/chat/{chat_id}/user/{ids}',
37
+ 'add_admins': '/core/chat/{chat_id}/admin/{ids}',
38
+ 'delete_admins': '/core/chat/{chat_id}/admin/{ids}',
39
+ 'add_groups': '/core/chat/{chat_id}/group/{ids}',
40
+ 'delete_groups': '/core/chat/{chat_id}/group/{ids}',
41
+ 'private_message': '/core/chat/pm/{user_id}',
42
+ 'sync': '/core/chat/sync',
43
+ }
44
+
45
+ # Post-related endpoints
46
+ POST_ENDPOINTS = {
47
+ 'list': '/msg/post',
48
+ 'create': '/msg/post',
49
+ 'edit': '/msg/post/{post_id}',
50
+ 'delete': '/msg/post/{post_id}',
51
+ 'search': '/posts/search',
52
+ 'last_read': '/msg/lastread/{chat_id}',
53
+ }
54
+
55
+ # Authentication endpoints
56
+ AUTH_ENDPOINTS = {
57
+ 'login': '/auth/login',
58
+ 'logout': '/auth/logout',
59
+ 'second_step': '/auth/second_step',
60
+ 'profile': '/auth/profile',
61
+ }
62
+
63
+ # File-related endpoints
64
+ FILE_ENDPOINTS = {
65
+ 'upload': '/msg/file',
66
+ 'download': '/msg/file/{file_id}',
67
+ }
@@ -0,0 +1,6 @@
1
+ """Exceptions module."""
2
+
3
+ # Import base exceptions
4
+ # Import auth-specific exceptions
5
+ from .auth import *
6
+ from .base import *
@@ -0,0 +1,91 @@
1
+ """Authentication-specific exceptions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from .base import PararamioException
8
+
9
+ __all__ = (
10
+ 'AuthenticationException',
11
+ 'CaptchaRequiredException',
12
+ 'InvalidCredentialsException',
13
+ 'RateLimitException',
14
+ 'SessionExpiredException',
15
+ 'TwoFactorFailedException',
16
+ 'TwoFactorRequiredException',
17
+ 'XSRFTokenException',
18
+ )
19
+
20
+
21
+ class AuthenticationException(PararamioException):
22
+ """Base authentication exception."""
23
+
24
+ def __init__(
25
+ self,
26
+ message: str,
27
+ error_code: str | None = None,
28
+ response_data: dict[str, Any] | None = None,
29
+ ):
30
+ super().__init__(message)
31
+ self.error_code = error_code
32
+ self.response_data = response_data or {}
33
+
34
+
35
+ class InvalidCredentialsException(AuthenticationException):
36
+ """Invalid login credentials."""
37
+
38
+ def __init__(self, message: str = 'Invalid email or password'):
39
+ super().__init__(message, error_code='invalid_credentials')
40
+
41
+
42
+ class XSRFTokenException(AuthenticationException):
43
+ """XSRF token related errors."""
44
+
45
+ def __init__(self, message: str = 'XSRF token validation failed'):
46
+ super().__init__(message, error_code='xsrf_token_error')
47
+
48
+
49
+ class TwoFactorRequiredException(AuthenticationException):
50
+ """Two-factor authentication is required."""
51
+
52
+ def __init__(self, message: str = 'Two-factor authentication required'):
53
+ super().__init__(message, error_code='2fa_required')
54
+
55
+
56
+ class TwoFactorFailedException(AuthenticationException):
57
+ """Two-factor authentication failed."""
58
+
59
+ def __init__(self, message: str = 'Invalid two-factor authentication code'):
60
+ super().__init__(message, error_code='2fa_failed')
61
+
62
+
63
+ class CaptchaRequiredException(AuthenticationException):
64
+ """Captcha verification required."""
65
+
66
+ def __init__(
67
+ self,
68
+ message: str = 'Captcha verification required',
69
+ captcha_url: str | None = None,
70
+ ):
71
+ super().__init__(message, error_code='captcha_required')
72
+ self.captcha_url = captcha_url
73
+
74
+
75
+ class RateLimitException(AuthenticationException):
76
+ """Rate limit exceeded (429 Too Many Requests)."""
77
+
78
+ def __init__(
79
+ self,
80
+ message: str = 'Rate limit exceeded',
81
+ retry_after: int | None = None,
82
+ ):
83
+ super().__init__(message, error_code='rate_limit')
84
+ self.retry_after = retry_after
85
+
86
+
87
+ class SessionExpiredException(AuthenticationException):
88
+ """Session has expired."""
89
+
90
+ def __init__(self, message: str = 'Session has expired, please authenticate again'):
91
+ super().__init__(message, error_code='session_expired')
@@ -0,0 +1,124 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ 'PararamModelNotLoaded',
5
+ 'PararamNoNextPost',
6
+ 'PararamNoPrevPost',
7
+ 'PararamNotFound',
8
+ 'PararamioAuthenticationException',
9
+ 'PararamioCaptchaAuthenticationException',
10
+ 'PararamioException',
11
+ 'PararamioHTTPRequestException',
12
+ 'PararamioLimitExceededException',
13
+ 'PararamioMethodNotAllowed',
14
+ 'PararamioPasswordAuthenticationException',
15
+ 'PararamioRequestException',
16
+ 'PararamioSecondFactorAuthenticationException',
17
+ 'PararamioServerResponseException',
18
+ 'PararamioValidationException',
19
+ 'PararamioXSFRRequestError',
20
+ )
21
+
22
+ import json
23
+ from json import JSONDecodeError
24
+ from typing import IO
25
+ from urllib.error import HTTPError
26
+
27
+
28
+ class PararamioException(Exception): # noqa: N818
29
+ pass
30
+
31
+
32
+ class PararamioValidationException(PararamioException):
33
+ pass
34
+
35
+
36
+ class PararamModelNotLoaded(PararamioException):
37
+ """Exception raised when trying to access an attribute that hasn't been loaded yet."""
38
+
39
+
40
+ class PararamNotFound(PararamioException):
41
+ pass
42
+
43
+
44
+ class PararamioHTTPRequestException(HTTPError, PararamioException): # noqa: N818 # pylint: disable=too-many-ancestors
45
+ _response: bytes | None
46
+ fp: IO[bytes]
47
+
48
+ def __init__(self, url: str, code: int, msg: str, hdrs: list[tuple[str, str]], fp: IO[bytes]):
49
+ self._response = None
50
+ self.msg = msg
51
+ super().__init__(url, code, msg, hdrs, fp) # type: ignore
52
+
53
+ @property
54
+ def response(self):
55
+ if not self._response and self.fp is not None:
56
+ self._response = self.fp.read()
57
+ return self._response
58
+
59
+ @property
60
+ def message(self) -> str | None:
61
+ if self.code in [403, 400]:
62
+ try:
63
+ resp = json.loads(self.response)
64
+ return resp.get('error', None) or resp.get('message', None)
65
+ except JSONDecodeError:
66
+ pass
67
+ return None
68
+
69
+ def __str__(self):
70
+ msg = self.message
71
+ if msg:
72
+ return msg
73
+ return str(super(HTTPError, self))
74
+
75
+
76
+ class PararamioRequestException(PararamioException):
77
+ pass
78
+
79
+
80
+ class PararamioServerResponseException(PararamioRequestException):
81
+ response: dict
82
+
83
+ def __init__(self, msg: str, response: dict):
84
+ self.msg = msg
85
+ self.response = response
86
+
87
+ def __str__(self):
88
+ return f'{self.__class__.__name__}, {self.msg or " has been raised"}'
89
+
90
+
91
+ class PararamioLimitExceededException(PararamioRequestException):
92
+ pass
93
+
94
+
95
+ class PararamioMethodNotAllowed(PararamioException):
96
+ pass
97
+
98
+
99
+ class PararamioAuthenticationException(PararamioException):
100
+ retry_after: int | None = None
101
+
102
+
103
+ class PararamioXSFRRequestError(PararamioAuthenticationException):
104
+ pass
105
+
106
+
107
+ class PararamioPasswordAuthenticationException(PararamioAuthenticationException):
108
+ pass
109
+
110
+
111
+ class PararamioSecondFactorAuthenticationException(PararamioAuthenticationException):
112
+ pass
113
+
114
+
115
+ class PararamioCaptchaAuthenticationException(PararamioAuthenticationException):
116
+ pass
117
+
118
+
119
+ class PararamNoNextPost(PararamioException, StopIteration):
120
+ pass
121
+
122
+
123
+ class PararamNoPrevPost(PararamioException, StopIteration):
124
+ pass
@@ -0,0 +1,17 @@
1
+ """Core models for pararamio packages."""
2
+
3
+ from .base import *
4
+ from .chat import *
5
+ from .post import *
6
+ from .user import *
7
+
8
+ __all__ = [
9
+ # Base classes
10
+ 'CoreBaseModel',
11
+ # Models
12
+ 'CoreChat',
13
+ 'CoreClientObject',
14
+ 'CorePost',
15
+ 'CoreUser',
16
+ 'UserInfoParsedItem',
17
+ ]
@@ -0,0 +1,66 @@
1
+ """Simplified base classes for core models without lazy loading."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from pararamio_aio._core._types import FormatterT
9
+
10
+ __all__ = (
11
+ 'CoreBaseModel',
12
+ 'CoreClientObject',
13
+ )
14
+
15
+
16
+ class CoreBaseModel:
17
+ """Base model class without lazy loading magic."""
18
+
19
+ _data: dict[str, Any]
20
+ _attr_formatters: FormatterT
21
+
22
+ def __init__(self, **kwargs: Any) -> None:
23
+ self._data = kwargs
24
+ self._attr_formatters = {}
25
+
26
+ def __getattribute__(self, key: str) -> Any:
27
+ # Simple version without lazy loading
28
+ try:
29
+ return super().__getattribute__(key)
30
+ except AttributeError:
31
+ if key.startswith('_'):
32
+ raise
33
+
34
+ # Get from _data if exists
35
+ data = super().__getattribute__('_data')
36
+ if key in data:
37
+ value = data[key]
38
+ # Apply formatter if exists
39
+ formatters = super().__getattribute__('_attr_formatters')
40
+ if key in formatters:
41
+ return formatters[key](data, key)
42
+ return value
43
+ raise
44
+
45
+ def load_data(self, data: dict[str, Any]) -> None:
46
+ """Load data into the model."""
47
+ self._data.update(data)
48
+
49
+ def to_dict(self) -> dict[str, Any]:
50
+ """Get data as dictionary."""
51
+ return self._data.copy()
52
+
53
+
54
+ class CoreClientObject:
55
+ """Base class for objects that need client reference."""
56
+
57
+ _client: Any # Union[ClientProtocol, AsyncClientProtocol] but without import
58
+
59
+ def __init__(self, client: Any, **kwargs: Any) -> None:
60
+ self._client = client
61
+ # kwargs are passed to subclasses, not used here
62
+ _ = kwargs
63
+
64
+ @property
65
+ def client(self) -> Any:
66
+ return self._client
@@ -0,0 +1,92 @@
1
+ """Core Chat model without lazy loading."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from pararamio_aio._core.utils.helpers import parse_iso_datetime
8
+
9
+ from .base import CoreBaseModel, CoreClientObject
10
+
11
+ if TYPE_CHECKING:
12
+ from datetime import datetime
13
+
14
+ from pararamio_aio._core._types import FormatterT
15
+
16
+ __all__ = ('CoreChat',)
17
+
18
+
19
+ # Attribute formatters for Chat
20
+ CHAT_ATTR_FORMATTERS: FormatterT = {
21
+ 'time_edited': parse_iso_datetime,
22
+ 'time_updated': parse_iso_datetime,
23
+ 'time_created': parse_iso_datetime,
24
+ 'user_time_edited': parse_iso_datetime,
25
+ }
26
+
27
+
28
+ class CoreChat(CoreBaseModel, CoreClientObject):
29
+ """Core Chat model with common functionality."""
30
+
31
+ # Chat attributes (copied from original)
32
+ id: int
33
+ title: str
34
+ history_mode: str
35
+ description: str | None
36
+ posts_count: int
37
+ pm: bool
38
+ e2e: bool
39
+ time_created: datetime
40
+ time_updated: datetime
41
+ time_edited: datetime | None
42
+ author_id: int
43
+ two_step_required: bool
44
+ org_visible: bool
45
+ organization_id: int | None
46
+ posts_live_time: int | None
47
+ allow_api: bool
48
+ read_only: bool
49
+ tnew: bool
50
+ adm_flag: bool
51
+ custom_title: str | None
52
+ is_favorite: bool
53
+ inviter_id: int | None
54
+ tshow: bool
55
+ user_time_edited: datetime
56
+ history_start: int
57
+ pinned: list[int]
58
+ thread_groups: list[int]
59
+ thread_users: list[int]
60
+ thread_admins: list[int]
61
+ thread_users_all: list[int]
62
+ last_msg_author_id: int | None
63
+ last_msg_author: str
64
+ last_msg_bot_name: str
65
+ last_msg_text: str
66
+ last_msg: str
67
+ last_read_post_no: int
68
+ thread_guests: list[int]
69
+
70
+ def __init__(self, client: Any, chat_id: int | None = None, **kwargs: Any) -> None:
71
+ # ID processing (as in original)
72
+ if chat_id is None:
73
+ chat_id = kwargs.get('chat_id')
74
+ if chat_id is None:
75
+ chat_id = kwargs['thread_id']
76
+
77
+ self.id = int(chat_id)
78
+ self._attr_formatters = CHAT_ATTR_FORMATTERS
79
+
80
+ # Call parent constructors
81
+ CoreBaseModel.__init__(self, id=self.id, **kwargs)
82
+ CoreClientObject.__init__(self, client)
83
+
84
+ def __str__(self) -> str:
85
+ title = self._data.get('title', '')
86
+ id_ = self.id or ''
87
+ return f'{id_} - {title}'
88
+
89
+ def __eq__(self, other: object) -> bool:
90
+ if not isinstance(other, CoreChat):
91
+ return id(other) == id(self)
92
+ return self.id == other.id
@@ -0,0 +1,65 @@
1
+ """Core Post model without lazy loading."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from pararamio_aio._core.utils.helpers import parse_iso_datetime
8
+
9
+ from .base import CoreBaseModel
10
+
11
+ if TYPE_CHECKING:
12
+ from datetime import datetime
13
+
14
+ from pararamio_aio._core._types import FormatterT, PostMetaT, TextParsedT
15
+
16
+ __all__ = ('CorePost',)
17
+
18
+
19
+ # Attribute formatters for Post
20
+ POST_ATTR_FORMATTERS: FormatterT = {
21
+ 'post_no': lambda data, key: int(data[key]),
22
+ 'time_edited': parse_iso_datetime,
23
+ 'time_created': parse_iso_datetime,
24
+ }
25
+
26
+
27
+ class CorePost(CoreBaseModel):
28
+ """Core Post model with common functionality."""
29
+
30
+ # Post attributes (copied from original)
31
+ _chat: Any # Reference to chat object
32
+ chat_id: int
33
+ event: dict[str, Any] | None
34
+ id: int | None
35
+ is_deleted: bool
36
+ meta: PostMetaT
37
+ post_no: int
38
+ reply_no: int | None
39
+ text: str
40
+ text_parsed: list[TextParsedT]
41
+ time_created: datetime
42
+ time_edited: datetime | None
43
+ user_id: int
44
+ uuid: str | None
45
+ ver: int | None
46
+
47
+ def __init__(self, chat: Any, **kwargs: Any) -> None:
48
+ self._chat = chat
49
+ self._attr_formatters = POST_ATTR_FORMATTERS
50
+
51
+ # Call parent constructor
52
+ CoreBaseModel.__init__(self, **kwargs)
53
+
54
+ @property
55
+ def chat(self) -> Any:
56
+ """Get chat object."""
57
+ return self._chat
58
+
59
+ def __eq__(self, other: object) -> bool:
60
+ if not isinstance(other, CorePost):
61
+ return id(other) == id(self)
62
+ return self.chat_id == other.chat_id and self.post_no == other.post_no
63
+
64
+ def __str__(self) -> str:
65
+ return f'Post {self.post_no} in Chat {self.chat_id}'
@@ -0,0 +1,54 @@
1
+ """Core User model without lazy loading."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from .base import CoreBaseModel, CoreClientObject
8
+
9
+ __all__ = ('CoreUser', 'UserInfoParsedItem')
10
+
11
+
12
+ # Types for User
13
+ class UserInfoParsedItem(dict[str, Any]):
14
+ """Parsed user info item."""
15
+
16
+ type: str
17
+ value: str
18
+
19
+
20
+ class CoreUser(CoreBaseModel, CoreClientObject):
21
+ """Core User model with common functionality."""
22
+
23
+ # User attributes (copied from original)
24
+ id: int
25
+ name: str
26
+ name_trans: str
27
+ info: str
28
+ unique_name: str
29
+ deleted: bool
30
+ active: bool
31
+ time_updated: str
32
+ time_created: str
33
+ is_bot: bool
34
+ alias: None | None
35
+ timezone_offset_minutes: int
36
+ owner_id: None | None
37
+ organizations: list[int]
38
+ info_parsed: list[UserInfoParsedItem]
39
+
40
+ def __init__(self, client: Any, user_id: int, **kwargs: Any) -> None:
41
+ self.id = user_id
42
+ self._attr_formatters = {} # User has no special formatters
43
+
44
+ # Call parent constructors
45
+ CoreBaseModel.__init__(self, id=user_id, **kwargs)
46
+ CoreClientObject.__init__(self, client)
47
+
48
+ def __eq__(self, other: object) -> bool:
49
+ if not isinstance(other, CoreUser):
50
+ return id(other) == id(self)
51
+ return self.id == other.id
52
+
53
+ def __str__(self) -> str:
54
+ return self._data.get('name', '')
@@ -0,0 +1,2 @@
1
+ # This file indicates that this package supports type hints
2
+ # See PEP 561 for more details
@@ -0,0 +1,73 @@
1
+ from .auth_flow import AuthenticationFlow, AuthenticationResult, generate_otp
2
+ from .authentication import authenticate, do_second_step, do_second_step_with_code, get_xsrf_token
3
+ from .captcha import get_captcha_img, verify_captcha
4
+ from .helpers import (
5
+ check_login_opts,
6
+ encode_chat_id,
7
+ encode_digit,
8
+ format_datetime,
9
+ format_or_none,
10
+ get_empty_vars,
11
+ get_formatted_attr_or_load,
12
+ get_utc,
13
+ join_ids,
14
+ lazy_loader,
15
+ parse_datetime,
16
+ parse_iso_datetime,
17
+ unescape_dict,
18
+ )
19
+ from .http_client import (
20
+ HTTPClientConfig,
21
+ RateLimitHandler,
22
+ RequestResult,
23
+ build_url,
24
+ prepare_headers,
25
+ should_retry_request,
26
+ )
27
+ from .requests import (
28
+ api_request,
29
+ bot_request,
30
+ delete_file,
31
+ download_file,
32
+ raw_api_request,
33
+ upload_file,
34
+ xupload_file,
35
+ )
36
+
37
+ __all__ = (
38
+ 'AuthenticationFlow',
39
+ 'AuthenticationResult',
40
+ 'HTTPClientConfig',
41
+ 'RateLimitHandler',
42
+ 'RequestResult',
43
+ 'api_request',
44
+ 'authenticate',
45
+ 'bot_request',
46
+ 'build_url',
47
+ 'check_login_opts',
48
+ 'delete_file',
49
+ 'do_second_step',
50
+ 'do_second_step_with_code',
51
+ 'download_file',
52
+ 'encode_chat_id',
53
+ 'encode_digit',
54
+ 'format_datetime',
55
+ 'format_or_none',
56
+ 'generate_otp',
57
+ 'get_captcha_img',
58
+ 'get_empty_vars',
59
+ 'get_formatted_attr_or_load',
60
+ 'get_utc',
61
+ 'get_xsrf_token',
62
+ 'join_ids',
63
+ 'lazy_loader',
64
+ 'parse_datetime',
65
+ 'parse_iso_datetime',
66
+ 'prepare_headers',
67
+ 'raw_api_request',
68
+ 'should_retry_request',
69
+ 'unescape_dict',
70
+ 'upload_file',
71
+ 'verify_captcha',
72
+ 'xupload_file',
73
+ )