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.
Files changed (57) hide show
  1. pararamio_aio/__init__.py +78 -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/cookie_manager.py +15 -0
  34. pararamio_aio/exceptions/__init__.py +31 -0
  35. pararamio_aio/exceptions/base.py +1 -0
  36. pararamio_aio/file_operations.py +232 -0
  37. pararamio_aio/models/__init__.py +32 -0
  38. pararamio_aio/models/activity.py +127 -0
  39. pararamio_aio/models/attachment.py +141 -0
  40. pararamio_aio/models/base.py +83 -0
  41. pararamio_aio/models/bot.py +274 -0
  42. pararamio_aio/models/chat.py +722 -0
  43. pararamio_aio/models/deferred_post.py +174 -0
  44. pararamio_aio/models/file.py +103 -0
  45. pararamio_aio/models/group.py +361 -0
  46. pararamio_aio/models/poll.py +275 -0
  47. pararamio_aio/models/post.py +643 -0
  48. pararamio_aio/models/team.py +403 -0
  49. pararamio_aio/models/user.py +239 -0
  50. pararamio_aio/py.typed +2 -0
  51. pararamio_aio/utils/__init__.py +18 -0
  52. pararamio_aio/utils/authentication.py +383 -0
  53. pararamio_aio/utils/requests.py +75 -0
  54. pararamio_aio-2.1.1.dist-info/METADATA +269 -0
  55. pararamio_aio-2.1.1.dist-info/RECORD +57 -0
  56. pararamio_aio-2.1.1.dist-info/WHEEL +5 -0
  57. 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,7 @@
1
+ """Constants module."""
2
+
3
+ # Export from base constants
4
+ from .base import *
5
+
6
+ # Export from endpoints
7
+ from .endpoints import *
@@ -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}'