pararamio-aio 2.1.1__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 +78 -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/cookie_manager.py +15 -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 +32 -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-2.1.1.dist-info/METADATA +269 -0
- pararamio_aio-2.1.1.dist-info/RECORD +57 -0
- pararamio_aio-2.1.1.dist-info/WHEEL +5 -0
- pararamio_aio-2.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
"""Async Python client library for pararam.io platform."""
|
2
|
+
|
3
|
+
from pararamio_aio._core.constants import VERSION
|
4
|
+
|
5
|
+
from ._types import (
|
6
|
+
BotProfileT,
|
7
|
+
PostMetaFileT,
|
8
|
+
PostMetaUserT,
|
9
|
+
ProfileTypeT,
|
10
|
+
QuoteRangeT,
|
11
|
+
TextParsedT,
|
12
|
+
)
|
13
|
+
from .client import AsyncPararamio
|
14
|
+
from .cookie_manager import (
|
15
|
+
AsyncCookieManager,
|
16
|
+
AsyncFileCookieManager,
|
17
|
+
AsyncInMemoryCookieManager,
|
18
|
+
AsyncRedisCookieManager,
|
19
|
+
)
|
20
|
+
from .exceptions import (
|
21
|
+
PararamioAuthenticationException,
|
22
|
+
PararamioException,
|
23
|
+
PararamioHTTPRequestException,
|
24
|
+
PararamioValidationException,
|
25
|
+
)
|
26
|
+
from .models import (
|
27
|
+
Activity,
|
28
|
+
ActivityAction,
|
29
|
+
AsyncPararamioBot,
|
30
|
+
Attachment,
|
31
|
+
Chat,
|
32
|
+
DeferredPost,
|
33
|
+
File,
|
34
|
+
Group,
|
35
|
+
Poll,
|
36
|
+
PollOption,
|
37
|
+
Post,
|
38
|
+
Team,
|
39
|
+
TeamMember,
|
40
|
+
TeamMemberStatus,
|
41
|
+
User,
|
42
|
+
UserSearchResult,
|
43
|
+
)
|
44
|
+
|
45
|
+
__version__ = VERSION
|
46
|
+
__all__ = (
|
47
|
+
"Activity",
|
48
|
+
"ActivityAction",
|
49
|
+
"Attachment",
|
50
|
+
"AsyncCookieManager",
|
51
|
+
"AsyncFileCookieManager",
|
52
|
+
"AsyncInMemoryCookieManager",
|
53
|
+
"AsyncPararamio",
|
54
|
+
"AsyncPararamioBot",
|
55
|
+
"AsyncRedisCookieManager",
|
56
|
+
"BotProfileT",
|
57
|
+
"Chat",
|
58
|
+
"DeferredPost",
|
59
|
+
"File",
|
60
|
+
"Group",
|
61
|
+
"PararamioException",
|
62
|
+
"PararamioAuthenticationException",
|
63
|
+
"PararamioHTTPRequestException",
|
64
|
+
"PararamioValidationException",
|
65
|
+
"Poll",
|
66
|
+
"PollOption",
|
67
|
+
"Post",
|
68
|
+
"PostMetaFileT",
|
69
|
+
"PostMetaUserT",
|
70
|
+
"ProfileTypeT",
|
71
|
+
"QuoteRangeT",
|
72
|
+
"Team",
|
73
|
+
"TeamMember",
|
74
|
+
"TeamMemberStatus",
|
75
|
+
"TextParsedT",
|
76
|
+
"User",
|
77
|
+
"UserSearchResult",
|
78
|
+
)
|
@@ -0,0 +1,125 @@
|
|
1
|
+
"""Core components for pararamio packages."""
|
2
|
+
|
3
|
+
from ._types import *
|
4
|
+
from .base import *
|
5
|
+
from .client_protocol import *
|
6
|
+
from .constants import *
|
7
|
+
from .constants.endpoints import *
|
8
|
+
from .cookie_decorator import CookieManagerMixin, auth_required, with_auth_retry
|
9
|
+
from .cookie_manager import (
|
10
|
+
AsyncCookieManager,
|
11
|
+
AsyncFileCookieManager,
|
12
|
+
AsyncInMemoryCookieManager,
|
13
|
+
AsyncRedisCookieManager,
|
14
|
+
CookieManager,
|
15
|
+
FileCookieManager,
|
16
|
+
InMemoryCookieManager,
|
17
|
+
RedisCookieManager,
|
18
|
+
)
|
19
|
+
from .endpoints import *
|
20
|
+
from .exceptions import *
|
21
|
+
from .exceptions.auth import *
|
22
|
+
from .models import *
|
23
|
+
from .utils.auth_flow import AuthenticationFlow, AuthenticationResult, generate_otp
|
24
|
+
from .utils.http_client import (
|
25
|
+
HTTPClientConfig,
|
26
|
+
RateLimitHandler,
|
27
|
+
RequestResult,
|
28
|
+
build_url,
|
29
|
+
prepare_headers,
|
30
|
+
should_retry_request,
|
31
|
+
)
|
32
|
+
from .validators import *
|
33
|
+
|
34
|
+
# Version
|
35
|
+
__version__ = VERSION
|
36
|
+
|
37
|
+
__all__ = [
|
38
|
+
'AUTH_ENDPOINTS',
|
39
|
+
'AUTH_INIT_URL',
|
40
|
+
'AUTH_LOGIN_URL',
|
41
|
+
'AUTH_NEXT_URL',
|
42
|
+
'AUTH_TOTP_URL',
|
43
|
+
'CHAT_ENDPOINTS',
|
44
|
+
'FILE_ENDPOINTS',
|
45
|
+
'POSTS_LIMIT',
|
46
|
+
'POST_ENDPOINTS',
|
47
|
+
# Endpoints
|
48
|
+
'USER_ENDPOINTS',
|
49
|
+
# Constants
|
50
|
+
'XSRF_HEADER_NAME',
|
51
|
+
'AsyncClientProtocol',
|
52
|
+
'AsyncCookieManager',
|
53
|
+
'AsyncFileCookieManager',
|
54
|
+
'AsyncInMemoryCookieManager',
|
55
|
+
'AsyncRedisCookieManager',
|
56
|
+
# Authentication exceptions
|
57
|
+
'AuthenticationException',
|
58
|
+
'AuthenticationFlow',
|
59
|
+
'AuthenticationResult',
|
60
|
+
'BaseClientObject',
|
61
|
+
'BaseEvent',
|
62
|
+
'BaseLoadedAttrMetaClass',
|
63
|
+
'BaseLoadedAttrPararamObject',
|
64
|
+
# Base classes
|
65
|
+
'BasePararamObject',
|
66
|
+
'CaptchaRequiredException',
|
67
|
+
# Client protocols
|
68
|
+
'ClientProtocol',
|
69
|
+
'CookieJarT',
|
70
|
+
# Cookie management
|
71
|
+
'CookieManager',
|
72
|
+
'CookieManagerMixin',
|
73
|
+
# Core models
|
74
|
+
'CoreBaseModel',
|
75
|
+
'CoreChat',
|
76
|
+
'CoreClientObject',
|
77
|
+
'CorePost',
|
78
|
+
'CoreUser',
|
79
|
+
'FileCookieManager',
|
80
|
+
'FormatterT',
|
81
|
+
# HTTP client utilities
|
82
|
+
'HTTPClientConfig',
|
83
|
+
'HeaderLikeT',
|
84
|
+
'InMemoryCookieManager',
|
85
|
+
'InvalidCredentialsException',
|
86
|
+
'MetaReplyT',
|
87
|
+
'PararamNotFound',
|
88
|
+
# Exceptions
|
89
|
+
'PararamioException',
|
90
|
+
'PararamioHTTPRequestException',
|
91
|
+
'PararamioLimitExceededException',
|
92
|
+
'PararamioMethodNotAllowed',
|
93
|
+
'PararamioRequestException',
|
94
|
+
'PararamioValidationException',
|
95
|
+
'PostMention',
|
96
|
+
'PostMetaFileT',
|
97
|
+
'PostMetaT',
|
98
|
+
'PostMetaThreadT',
|
99
|
+
'PostMetaUserT',
|
100
|
+
# Types
|
101
|
+
'ProfileTypeT',
|
102
|
+
'QuoteRangeT',
|
103
|
+
'RateLimitException',
|
104
|
+
'RateLimitHandler',
|
105
|
+
'RedisCookieManager',
|
106
|
+
'RequestResult',
|
107
|
+
'SecondStepFnT',
|
108
|
+
'SessionExpiredException',
|
109
|
+
'TextParsedT',
|
110
|
+
'TwoFactorFailedException',
|
111
|
+
'TwoFactorRequiredException',
|
112
|
+
'UserInfoParsedItem',
|
113
|
+
'XSRFTokenException',
|
114
|
+
'auth_required',
|
115
|
+
'build_url',
|
116
|
+
# Authentication utilities
|
117
|
+
'generate_otp',
|
118
|
+
'prepare_headers',
|
119
|
+
'should_retry_request',
|
120
|
+
'validate_filename',
|
121
|
+
'validate_ids_list',
|
122
|
+
# Validators
|
123
|
+
'validate_post_load_range',
|
124
|
+
'with_auth_retry',
|
125
|
+
]
|
@@ -0,0 +1,120 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from http.cookiejar import CookieJar
|
4
|
+
from typing import Any, Callable, TypedDict, TypeVar, Union
|
5
|
+
|
6
|
+
try:
|
7
|
+
from typing import Literal, NotRequired
|
8
|
+
except ImportError:
|
9
|
+
from typing_extensions import Literal, NotRequired
|
10
|
+
|
11
|
+
|
12
|
+
class ProfileTypeT(TypedDict):
|
13
|
+
unique_name: str
|
14
|
+
id: int
|
15
|
+
info: str | None
|
16
|
+
find_strict: bool
|
17
|
+
name: str
|
18
|
+
is_google: bool
|
19
|
+
two_step_enabled: bool
|
20
|
+
has_password: bool
|
21
|
+
phoneconfirmed: bool
|
22
|
+
email: str
|
23
|
+
phonenumber: NotRequired[str | None]
|
24
|
+
|
25
|
+
|
26
|
+
# noinspection PyUnresolvedReferences
|
27
|
+
class TextParsedT(TypedDict):
|
28
|
+
type: str
|
29
|
+
value: str
|
30
|
+
name: NotRequired[str]
|
31
|
+
id: NotRequired[int]
|
32
|
+
|
33
|
+
|
34
|
+
# noinspection PyUnresolvedReferences
|
35
|
+
class PostMetaUserT(TypedDict):
|
36
|
+
id: int
|
37
|
+
name: str
|
38
|
+
unique_name: str
|
39
|
+
is_bot: NotRequired[bool]
|
40
|
+
|
41
|
+
|
42
|
+
class BotProfileT(TypedDict):
|
43
|
+
id: int
|
44
|
+
active: bool
|
45
|
+
deleted: bool
|
46
|
+
email: str | None
|
47
|
+
find_strict: bool
|
48
|
+
has_password: bool
|
49
|
+
info: str | None
|
50
|
+
info_parsed: list
|
51
|
+
info_chat: int
|
52
|
+
is_bot: bool
|
53
|
+
is_google: bool
|
54
|
+
name: str
|
55
|
+
name_trans: str
|
56
|
+
unique_name: str
|
57
|
+
organizations: list
|
58
|
+
phoneconfirmed: bool
|
59
|
+
phonenumber: str | None
|
60
|
+
time_created: str
|
61
|
+
time_updated: str
|
62
|
+
two_step_enabled: bool
|
63
|
+
|
64
|
+
|
65
|
+
class PostMetaFileT(TypedDict):
|
66
|
+
name: str
|
67
|
+
guid: str
|
68
|
+
size: int
|
69
|
+
mime_type: str
|
70
|
+
origin: tuple[int, int]
|
71
|
+
|
72
|
+
|
73
|
+
class MetaReplyT(TypedDict):
|
74
|
+
text: str
|
75
|
+
user_id: int
|
76
|
+
user_name: str
|
77
|
+
in_thread_no: int
|
78
|
+
|
79
|
+
|
80
|
+
class PostMetaThreadT(TypedDict):
|
81
|
+
title: str
|
82
|
+
|
83
|
+
|
84
|
+
class PostMetaT(TypedDict):
|
85
|
+
user: PostMetaUserT
|
86
|
+
thread: PostMetaThreadT
|
87
|
+
file: PostMetaFileT
|
88
|
+
reply: MetaReplyT
|
89
|
+
attachments: list[str]
|
90
|
+
|
91
|
+
|
92
|
+
class PostMention(TypedDict):
|
93
|
+
id: int
|
94
|
+
name: str
|
95
|
+
value: str
|
96
|
+
|
97
|
+
|
98
|
+
class BaseEvent(TypedDict):
|
99
|
+
type: Literal[
|
100
|
+
'GROUP_LEAVED',
|
101
|
+
'GROUP_DELETED',
|
102
|
+
'ORG_MEMBERS',
|
103
|
+
'GROUP_CREATED',
|
104
|
+
'GROUP_UPDATED',
|
105
|
+
'ENTER_TO_THREAD',
|
106
|
+
'CALL',
|
107
|
+
'POST_PINNED',
|
108
|
+
'NEW_THREAD',
|
109
|
+
'EDIT_THREAD',
|
110
|
+
'ENTER_TO_THREAD',
|
111
|
+
'CHAT_TITLE',
|
112
|
+
]
|
113
|
+
data: dict[str, Any]
|
114
|
+
|
115
|
+
|
116
|
+
FormatterT = dict[str, Callable[[dict[str, Any], str], Any]]
|
117
|
+
CookieJarT = TypeVar('CookieJarT', bound=CookieJar)
|
118
|
+
QuoteRangeT = dict[str, Union[str, int]]
|
119
|
+
HeaderLikeT = dict[str, str]
|
120
|
+
SecondStepFnT = Callable[[CookieJar, dict[str, str], str], tuple[bool, dict[str, str]]]
|
@@ -0,0 +1,143 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import contextlib
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable
|
5
|
+
|
6
|
+
from .utils.helpers import get_formatted_attr_or_load
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from ._types import FormatterT
|
10
|
+
from .client_protocol import AsyncClientProtocol, ClientProtocol
|
11
|
+
__all__ = (
|
12
|
+
'BaseClientObject',
|
13
|
+
'BaseLoadedAttrMetaClass',
|
14
|
+
'BaseLoadedAttrPararamObject',
|
15
|
+
'BasePararamObject',
|
16
|
+
)
|
17
|
+
|
18
|
+
DEFAULT_ATTRIBUTES = {
|
19
|
+
'_attr_formatters': {},
|
20
|
+
'load': lambda _: None,
|
21
|
+
'_load_on_key_error': False,
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
# noinspection PyIncorrectDocstring
|
26
|
+
def get_loaded_formatted_attr(self, key: str) -> Any:
|
27
|
+
"""
|
28
|
+
Fetches a formatted attribute from the object's internal storage.
|
29
|
+
|
30
|
+
If the attribute is not already loaded and '_load_on_key_error' is True,
|
31
|
+
the method attempts to load the attribute first.
|
32
|
+
|
33
|
+
Parameters:
|
34
|
+
key (str): The key corresponding to the desired attribute.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
Any: The formatted attribute value.
|
38
|
+
"""
|
39
|
+
if key in DEFAULT_ATTRIBUTES:
|
40
|
+
return DEFAULT_ATTRIBUTES[key]
|
41
|
+
return get_formatted_attr_or_load(
|
42
|
+
self,
|
43
|
+
key,
|
44
|
+
getattr(self, '_attr_formatters', None), # pylint: disable=protected-access
|
45
|
+
self.load if self._load_on_key_error else None, # pylint: disable=protected-access
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
# noinspection PyIncorrectDocstring
|
50
|
+
def get_formatted_attr(self, key: str) -> Any:
|
51
|
+
"""
|
52
|
+
|
53
|
+
Retrieves the formatted attribute value for the specified key.
|
54
|
+
|
55
|
+
Parameters:
|
56
|
+
key (str): The attribute key whose formatted value is to be retrieved.
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
Any: The formatted attribute value associated with the given key,
|
60
|
+
or the original attribute value if no formatter is enabled.
|
61
|
+
"""
|
62
|
+
if key in DEFAULT_ATTRIBUTES:
|
63
|
+
return DEFAULT_ATTRIBUTES[key]
|
64
|
+
return get_formatted_attr_or_load(self, key, getattr(self, '_attr_formatters', None))
|
65
|
+
|
66
|
+
|
67
|
+
def get_formatted_attr_fn(
|
68
|
+
cls: BaseLoadedAttrMetaClass,
|
69
|
+
can_be_loaded: bool = False,
|
70
|
+
) -> Any:
|
71
|
+
"""
|
72
|
+
|
73
|
+
Determines the appropriate function to get an attribute from a class,
|
74
|
+
potentially loading it if required.
|
75
|
+
|
76
|
+
Returns a function that either retrieves a formatted attribute or
|
77
|
+
loads it if 'load' and '_load_on_key_error' attributes are present in the class.
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
get_formatted_attr_or_load: If the class has both
|
81
|
+
'load' and '_load_on_key_error' attributes.
|
82
|
+
get_formatted_attr: Otherwise.
|
83
|
+
"""
|
84
|
+
_get_formatted_attr_fn = getattr(cls, '_get_formatted_attr', None)
|
85
|
+
if _get_formatted_attr_fn:
|
86
|
+
return _get_formatted_attr_fn
|
87
|
+
if can_be_loaded:
|
88
|
+
return get_loaded_formatted_attr
|
89
|
+
return get_formatted_attr
|
90
|
+
|
91
|
+
|
92
|
+
class BaseLoadedAttrMetaClass(type):
|
93
|
+
"""Metaclass for classes that have loaded attributes.
|
94
|
+
Adds a custom __getattr__ method to the class to fetch or load formatted attributes.
|
95
|
+
|
96
|
+
this monkeypatching needs to be done to linter-check
|
97
|
+
class attributes so that undeclared attributes throw an error
|
98
|
+
"""
|
99
|
+
|
100
|
+
def __new__(mcs, name, bases, dct): # pylint: disable=bad-mcs-classmethod-argument
|
101
|
+
"""
|
102
|
+
|
103
|
+
Creates a new instance of the class, sets up custom attribute access.
|
104
|
+
|
105
|
+
Parameters:
|
106
|
+
mcs: The class being instantiated.
|
107
|
+
name: The name of the class.
|
108
|
+
bases: The base classes.
|
109
|
+
dct: The class attributes.
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
The new class instance with a custom __getattr__ method.
|
113
|
+
"""
|
114
|
+
new_cls = super().__new__(mcs, name, bases, dct)
|
115
|
+
with contextlib.suppress(NameError):
|
116
|
+
new_cls.__getattr__ = get_formatted_attr_fn(
|
117
|
+
new_cls, BaseLoadedAttrPararamObject in bases
|
118
|
+
)
|
119
|
+
return new_cls
|
120
|
+
|
121
|
+
|
122
|
+
class BasePararamObject(metaclass=BaseLoadedAttrMetaClass):
|
123
|
+
_data: dict[str, Any]
|
124
|
+
_attr_formatters: FormatterT
|
125
|
+
_get_formatted_attr: Callable[[str], Any]
|
126
|
+
|
127
|
+
|
128
|
+
class BaseLoadedAttrPararamObject(BasePararamObject):
|
129
|
+
_load_on_key_error: bool
|
130
|
+
|
131
|
+
def load(self) -> Any:
|
132
|
+
"""
|
133
|
+
Load the object from the server.
|
134
|
+
"""
|
135
|
+
raise NotImplementedError
|
136
|
+
|
137
|
+
|
138
|
+
class BaseClientObject:
|
139
|
+
_client: ClientProtocol | AsyncClientProtocol
|
140
|
+
|
141
|
+
@property
|
142
|
+
def client(self) -> ClientProtocol | AsyncClientProtocol:
|
143
|
+
return self._client
|
@@ -0,0 +1,90 @@
|
|
1
|
+
"""Abstract client protocol for both sync and async implementations."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
from typing import Any, BinaryIO
|
7
|
+
|
8
|
+
__all__ = ['AsyncClientProtocol', 'ClientProtocol']
|
9
|
+
|
10
|
+
|
11
|
+
class ClientProtocol(ABC):
|
12
|
+
"""Abstract base class defining the interface for both sync and async clients."""
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
def api_get(self, url: str, **kwargs: Any) -> dict[str, Any]:
|
16
|
+
"""Make a GET request to the API."""
|
17
|
+
|
18
|
+
@abstractmethod
|
19
|
+
def api_post(
|
20
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
21
|
+
) -> dict[str, Any]:
|
22
|
+
"""Make a POST request to the API."""
|
23
|
+
|
24
|
+
@abstractmethod
|
25
|
+
def api_put(
|
26
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
27
|
+
) -> dict[str, Any]:
|
28
|
+
"""Make a PUT request to the API."""
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
def api_delete(
|
32
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
33
|
+
) -> dict[str, Any]:
|
34
|
+
"""Make a DELETE request to the API."""
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
def upload_file(
|
38
|
+
self,
|
39
|
+
file: BinaryIO | bytes,
|
40
|
+
chat_id: int,
|
41
|
+
filename: str | None = None,
|
42
|
+
**kwargs: Any,
|
43
|
+
) -> dict[str, Any]:
|
44
|
+
"""Upload a file."""
|
45
|
+
|
46
|
+
@property
|
47
|
+
@abstractmethod
|
48
|
+
def authenticated(self) -> bool:
|
49
|
+
"""Check if client is authenticated."""
|
50
|
+
|
51
|
+
|
52
|
+
class AsyncClientProtocol(ABC):
|
53
|
+
"""Abstract base class for async client implementations."""
|
54
|
+
|
55
|
+
@abstractmethod
|
56
|
+
async def api_get(self, url: str, **kwargs: Any) -> dict[str, Any]:
|
57
|
+
"""Make an async GET request to the API."""
|
58
|
+
|
59
|
+
@abstractmethod
|
60
|
+
async def api_post(
|
61
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
62
|
+
) -> dict[str, Any]:
|
63
|
+
"""Make an async POST request to the API."""
|
64
|
+
|
65
|
+
@abstractmethod
|
66
|
+
async def api_put(
|
67
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
68
|
+
) -> dict[str, Any]:
|
69
|
+
"""Make an async PUT request to the API."""
|
70
|
+
|
71
|
+
@abstractmethod
|
72
|
+
async def api_delete(
|
73
|
+
self, url: str, data: dict[str, Any] | None = None, **kwargs: Any
|
74
|
+
) -> dict[str, Any]:
|
75
|
+
"""Make an async DELETE request to the API."""
|
76
|
+
|
77
|
+
@abstractmethod
|
78
|
+
async def upload_file(
|
79
|
+
self,
|
80
|
+
file: BinaryIO | bytes,
|
81
|
+
chat_id: int,
|
82
|
+
filename: str | None = None,
|
83
|
+
**kwargs: Any,
|
84
|
+
) -> dict[str, Any]:
|
85
|
+
"""Upload a file asynchronously."""
|
86
|
+
|
87
|
+
@property
|
88
|
+
@abstractmethod
|
89
|
+
def authenticated(self) -> bool:
|
90
|
+
"""Check if client is authenticated."""
|
@@ -0,0 +1,9 @@
|
|
1
|
+
VERSION = '2.1.1'
|
2
|
+
BASE_API_URL = 'https://api.pararam.io'
|
3
|
+
FILE_UPLOAD_URL = 'https://file.pararam.io'
|
4
|
+
DEFAULT_CONNECTION_NAME = '_default'
|
5
|
+
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
|
6
|
+
XSRF_HEADER_NAME: str = 'X-Xsrftoken'
|
7
|
+
UPLOAD_TIMEOUT = 600
|
8
|
+
REQUEST_TIMEOUT = 30
|
9
|
+
POSTS_LIMIT = 100
|
@@ -0,0 +1,84 @@
|
|
1
|
+
"""API endpoint constants."""
|
2
|
+
|
3
|
+
__all__ = (
|
4
|
+
# Auth endpoints
|
5
|
+
'AUTH_INIT_URL',
|
6
|
+
'AUTH_LOGIN_URL',
|
7
|
+
'AUTH_NEXT_URL',
|
8
|
+
'AUTH_TOTP_URL',
|
9
|
+
'CHAT_BY_ID_URL',
|
10
|
+
# Chat endpoints
|
11
|
+
'CHAT_ENDPOINTS',
|
12
|
+
'CHAT_LIST_URL',
|
13
|
+
'CHAT_POSTS_URL',
|
14
|
+
'CHAT_SEND_URL',
|
15
|
+
'FILE_DELETE_URL',
|
16
|
+
'FILE_DOWNLOAD_URL',
|
17
|
+
# File endpoints
|
18
|
+
'FILE_UPLOAD_URL',
|
19
|
+
'GROUP_BY_ID_URL',
|
20
|
+
'GROUP_JOIN_URL',
|
21
|
+
'GROUP_LEAVE_URL',
|
22
|
+
# Group endpoints
|
23
|
+
'GROUP_LIST_URL',
|
24
|
+
'POST_BY_ID_URL',
|
25
|
+
'POST_CREATE_URL',
|
26
|
+
'POST_DELETE_URL',
|
27
|
+
# Post endpoints
|
28
|
+
'POST_ENDPOINTS',
|
29
|
+
'POST_UPDATE_URL',
|
30
|
+
'USER_BY_ID_URL',
|
31
|
+
# User endpoints
|
32
|
+
'USER_PROFILE_URL',
|
33
|
+
'USER_SEARCH_URL',
|
34
|
+
)
|
35
|
+
|
36
|
+
# Authentication endpoints
|
37
|
+
AUTH_INIT_URL = '/auth/init'
|
38
|
+
AUTH_LOGIN_URL = '/auth/login/password'
|
39
|
+
AUTH_TOTP_URL = '/auth/totp'
|
40
|
+
AUTH_NEXT_URL = '/auth/next'
|
41
|
+
|
42
|
+
# User endpoints
|
43
|
+
USER_PROFILE_URL = '/user/profile'
|
44
|
+
USER_SEARCH_URL = '/user/search'
|
45
|
+
USER_BY_ID_URL = '/user/{user_id}'
|
46
|
+
|
47
|
+
# Chat endpoints
|
48
|
+
CHAT_LIST_URL = '/chat/list'
|
49
|
+
CHAT_BY_ID_URL = '/chat/{chat_id}'
|
50
|
+
CHAT_POSTS_URL = '/chat/{chat_id}/posts'
|
51
|
+
CHAT_SEND_URL = '/chat/{chat_id}/send'
|
52
|
+
|
53
|
+
# Grouped chat endpoints for convenience
|
54
|
+
CHAT_ENDPOINTS = {
|
55
|
+
'list': CHAT_LIST_URL,
|
56
|
+
'by_id': CHAT_BY_ID_URL,
|
57
|
+
'posts': CHAT_POSTS_URL,
|
58
|
+
'send': CHAT_SEND_URL,
|
59
|
+
}
|
60
|
+
|
61
|
+
# Post endpoints
|
62
|
+
POST_BY_ID_URL = '/post/{post_id}'
|
63
|
+
POST_CREATE_URL = '/post/create'
|
64
|
+
POST_UPDATE_URL = '/post/{post_id}/update'
|
65
|
+
POST_DELETE_URL = '/post/{post_id}/delete'
|
66
|
+
|
67
|
+
# Grouped post endpoints for convenience
|
68
|
+
POST_ENDPOINTS = {
|
69
|
+
'by_id': POST_BY_ID_URL,
|
70
|
+
'create': POST_CREATE_URL,
|
71
|
+
'update': POST_UPDATE_URL,
|
72
|
+
'delete': POST_DELETE_URL,
|
73
|
+
}
|
74
|
+
|
75
|
+
# Group endpoints
|
76
|
+
GROUP_LIST_URL = '/group/list'
|
77
|
+
GROUP_BY_ID_URL = '/group/{group_id}'
|
78
|
+
GROUP_JOIN_URL = '/group/{group_id}/join'
|
79
|
+
GROUP_LEAVE_URL = '/group/{group_id}/leave'
|
80
|
+
|
81
|
+
# File endpoints
|
82
|
+
FILE_UPLOAD_URL = '/upload/{perm}'
|
83
|
+
FILE_DOWNLOAD_URL = '/download/{guid}/{filename}'
|
84
|
+
FILE_DELETE_URL = '/delete/{guid}'
|