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.
- pararamio_aio/__init__.py +26 -0
- pararamio_aio/_core/__init__.py +125 -0
- pararamio_aio/_core/_types.py +120 -0
- pararamio_aio/_core/base.py +143 -0
- pararamio_aio/_core/client_protocol.py +90 -0
- pararamio_aio/_core/constants/__init__.py +7 -0
- pararamio_aio/_core/constants/base.py +9 -0
- pararamio_aio/_core/constants/endpoints.py +84 -0
- pararamio_aio/_core/cookie_decorator.py +208 -0
- pararamio_aio/_core/cookie_manager.py +1222 -0
- pararamio_aio/_core/endpoints.py +67 -0
- pararamio_aio/_core/exceptions/__init__.py +6 -0
- pararamio_aio/_core/exceptions/auth.py +91 -0
- pararamio_aio/_core/exceptions/base.py +124 -0
- pararamio_aio/_core/models/__init__.py +17 -0
- pararamio_aio/_core/models/base.py +66 -0
- pararamio_aio/_core/models/chat.py +92 -0
- pararamio_aio/_core/models/post.py +65 -0
- pararamio_aio/_core/models/user.py +54 -0
- pararamio_aio/_core/py.typed +2 -0
- pararamio_aio/_core/utils/__init__.py +73 -0
- pararamio_aio/_core/utils/async_requests.py +417 -0
- pararamio_aio/_core/utils/auth_flow.py +202 -0
- pararamio_aio/_core/utils/authentication.py +235 -0
- pararamio_aio/_core/utils/captcha.py +92 -0
- pararamio_aio/_core/utils/helpers.py +336 -0
- pararamio_aio/_core/utils/http_client.py +199 -0
- pararamio_aio/_core/utils/requests.py +424 -0
- pararamio_aio/_core/validators.py +78 -0
- pararamio_aio/_types.py +29 -0
- pararamio_aio/client.py +989 -0
- pararamio_aio/constants/__init__.py +16 -0
- pararamio_aio/exceptions/__init__.py +31 -0
- pararamio_aio/exceptions/base.py +1 -0
- pararamio_aio/file_operations.py +232 -0
- pararamio_aio/models/__init__.py +31 -0
- pararamio_aio/models/activity.py +127 -0
- pararamio_aio/models/attachment.py +141 -0
- pararamio_aio/models/base.py +83 -0
- pararamio_aio/models/bot.py +274 -0
- pararamio_aio/models/chat.py +722 -0
- pararamio_aio/models/deferred_post.py +174 -0
- pararamio_aio/models/file.py +103 -0
- pararamio_aio/models/group.py +361 -0
- pararamio_aio/models/poll.py +275 -0
- pararamio_aio/models/post.py +643 -0
- pararamio_aio/models/team.py +403 -0
- pararamio_aio/models/user.py +239 -0
- pararamio_aio/py.typed +2 -0
- pararamio_aio/utils/__init__.py +18 -0
- pararamio_aio/utils/authentication.py +383 -0
- pararamio_aio/utils/requests.py +75 -0
- pararamio_aio-3.0.0.dist-info/METADATA +269 -0
- pararamio_aio-3.0.0.dist-info/RECORD +56 -0
- pararamio_aio-3.0.0.dist-info/WHEEL +5 -0
- 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,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,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
|
+
)
|