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,208 @@
1
+ """Decorator for automatic authentication error handling with cookie manager."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ import inspect
7
+ import logging
8
+ from typing import Any, Callable, TypeVar
9
+
10
+ from .cookie_manager import (
11
+ AsyncCookieManager,
12
+ AsyncFileCookieManager,
13
+ AsyncRedisCookieManager,
14
+ CookieManager,
15
+ FileCookieManager,
16
+ RedisCookieManager,
17
+ )
18
+
19
+ log = logging.getLogger(__name__)
20
+
21
+ F = TypeVar('F', bound=Callable[..., Any])
22
+
23
+
24
+ async def _ensure_cookies_async(obj: Any, cookie_manager_attr: str) -> None:
25
+ """Ensure cookies are loaded and refreshed for async operations."""
26
+ cookie_manager = getattr(obj, cookie_manager_attr, None)
27
+ if isinstance(cookie_manager, AsyncCookieManager):
28
+ # Ensure cookies are loaded
29
+ if not cookie_manager.has_cookies():
30
+ await cookie_manager.load_cookies()
31
+ # Check version and refresh if needed
32
+ await cookie_manager.refresh_if_needed()
33
+
34
+
35
+ def _ensure_cookies_sync(obj: Any, cookie_manager_attr: str) -> None:
36
+ """Ensure cookies are loaded and refreshed for sync operations."""
37
+ cookie_manager = getattr(obj, cookie_manager_attr, None)
38
+ if isinstance(cookie_manager, CookieManager):
39
+ # Ensure cookies are loaded
40
+ if not cookie_manager.has_cookies():
41
+ cookie_manager.load_cookies()
42
+ # Check version and refresh if needed
43
+ cookie_manager.refresh_if_needed()
44
+
45
+
46
+ # List of error indicators for authentication failures
47
+ AUTH_ERROR_INDICATORS = ['401', 'unauthorized', 'authentication', 'auth failed']
48
+
49
+
50
+ def is_auth_error(error: Exception) -> bool:
51
+ """Check if an exception is an authentication error."""
52
+ error_str = str(error).lower()
53
+ return any(indicator in error_str for indicator in AUTH_ERROR_INDICATORS)
54
+
55
+
56
+ def with_auth_retry(cookie_manager_attr: str = 'cookie_manager'):
57
+ """Decorator to automatically handle authentication errors with cookie manager.
58
+
59
+ Args:
60
+ cookie_manager_attr: Name of the attribute containing the CookieManager instance
61
+
62
+ Example:
63
+ class MyClient:
64
+ def __init__(self):
65
+ self.cookie_manager = FileCookieManager('/path/to/cookies')
66
+
67
+ @with_auth_retry()
68
+ def make_api_call(self):
69
+ # This will automatically retry with cookie refresh on auth errors
70
+ return self._do_api_request()
71
+ """
72
+
73
+ def decorator(func: F) -> F:
74
+ if inspect.iscoroutinefunction(func):
75
+ # Async version
76
+ @functools.wraps(func)
77
+ async def async_wrapper(self, *args, **kwargs):
78
+ cookie_manager = getattr(self, cookie_manager_attr, None)
79
+ if not isinstance(cookie_manager, (CookieManager, AsyncCookieManager)):
80
+ # No cookie manager, just call the function
81
+ return await func(self, *args, **kwargs)
82
+
83
+ try:
84
+ return await func(self, *args, **kwargs)
85
+ except Exception as e:
86
+ # Check if this is an authentication error
87
+ if is_auth_error(e):
88
+ log.info('Authentication error in %s: %s', func.__name__, e)
89
+
90
+ # Create retry function
91
+ async def retry():
92
+ return await func(self, *args, **kwargs)
93
+
94
+ # Use cookie manager to handle the error
95
+ return await cookie_manager.handle_auth_error(retry)
96
+ # Not an auth error, re-raise
97
+ raise
98
+
99
+ return async_wrapper
100
+
101
+ # Sync version
102
+ @functools.wraps(func)
103
+ def sync_wrapper(self, *args, **kwargs):
104
+ cookie_manager = getattr(self, cookie_manager_attr, None)
105
+ if not isinstance(cookie_manager, CookieManager):
106
+ # No cookie manager, just call the function
107
+ return func(self, *args, **kwargs)
108
+
109
+ try:
110
+ return func(self, *args, **kwargs)
111
+ except Exception as e:
112
+ # Check if this is an authentication error
113
+ if is_auth_error(e):
114
+ log.info('Authentication error in %s: %s', func.__name__, e)
115
+
116
+ # Create retry function
117
+ def retry():
118
+ return func(self, *args, **kwargs)
119
+
120
+ # Use cookie manager to handle the error
121
+ return cookie_manager.handle_auth_error(retry)
122
+ # Not an auth error, re-raise
123
+ raise
124
+
125
+ return sync_wrapper
126
+
127
+ return decorator
128
+
129
+
130
+ def auth_required(cookie_manager_attr: str = 'cookie_manager'):
131
+ """Decorator that ensures cookies are loaded before method execution.
132
+
133
+ Args:
134
+ cookie_manager_attr: Name of the attribute containing the CookieManager instance
135
+
136
+ Example:
137
+ @auth_required()
138
+ def get_profile(self):
139
+ # Cookies will be loaded automatically if needed
140
+ return self.api_get('/user/profile')
141
+ """
142
+
143
+ def decorator(func: F) -> F:
144
+ if inspect.iscoroutinefunction(func):
145
+ # Async version
146
+ @functools.wraps(func)
147
+ async def async_wrapper(self, *args, **kwargs):
148
+ await _ensure_cookies_async(self, cookie_manager_attr)
149
+ return await func(self, *args, **kwargs)
150
+
151
+ return async_wrapper
152
+
153
+ # Sync version
154
+ @functools.wraps(func)
155
+ def sync_wrapper(self, *args, **kwargs):
156
+ _ensure_cookies_sync(self, cookie_manager_attr)
157
+ return func(self, *args, **kwargs)
158
+
159
+ return sync_wrapper
160
+
161
+ return decorator
162
+
163
+
164
+ class CookieManagerMixin:
165
+ """Mixin class to add cookie manager support to any client class.
166
+
167
+ Example:
168
+ class MyAPIClient(CookieManagerMixin):
169
+ def __init__(self, cookie_path: str):
170
+ self.init_cookie_manager(cookie_path)
171
+
172
+ @with_auth_retry()
173
+ def get_data(self):
174
+ return self.api_request('/data')
175
+ """
176
+
177
+ cookie_manager: CookieManager | AsyncCookieManager
178
+
179
+ def init_cookie_manager(
180
+ self,
181
+ cookie_path: str | None = None,
182
+ redis_client: Any = None,
183
+ key_prefix: str = 'pararamio:cookies',
184
+ use_async: bool = False,
185
+ ):
186
+ """Initialize cookie manager.
187
+
188
+ Args:
189
+ cookie_path: Path to cookie file (for file-based storage)
190
+ redis_client: Redis client instance (for Redis-based storage)
191
+ key_prefix: Redis key prefix
192
+ use_async: Whether to use async cookie manager
193
+ """
194
+ if redis_client:
195
+ # Use Redis-based manager
196
+ if use_async:
197
+ self.cookie_manager = AsyncRedisCookieManager(redis_client, key_prefix)
198
+ else:
199
+ self.cookie_manager = RedisCookieManager(redis_client, key_prefix)
200
+ elif cookie_path:
201
+ # Use file-based manager
202
+ if use_async:
203
+ self.cookie_manager = AsyncFileCookieManager(cookie_path)
204
+ else:
205
+ self.cookie_manager = FileCookieManager(cookie_path)
206
+ else:
207
+ msg = 'Either cookie_path or redis_client must be provided'
208
+ raise ValueError(msg)