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,403 @@
|
|
1
|
+
"""Async Team model."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from typing import TYPE_CHECKING, Any
|
7
|
+
|
8
|
+
# Imports from core
|
9
|
+
from pararamio_aio._core import PararamioRequestException
|
10
|
+
|
11
|
+
from .base import BaseModel
|
12
|
+
from .group import Group
|
13
|
+
from .user import User
|
14
|
+
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from ..client import AsyncPararamio
|
17
|
+
|
18
|
+
__all__ = ("TeamMemberStatus", "TeamMember", "Team")
|
19
|
+
|
20
|
+
|
21
|
+
class TeamCommonMixin:
|
22
|
+
"""Mixin for common team/member properties."""
|
23
|
+
|
24
|
+
_data: dict[str, Any] # This will be provided by BaseModel
|
25
|
+
|
26
|
+
@property
|
27
|
+
def state(self) -> str:
|
28
|
+
"""Get state."""
|
29
|
+
return self._data.get("state", "")
|
30
|
+
|
31
|
+
@property
|
32
|
+
def time_created(self) -> datetime | None:
|
33
|
+
"""Get creation time."""
|
34
|
+
return self._data.get("time_created")
|
35
|
+
|
36
|
+
@property
|
37
|
+
def time_updated(self) -> datetime | None:
|
38
|
+
"""Get last update time."""
|
39
|
+
return self._data.get("time_updated")
|
40
|
+
|
41
|
+
|
42
|
+
class TeamMemberStatus(BaseModel):
|
43
|
+
"""Team member status model."""
|
44
|
+
|
45
|
+
def __init__(self, client: AsyncPararamio, id: int, **kwargs):
|
46
|
+
"""Initialize team member status.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
client: AsyncPararamio client
|
50
|
+
id: Status ID
|
51
|
+
**kwargs: Additional status data
|
52
|
+
"""
|
53
|
+
super().__init__(client, id=id, **kwargs)
|
54
|
+
self.id = id
|
55
|
+
|
56
|
+
@property
|
57
|
+
def user_id(self) -> int:
|
58
|
+
"""Get user ID."""
|
59
|
+
return self._data.get("user_id", 0)
|
60
|
+
|
61
|
+
@property
|
62
|
+
def setter_id(self) -> int:
|
63
|
+
"""Get setter user ID."""
|
64
|
+
return self._data.get("setter_id", 0)
|
65
|
+
|
66
|
+
@property
|
67
|
+
def org_id(self) -> int:
|
68
|
+
"""Get organization ID."""
|
69
|
+
return self._data.get("org_id", 0)
|
70
|
+
|
71
|
+
@property
|
72
|
+
def time_created(self) -> datetime | None:
|
73
|
+
"""Get creation time."""
|
74
|
+
return self._data.get("time_created")
|
75
|
+
|
76
|
+
@property
|
77
|
+
def status(self) -> str:
|
78
|
+
"""Get status text."""
|
79
|
+
return self._data.get("status", "")
|
80
|
+
|
81
|
+
|
82
|
+
class TeamMember(TeamCommonMixin, BaseModel):
|
83
|
+
"""Team member model."""
|
84
|
+
|
85
|
+
def __init__(self, client: AsyncPararamio, id: int, org_id: int, **kwargs):
|
86
|
+
"""Initialize team member.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
client: AsyncPararamio client
|
90
|
+
id: Member ID
|
91
|
+
org_id: Organization ID
|
92
|
+
**kwargs: Additional member data
|
93
|
+
"""
|
94
|
+
super().__init__(client, id=id, org_id=org_id, **kwargs)
|
95
|
+
self.id = id
|
96
|
+
self.org_id = org_id
|
97
|
+
|
98
|
+
@property
|
99
|
+
def email(self) -> str:
|
100
|
+
"""Get member email."""
|
101
|
+
return self._data.get("email", "")
|
102
|
+
|
103
|
+
@property
|
104
|
+
def chats(self) -> list[int]:
|
105
|
+
"""Get member chat IDs."""
|
106
|
+
return self._data.get("chats", [])
|
107
|
+
|
108
|
+
@property
|
109
|
+
def groups(self) -> list[int]:
|
110
|
+
"""Get member group IDs."""
|
111
|
+
return self._data.get("groups", [])
|
112
|
+
|
113
|
+
@property
|
114
|
+
def is_admin(self) -> bool:
|
115
|
+
"""Check if member is admin."""
|
116
|
+
return self._data.get("is_admin", False)
|
117
|
+
|
118
|
+
@property
|
119
|
+
def is_member(self) -> bool:
|
120
|
+
"""Check if user is active member."""
|
121
|
+
return self._data.get("is_member", True)
|
122
|
+
|
123
|
+
@property
|
124
|
+
def last_activity(self) -> datetime | None:
|
125
|
+
"""Get last activity time."""
|
126
|
+
return self._data.get("last_activity")
|
127
|
+
|
128
|
+
@property
|
129
|
+
def phonenumber(self) -> str | None:
|
130
|
+
"""Get phone number."""
|
131
|
+
return self._data.get("phonenumber")
|
132
|
+
|
133
|
+
@property
|
134
|
+
def two_step_enabled(self) -> bool:
|
135
|
+
"""Check if two-step verification is enabled."""
|
136
|
+
return self._data.get("two_step_enabled", False)
|
137
|
+
|
138
|
+
@property
|
139
|
+
def inviter_id(self) -> int | None:
|
140
|
+
"""Get inviter user ID."""
|
141
|
+
return self._data.get("inviter_id")
|
142
|
+
|
143
|
+
async def get_user(self) -> User | None:
|
144
|
+
"""Get associated User object.
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
User object or None
|
148
|
+
"""
|
149
|
+
return await self.client.get_user_by_id(self.id)
|
150
|
+
|
151
|
+
async def get_last_status(self) -> TeamMemberStatus | None:
|
152
|
+
"""Get last status for this member.
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
TeamMemberStatus or None if no status
|
156
|
+
"""
|
157
|
+
url = f"/core/org/status?user_ids={self.id}"
|
158
|
+
response = await self.client.api_get(url)
|
159
|
+
data = response.get("data", [])
|
160
|
+
|
161
|
+
if not data:
|
162
|
+
return None
|
163
|
+
|
164
|
+
return TeamMemberStatus(self.client, **data[0])
|
165
|
+
|
166
|
+
async def add_status(self, status: str) -> bool:
|
167
|
+
"""Add status for this member.
|
168
|
+
|
169
|
+
Args:
|
170
|
+
status: Status text
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
True if successful
|
174
|
+
"""
|
175
|
+
url = "/core/org/status"
|
176
|
+
data = {
|
177
|
+
"org_id": self.org_id,
|
178
|
+
"status": status,
|
179
|
+
"user_id": self.id,
|
180
|
+
}
|
181
|
+
response = await self.client.api_post(url, data=data)
|
182
|
+
return bool(response) and response.get("result") == "OK"
|
183
|
+
|
184
|
+
def __str__(self) -> str:
|
185
|
+
"""String representation."""
|
186
|
+
return self.email or str(self.id)
|
187
|
+
|
188
|
+
def __eq__(self, other) -> bool:
|
189
|
+
"""Check equality."""
|
190
|
+
if not isinstance(other, (TeamMember, User)):
|
191
|
+
return False
|
192
|
+
return self.id == other.id
|
193
|
+
|
194
|
+
|
195
|
+
class Team(TeamCommonMixin, BaseModel):
|
196
|
+
"""Async Team model with explicit loading."""
|
197
|
+
|
198
|
+
def __init__(self, client: AsyncPararamio, id: int, **kwargs):
|
199
|
+
"""Initialize async team.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
client: AsyncPararamio client
|
203
|
+
id: Team ID
|
204
|
+
**kwargs: Additional team data
|
205
|
+
"""
|
206
|
+
super().__init__(client, id=id, **kwargs)
|
207
|
+
self.id = id
|
208
|
+
|
209
|
+
@property
|
210
|
+
def title(self) -> str:
|
211
|
+
"""Get team title."""
|
212
|
+
return self._data.get("title", "")
|
213
|
+
|
214
|
+
@property
|
215
|
+
def slug(self) -> str:
|
216
|
+
"""Get team slug."""
|
217
|
+
return self._data.get("slug", "")
|
218
|
+
|
219
|
+
@property
|
220
|
+
def description(self) -> str | None:
|
221
|
+
"""Get team description."""
|
222
|
+
return self._data.get("description")
|
223
|
+
|
224
|
+
@property
|
225
|
+
def email_domain(self) -> str | None:
|
226
|
+
"""Get email domain."""
|
227
|
+
return self._data.get("email_domain")
|
228
|
+
|
229
|
+
@property
|
230
|
+
def is_active(self) -> bool:
|
231
|
+
"""Check if team is active."""
|
232
|
+
return self._data.get("is_active", True)
|
233
|
+
|
234
|
+
@property
|
235
|
+
def two_step_required(self) -> bool:
|
236
|
+
"""Check if two-step verification is required."""
|
237
|
+
return self._data.get("two_step_required", False)
|
238
|
+
|
239
|
+
@property
|
240
|
+
def default_thread_id(self) -> int:
|
241
|
+
"""Get default thread ID."""
|
242
|
+
return self._data.get("default_thread_id", 0)
|
243
|
+
|
244
|
+
@property
|
245
|
+
def guest_thread_id(self) -> int | None:
|
246
|
+
"""Get guest thread ID."""
|
247
|
+
return self._data.get("guest_thread_id")
|
248
|
+
|
249
|
+
@property
|
250
|
+
def inviter_id(self) -> int | None:
|
251
|
+
"""Get inviter user ID."""
|
252
|
+
return self._data.get("inviter_id")
|
253
|
+
|
254
|
+
@property
|
255
|
+
def users(self) -> list[int]:
|
256
|
+
"""Get team user IDs."""
|
257
|
+
return self._data.get("users", [])
|
258
|
+
|
259
|
+
@property
|
260
|
+
def admins(self) -> list[int]:
|
261
|
+
"""Get team admin IDs."""
|
262
|
+
return self._data.get("admins", [])
|
263
|
+
|
264
|
+
@property
|
265
|
+
def groups(self) -> list[int]:
|
266
|
+
"""Get team group IDs."""
|
267
|
+
return self._data.get("groups", [])
|
268
|
+
|
269
|
+
@property
|
270
|
+
def guests(self) -> list[int]:
|
271
|
+
"""Get team guest IDs."""
|
272
|
+
return self._data.get("guests", [])
|
273
|
+
|
274
|
+
async def load(self) -> Team:
|
275
|
+
"""Load full team data from API.
|
276
|
+
|
277
|
+
Returns:
|
278
|
+
Self with updated data
|
279
|
+
"""
|
280
|
+
url = f"/core/org?ids={self.id}"
|
281
|
+
response = await self.client.api_get(url)
|
282
|
+
|
283
|
+
if "orgs" in response and response["orgs"]:
|
284
|
+
self._data.update(response["orgs"][0])
|
285
|
+
else:
|
286
|
+
self._data.update(response)
|
287
|
+
|
288
|
+
return self
|
289
|
+
|
290
|
+
async def create_role(self, name: str, description: str | None = None) -> Group:
|
291
|
+
"""Create a new role (group) in this team.
|
292
|
+
|
293
|
+
Args:
|
294
|
+
name: Role name
|
295
|
+
description: Role description
|
296
|
+
|
297
|
+
Returns:
|
298
|
+
Created Group object
|
299
|
+
"""
|
300
|
+
return await Group.create(
|
301
|
+
self.client,
|
302
|
+
name=name,
|
303
|
+
unique_name=name, # Async version requires unique_name
|
304
|
+
description=description or "",
|
305
|
+
# Note: organization_id parameter not supported in async Group.create
|
306
|
+
)
|
307
|
+
|
308
|
+
async def get_member_info(self, user_id: int) -> TeamMember:
|
309
|
+
"""Get information about a specific member.
|
310
|
+
|
311
|
+
Args:
|
312
|
+
user_id: User ID
|
313
|
+
|
314
|
+
Returns:
|
315
|
+
TeamMember object
|
316
|
+
|
317
|
+
Raises:
|
318
|
+
PararamioRequestException: If member not found
|
319
|
+
"""
|
320
|
+
url = f"/core/org/{self.id}/member_info/{user_id}"
|
321
|
+
response = await self.client.api_get(url)
|
322
|
+
|
323
|
+
if not response:
|
324
|
+
raise PararamioRequestException(f"empty response for user {user_id}")
|
325
|
+
|
326
|
+
return TeamMember(self.client, org_id=self.id, **response)
|
327
|
+
|
328
|
+
async def get_members_info(self) -> list[TeamMember]:
|
329
|
+
"""Get information about all team members.
|
330
|
+
|
331
|
+
Returns:
|
332
|
+
List of TeamMember objects
|
333
|
+
"""
|
334
|
+
url = f"/core/org/{self.id}/member_info"
|
335
|
+
response = await self.client.api_get(url)
|
336
|
+
|
337
|
+
if response:
|
338
|
+
return [
|
339
|
+
TeamMember(self.client, org_id=self.id, **member_data)
|
340
|
+
for member_data in response.get("data", [])
|
341
|
+
]
|
342
|
+
return []
|
343
|
+
|
344
|
+
async def mark_all_messages_as_read(self) -> bool:
|
345
|
+
"""Mark all messages in this team as read.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
True if successful
|
349
|
+
"""
|
350
|
+
return await self.client.mark_all_messages_as_read(self.id)
|
351
|
+
|
352
|
+
@classmethod
|
353
|
+
async def get_my_team_ids(cls, client: AsyncPararamio) -> list[int]:
|
354
|
+
"""Get IDs of teams the current user belongs to.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
client: AsyncPararamio client
|
358
|
+
|
359
|
+
Returns:
|
360
|
+
List of team IDs
|
361
|
+
"""
|
362
|
+
url = "/core/org/sync"
|
363
|
+
response = await client.api_get(url)
|
364
|
+
return response.get("ids", [])
|
365
|
+
|
366
|
+
@classmethod
|
367
|
+
async def load_teams(cls, client: AsyncPararamio) -> list[Team]:
|
368
|
+
"""Load all teams for the current user.
|
369
|
+
|
370
|
+
Args:
|
371
|
+
client: AsyncPararamio client
|
372
|
+
|
373
|
+
Returns:
|
374
|
+
List of Team objects
|
375
|
+
"""
|
376
|
+
ids = await cls.get_my_team_ids(client)
|
377
|
+
|
378
|
+
if not ids:
|
379
|
+
return []
|
380
|
+
|
381
|
+
url = "/core/org?ids=" + ",".join(map(str, ids))
|
382
|
+
response = await client.api_get(url)
|
383
|
+
|
384
|
+
if response:
|
385
|
+
return [cls(client, **team_data) for team_data in response.get("orgs", [])]
|
386
|
+
|
387
|
+
return []
|
388
|
+
|
389
|
+
def __contains__(self, item) -> bool:
|
390
|
+
"""Check if user is in team."""
|
391
|
+
if not isinstance(item, (TeamMember, User)):
|
392
|
+
return False
|
393
|
+
return item.id in self.users
|
394
|
+
|
395
|
+
def __str__(self) -> str:
|
396
|
+
"""String representation."""
|
397
|
+
return self.title or f"Team({self.id})"
|
398
|
+
|
399
|
+
def __eq__(self, other) -> bool:
|
400
|
+
"""Check equality."""
|
401
|
+
if not isinstance(other, Team):
|
402
|
+
return False
|
403
|
+
return self.id == other.id
|
@@ -0,0 +1,239 @@
|
|
1
|
+
"""Async User model."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from datetime import datetime
|
7
|
+
from typing import TYPE_CHECKING, Any
|
8
|
+
|
9
|
+
# Imports from core
|
10
|
+
from pararamio_aio._core import PararamNotFound
|
11
|
+
|
12
|
+
from .activity import Activity, ActivityAction
|
13
|
+
from .base import BaseModel
|
14
|
+
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from ..client import AsyncPararamio
|
17
|
+
from .chat import Chat
|
18
|
+
from .post import Post
|
19
|
+
|
20
|
+
__all__ = ("User", "UserSearchResult")
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class UserSearchResult:
|
25
|
+
"""User search result with explicit data."""
|
26
|
+
|
27
|
+
id: int
|
28
|
+
avatar: str | None
|
29
|
+
name: str
|
30
|
+
unique_name: str
|
31
|
+
custom_name: str | None
|
32
|
+
time_created: str
|
33
|
+
time_updated: str
|
34
|
+
other_blocked: bool
|
35
|
+
pm_thread_id: int | None
|
36
|
+
is_bot: bool
|
37
|
+
client: AsyncPararamio
|
38
|
+
|
39
|
+
@property
|
40
|
+
def has_pm(self) -> bool:
|
41
|
+
"""Check if user has PM thread."""
|
42
|
+
return self.pm_thread_id is not None
|
43
|
+
|
44
|
+
async def get_pm_thread(self) -> Chat:
|
45
|
+
"""Get PM thread for this user.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Chat object for PM
|
49
|
+
"""
|
50
|
+
if self.pm_thread_id:
|
51
|
+
chat = await self.client.get_chat_by_id(self.pm_thread_id)
|
52
|
+
if chat is None:
|
53
|
+
raise ValueError(f"Chat with id {self.pm_thread_id} not found")
|
54
|
+
return chat
|
55
|
+
# Create new PM thread
|
56
|
+
return await self.create_pm_thread()
|
57
|
+
|
58
|
+
async def create_pm_thread(self) -> Chat:
|
59
|
+
"""Create a new PM thread with this user.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
New chat object
|
63
|
+
"""
|
64
|
+
|
65
|
+
url = f"/core/chat/pm/{self.id}"
|
66
|
+
response = await self.client.api_post(url)
|
67
|
+
chat_id = response["chat_id"]
|
68
|
+
chat = await self.client.get_chat_by_id(chat_id)
|
69
|
+
if chat is None:
|
70
|
+
raise ValueError(f"Failed to create or retrieve chat with id {chat_id}")
|
71
|
+
return chat
|
72
|
+
|
73
|
+
async def send_message(self, text: str) -> Post:
|
74
|
+
"""Send a private message to this user.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
text: Message text
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
Created post
|
81
|
+
"""
|
82
|
+
chat = await self.get_pm_thread()
|
83
|
+
return await chat.send_message(text)
|
84
|
+
|
85
|
+
|
86
|
+
class User(BaseModel):
|
87
|
+
"""Async User model with explicit loading."""
|
88
|
+
|
89
|
+
def __init__(self, client: AsyncPararamio, id: int, name: str | None = None, **kwargs):
|
90
|
+
"""Initialize async user.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
client: AsyncPararamio client
|
94
|
+
id: User ID
|
95
|
+
name: Optional user name
|
96
|
+
**kwargs: Additional user data
|
97
|
+
"""
|
98
|
+
super().__init__(client, id=id, name=name, **kwargs)
|
99
|
+
self.id = id
|
100
|
+
|
101
|
+
@property
|
102
|
+
def name(self) -> str | None:
|
103
|
+
"""Get user name."""
|
104
|
+
return self._data.get("name")
|
105
|
+
|
106
|
+
@property
|
107
|
+
def unique_name(self) -> str | None:
|
108
|
+
"""Get user unique name."""
|
109
|
+
return self._data.get("unique_name")
|
110
|
+
|
111
|
+
@property
|
112
|
+
def is_bot(self) -> bool:
|
113
|
+
"""Check if user is a bot."""
|
114
|
+
return self._data.get("is_bot", False)
|
115
|
+
|
116
|
+
@property
|
117
|
+
def time_created(self) -> datetime | None:
|
118
|
+
"""Get user creation time."""
|
119
|
+
return self._data.get("time_created")
|
120
|
+
|
121
|
+
@property
|
122
|
+
def time_updated(self) -> datetime | None:
|
123
|
+
"""Get user last update time."""
|
124
|
+
return self._data.get("time_updated")
|
125
|
+
|
126
|
+
@property
|
127
|
+
def info(self) -> str | None:
|
128
|
+
"""Get user info."""
|
129
|
+
return self._data.get("info")
|
130
|
+
|
131
|
+
@property
|
132
|
+
def organizations(self) -> list[int]:
|
133
|
+
"""Get user organization IDs."""
|
134
|
+
return self._data.get("organizations", [])
|
135
|
+
|
136
|
+
async def load(self) -> User:
|
137
|
+
"""Load full user data from API.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
Self with updated data
|
141
|
+
"""
|
142
|
+
users = await self.client.get_users_by_ids([self.id])
|
143
|
+
if not users:
|
144
|
+
raise PararamNotFound(f"User {self.id} not found")
|
145
|
+
|
146
|
+
# Update our data with loaded data
|
147
|
+
self._data.update(users[0]._data)
|
148
|
+
return self
|
149
|
+
|
150
|
+
async def send_private_message(self, text: str) -> Post:
|
151
|
+
"""Send a private message to this user.
|
152
|
+
|
153
|
+
Args:
|
154
|
+
text: Message text
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
Created post
|
158
|
+
"""
|
159
|
+
url = "/msg/post/private"
|
160
|
+
response = await self.client.api_post(url, {"text": text, "user_id": self.id})
|
161
|
+
|
162
|
+
# Load the created post
|
163
|
+
post = await self.client.get_post(response["chat_id"], response["post_no"])
|
164
|
+
if post is None:
|
165
|
+
raise ValueError(
|
166
|
+
f"Failed to retrieve post {response['post_no']} from chat {response['chat_id']}"
|
167
|
+
)
|
168
|
+
return post
|
169
|
+
|
170
|
+
async def _activity_page_loader(
|
171
|
+
self, action: ActivityAction | None = None, page: int = 1
|
172
|
+
) -> dict[str, Any]:
|
173
|
+
"""Load activity page from API.
|
174
|
+
|
175
|
+
Args:
|
176
|
+
action: Optional action type to filter
|
177
|
+
page: Page number
|
178
|
+
|
179
|
+
Returns:
|
180
|
+
API response dict
|
181
|
+
"""
|
182
|
+
url = f"/activity?user_id={self.id}&page={page}"
|
183
|
+
if action:
|
184
|
+
url += f"&action={action.value}"
|
185
|
+
|
186
|
+
return await self.client.api_get(url)
|
187
|
+
|
188
|
+
async def get_activity(
|
189
|
+
self, start: datetime, end: datetime, actions: list[ActivityAction] | None = None
|
190
|
+
) -> list[Activity]:
|
191
|
+
"""Get user activity within date range.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
start: Start datetime
|
195
|
+
end: End datetime
|
196
|
+
actions: Optional list of ActivityAction types to filter
|
197
|
+
|
198
|
+
Returns:
|
199
|
+
List of Activity objects sorted by time
|
200
|
+
"""
|
201
|
+
|
202
|
+
# Create async page loader
|
203
|
+
async def page_loader(
|
204
|
+
action: ActivityAction | None = None, page: int = 1
|
205
|
+
) -> dict[str, Any]:
|
206
|
+
return await self._activity_page_loader(action, page)
|
207
|
+
|
208
|
+
return await Activity.get_activity(page_loader, start, end, actions)
|
209
|
+
|
210
|
+
@classmethod
|
211
|
+
async def search(cls, client: AsyncPararamio, query: str) -> list[UserSearchResult]:
|
212
|
+
"""Search for users.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
client: AsyncPararamio client
|
216
|
+
query: Search query
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
List of search results
|
220
|
+
"""
|
221
|
+
url = f"/users?flt={query}"
|
222
|
+
response = await client.api_get(url)
|
223
|
+
|
224
|
+
results = []
|
225
|
+
for data in response.get("users", []):
|
226
|
+
result = UserSearchResult(client=client, **data)
|
227
|
+
results.append(result)
|
228
|
+
|
229
|
+
return results
|
230
|
+
|
231
|
+
def __eq__(self, other) -> bool:
|
232
|
+
"""Check equality with another user."""
|
233
|
+
if not isinstance(other, User):
|
234
|
+
return False
|
235
|
+
return self.id == other.id
|
236
|
+
|
237
|
+
def __str__(self) -> str:
|
238
|
+
"""String representation."""
|
239
|
+
return self.name or f"User({self.id})"
|
pararamio_aio/py.typed
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
"""Async utilities for pararamio_aio package."""
|
2
|
+
|
3
|
+
from .authentication import (
|
4
|
+
async_authenticate,
|
5
|
+
async_do_second_step,
|
6
|
+
async_do_second_step_with_code,
|
7
|
+
get_async_xsrf_token,
|
8
|
+
)
|
9
|
+
from .requests import async_api_request, async_bot_request
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"async_authenticate",
|
13
|
+
"async_do_second_step",
|
14
|
+
"async_do_second_step_with_code",
|
15
|
+
"get_async_xsrf_token",
|
16
|
+
"async_api_request",
|
17
|
+
"async_bot_request",
|
18
|
+
]
|