nc-py-api 0.10.0__py3-none-any.whl → 0.18.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.
- nc_py_api/__init__.py +1 -1
- nc_py_api/_session.py +27 -14
- nc_py_api/_version.py +1 -1
- nc_py_api/apps.py +0 -13
- nc_py_api/ex_app/__init__.py +13 -3
- nc_py_api/ex_app/defs.py +17 -29
- nc_py_api/ex_app/events_listener.py +137 -0
- nc_py_api/ex_app/integration_fastapi.py +25 -14
- nc_py_api/ex_app/logging.py +46 -0
- nc_py_api/ex_app/misc.py +6 -1
- nc_py_api/ex_app/occ_commands.py +153 -0
- nc_py_api/ex_app/providers/providers.py +7 -21
- nc_py_api/ex_app/providers/task_processing.py +261 -0
- nc_py_api/ex_app/ui/files_actions.py +45 -61
- nc_py_api/files/__init__.py +137 -6
- nc_py_api/files/_files.py +44 -10
- nc_py_api/files/files.py +59 -464
- nc_py_api/files/files_async.py +528 -0
- nc_py_api/loginflow_v2.py +161 -0
- nc_py_api/nextcloud.py +77 -21
- nc_py_api/talk_bot.py +5 -0
- nc_py_api/users.py +3 -3
- nc_py_api/webhooks.py +224 -0
- {nc_py_api-0.10.0.dist-info → nc_py_api-0.18.1.dist-info}/METADATA +36 -24
- nc_py_api-0.18.1.dist-info/RECORD +53 -0
- {nc_py_api-0.10.0.dist-info → nc_py_api-0.18.1.dist-info}/WHEEL +1 -1
- {nc_py_api-0.10.0.dist-info → nc_py_api-0.18.1.dist-info}/licenses/AUTHORS +1 -0
- nc_py_api/ex_app/providers/speech_to_text.py +0 -128
- nc_py_api/ex_app/providers/text_processing.py +0 -135
- nc_py_api/ex_app/providers/translations.py +0 -156
- nc_py_api-0.10.0.dist-info/RECORD +0 -49
- {nc_py_api-0.10.0.dist-info → nc_py_api-0.18.1.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Login flow v2 API wrapper."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from ._exceptions import check_error
|
|
11
|
+
from ._session import AsyncNcSession, NcSession
|
|
12
|
+
|
|
13
|
+
MAX_TIMEOUT = 60 * 20
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class LoginFlow:
|
|
18
|
+
"""The Nextcloud Login flow v2 initialization response representation."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, raw_data: dict) -> None:
|
|
21
|
+
self.raw_data = raw_data
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def login(self) -> str:
|
|
25
|
+
"""The URL for user authorization.
|
|
26
|
+
|
|
27
|
+
Should be opened by the user in the default browser to authorize in Nextcloud.
|
|
28
|
+
"""
|
|
29
|
+
return self.raw_data["login"]
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def token(self) -> str:
|
|
33
|
+
"""Token for a polling for confirmation of user authorization."""
|
|
34
|
+
return self.raw_data["poll"]["token"]
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def endpoint(self) -> str:
|
|
38
|
+
"""Endpoint for polling."""
|
|
39
|
+
return self.raw_data["poll"]["endpoint"]
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"<{self.__class__.__name__} login_url={self.login}>"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Credentials:
|
|
47
|
+
"""The Nextcloud Login flow v2 response with app credentials representation."""
|
|
48
|
+
|
|
49
|
+
def __init__(self, raw_data: dict) -> None:
|
|
50
|
+
self.raw_data = raw_data
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def server(self) -> str:
|
|
54
|
+
"""The address of Nextcloud to connect to.
|
|
55
|
+
|
|
56
|
+
The server may specify a protocol (http or https). If no protocol is specified https will be used.
|
|
57
|
+
"""
|
|
58
|
+
return self.raw_data["server"]
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def login_name(self) -> str:
|
|
62
|
+
"""The username for authenticating with Nextcloud."""
|
|
63
|
+
return self.raw_data["loginName"]
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def app_password(self) -> str:
|
|
67
|
+
"""The application password generated for authenticating with Nextcloud."""
|
|
68
|
+
return self.raw_data["appPassword"]
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
return f"<{self.__class__.__name__} login={self.login_name} app_password={self.app_password}>"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _LoginFlowV2API:
|
|
75
|
+
"""Class implementing Nextcloud Login flow v2."""
|
|
76
|
+
|
|
77
|
+
_ep_init: str = "/index.php/login/v2"
|
|
78
|
+
_ep_poll: str = "/index.php/login/v2/poll"
|
|
79
|
+
|
|
80
|
+
def __init__(self, session: NcSession) -> None:
|
|
81
|
+
self._session = session
|
|
82
|
+
|
|
83
|
+
def init(self, user_agent: str = "nc_py_api") -> LoginFlow:
|
|
84
|
+
"""Init a Login flow v2.
|
|
85
|
+
|
|
86
|
+
:param user_agent: Application name. Application password will be associated with this name.
|
|
87
|
+
"""
|
|
88
|
+
r = self._session.adapter.post(self._ep_init, headers={"user-agent": user_agent})
|
|
89
|
+
return LoginFlow(_res_to_json(r))
|
|
90
|
+
|
|
91
|
+
def poll(self, token: str, timeout: int = MAX_TIMEOUT, step: int = 1, overwrite_auth: bool = True) -> Credentials:
|
|
92
|
+
"""Poll the Login flow v2 credentials.
|
|
93
|
+
|
|
94
|
+
:param token: Token for a polling for confirmation of user authorization.
|
|
95
|
+
:param timeout: Maximum time to wait for polling in seconds, defaults to MAX_TIMEOUT.
|
|
96
|
+
:param step: Interval for polling in seconds, defaults to 1.
|
|
97
|
+
:param overwrite_auth: If True current session will be overwritten with new credentials, defaults to True.
|
|
98
|
+
:raises ValueError: If timeout more than 20 minutes.
|
|
99
|
+
"""
|
|
100
|
+
if timeout > MAX_TIMEOUT:
|
|
101
|
+
msg = "Timeout can't be more than 20 minutes."
|
|
102
|
+
raise ValueError(msg)
|
|
103
|
+
for _ in range(timeout // step):
|
|
104
|
+
r = self._session.adapter.post(self._ep_poll, data={"token": token})
|
|
105
|
+
if r.status_code == 200:
|
|
106
|
+
break
|
|
107
|
+
time.sleep(step)
|
|
108
|
+
r_model = Credentials(_res_to_json(r))
|
|
109
|
+
if overwrite_auth:
|
|
110
|
+
self._session.cfg.auth = (r_model.login_name, r_model.app_password)
|
|
111
|
+
self._session.init_adapter(restart=True)
|
|
112
|
+
self._session.init_adapter_dav(restart=True)
|
|
113
|
+
return r_model
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class _AsyncLoginFlowV2API:
|
|
117
|
+
"""Class implementing Async Nextcloud Login flow v2."""
|
|
118
|
+
|
|
119
|
+
_ep_init: str = "/index.php/login/v2"
|
|
120
|
+
_ep_poll: str = "/index.php/login/v2/poll"
|
|
121
|
+
|
|
122
|
+
def __init__(self, session: AsyncNcSession) -> None:
|
|
123
|
+
self._session = session
|
|
124
|
+
|
|
125
|
+
async def init(self, user_agent: str = "nc_py_api") -> LoginFlow:
|
|
126
|
+
"""Init a Login flow v2.
|
|
127
|
+
|
|
128
|
+
:param user_agent: Application name. Application password will be associated with this name.
|
|
129
|
+
"""
|
|
130
|
+
r = await self._session.adapter.post(self._ep_init, headers={"user-agent": user_agent})
|
|
131
|
+
return LoginFlow(_res_to_json(r))
|
|
132
|
+
|
|
133
|
+
async def poll(
|
|
134
|
+
self, token: str, timeout: int = MAX_TIMEOUT, step: int = 1, overwrite_auth: bool = True
|
|
135
|
+
) -> Credentials:
|
|
136
|
+
"""Poll the Login flow v2 credentials.
|
|
137
|
+
|
|
138
|
+
:param token: Token for a polling for confirmation of user authorization.
|
|
139
|
+
:param timeout: Maximum time to wait for polling in seconds, defaults to MAX_TIMEOUT.
|
|
140
|
+
:param step: Interval for polling in seconds, defaults to 1.
|
|
141
|
+
:param overwrite_auth: If True current session will be overwritten with new credentials, defaults to True.
|
|
142
|
+
:raises ValueError: If timeout more than 20 minutes.
|
|
143
|
+
"""
|
|
144
|
+
if timeout > MAX_TIMEOUT:
|
|
145
|
+
raise ValueError("Timeout can't be more than 20 minutes.")
|
|
146
|
+
for _ in range(timeout // step):
|
|
147
|
+
r = await self._session.adapter.post(self._ep_poll, data={"token": token})
|
|
148
|
+
if r.status_code == 200:
|
|
149
|
+
break
|
|
150
|
+
await asyncio.sleep(step)
|
|
151
|
+
r_model = Credentials(_res_to_json(r))
|
|
152
|
+
if overwrite_auth:
|
|
153
|
+
self._session.cfg.auth = (r_model.login_name, r_model.app_password)
|
|
154
|
+
self._session.init_adapter(restart=True)
|
|
155
|
+
self._session.init_adapter_dav(restart=True)
|
|
156
|
+
return r_model
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _res_to_json(response: httpx.Response) -> dict:
|
|
160
|
+
check_error(response)
|
|
161
|
+
return json.loads(response.text)
|
nc_py_api/nextcloud.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Nextcloud class providing access to all API endpoints."""
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import typing
|
|
4
5
|
from abc import ABC
|
|
5
6
|
|
|
@@ -30,15 +31,20 @@ from .activity import _ActivityAPI, _AsyncActivityAPI
|
|
|
30
31
|
from .apps import _AppsAPI, _AsyncAppsAPI
|
|
31
32
|
from .calendar import _CalendarAPI
|
|
32
33
|
from .ex_app.defs import LogLvl
|
|
34
|
+
from .ex_app.events_listener import AsyncEventsListenerAPI, EventsListenerAPI
|
|
35
|
+
from .ex_app.occ_commands import AsyncOccCommandsAPI, OccCommandsAPI
|
|
33
36
|
from .ex_app.providers.providers import AsyncProvidersApi, ProvidersApi
|
|
34
37
|
from .ex_app.ui.ui import AsyncUiApi, UiApi
|
|
35
|
-
from .files.files import
|
|
38
|
+
from .files.files import FilesAPI
|
|
39
|
+
from .files.files_async import AsyncFilesAPI
|
|
40
|
+
from .loginflow_v2 import _AsyncLoginFlowV2API, _LoginFlowV2API
|
|
36
41
|
from .notes import _AsyncNotesAPI, _NotesAPI
|
|
37
42
|
from .notifications import _AsyncNotificationsAPI, _NotificationsAPI
|
|
38
43
|
from .user_status import _AsyncUserStatusAPI, _UserStatusAPI
|
|
39
44
|
from .users import _AsyncUsersAPI, _UsersAPI
|
|
40
45
|
from .users_groups import _AsyncUsersGroupsAPI, _UsersGroupsAPI
|
|
41
46
|
from .weather_status import _AsyncWeatherStatusAPI, _WeatherStatusAPI
|
|
47
|
+
from .webhooks import _AsyncWebhooksAPI, _WebhooksAPI
|
|
42
48
|
|
|
43
49
|
|
|
44
50
|
class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
|
|
@@ -66,6 +72,8 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
|
|
|
66
72
|
"""Nextcloud API for managing users statuses"""
|
|
67
73
|
weather_status: _WeatherStatusAPI
|
|
68
74
|
"""Nextcloud API for managing user weather statuses"""
|
|
75
|
+
webhooks: _WebhooksAPI
|
|
76
|
+
"""Nextcloud API for managing webhooks"""
|
|
69
77
|
_session: NcSessionBasic
|
|
70
78
|
|
|
71
79
|
def __init__(self, session: NcSessionBasic):
|
|
@@ -81,6 +89,7 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
|
|
|
81
89
|
self.users_groups = _UsersGroupsAPI(session)
|
|
82
90
|
self.user_status = _UserStatusAPI(session)
|
|
83
91
|
self.weather_status = _WeatherStatusAPI(session)
|
|
92
|
+
self.webhooks = _WebhooksAPI(session)
|
|
84
93
|
|
|
85
94
|
@property
|
|
86
95
|
def capabilities(self) -> dict:
|
|
@@ -164,6 +173,8 @@ class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
|
|
|
164
173
|
"""Nextcloud API for managing users statuses"""
|
|
165
174
|
weather_status: _AsyncWeatherStatusAPI
|
|
166
175
|
"""Nextcloud API for managing user weather statuses"""
|
|
176
|
+
webhooks: _AsyncWebhooksAPI
|
|
177
|
+
"""Nextcloud API for managing webhooks"""
|
|
167
178
|
_session: AsyncNcSessionBasic
|
|
168
179
|
|
|
169
180
|
def __init__(self, session: AsyncNcSessionBasic):
|
|
@@ -179,6 +190,7 @@ class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
|
|
|
179
190
|
self.users_groups = _AsyncUsersGroupsAPI(session)
|
|
180
191
|
self.user_status = _AsyncUserStatusAPI(session)
|
|
181
192
|
self.weather_status = _AsyncWeatherStatusAPI(session)
|
|
193
|
+
self.webhooks = _AsyncWebhooksAPI(session)
|
|
182
194
|
|
|
183
195
|
@property
|
|
184
196
|
async def capabilities(self) -> dict:
|
|
@@ -244,15 +256,18 @@ class Nextcloud(_NextcloudBasic):
|
|
|
244
256
|
"""
|
|
245
257
|
|
|
246
258
|
_session: NcSession
|
|
259
|
+
loginflow_v2: _LoginFlowV2API
|
|
260
|
+
"""Nextcloud Login flow v2."""
|
|
247
261
|
|
|
248
262
|
def __init__(self, **kwargs):
|
|
249
263
|
"""If the parameters are not specified, they will be taken from the environment.
|
|
250
264
|
|
|
251
265
|
:param nextcloud_url: url of the nextcloud instance.
|
|
252
|
-
:param nc_auth_user: login username.
|
|
253
|
-
:param nc_auth_pass: password or app-password for the username.
|
|
266
|
+
:param nc_auth_user: login username. Optional.
|
|
267
|
+
:param nc_auth_pass: password or app-password for the username. Optional.
|
|
254
268
|
"""
|
|
255
269
|
self._session = NcSession(**kwargs)
|
|
270
|
+
self.loginflow_v2 = _LoginFlowV2API(self._session)
|
|
256
271
|
super().__init__(self._session)
|
|
257
272
|
|
|
258
273
|
@property
|
|
@@ -268,15 +283,18 @@ class AsyncNextcloud(_AsyncNextcloudBasic):
|
|
|
268
283
|
"""
|
|
269
284
|
|
|
270
285
|
_session: AsyncNcSession
|
|
286
|
+
loginflow_v2: _AsyncLoginFlowV2API
|
|
287
|
+
"""Nextcloud Login flow v2."""
|
|
271
288
|
|
|
272
289
|
def __init__(self, **kwargs):
|
|
273
290
|
"""If the parameters are not specified, they will be taken from the environment.
|
|
274
291
|
|
|
275
292
|
:param nextcloud_url: url of the nextcloud instance.
|
|
276
|
-
:param nc_auth_user: login username.
|
|
277
|
-
:param nc_auth_pass: password or app-password for the username.
|
|
293
|
+
:param nc_auth_user: login username. Optional.
|
|
294
|
+
:param nc_auth_pass: password or app-password for the username. Optional.
|
|
278
295
|
"""
|
|
279
296
|
self._session = AsyncNcSession(**kwargs)
|
|
297
|
+
self.loginflow_v2 = _AsyncLoginFlowV2API(self._session)
|
|
280
298
|
super().__init__(self._session)
|
|
281
299
|
|
|
282
300
|
@property
|
|
@@ -304,6 +322,10 @@ class NextcloudApp(_NextcloudBasic):
|
|
|
304
322
|
"""Nextcloud UI API for ExApps"""
|
|
305
323
|
providers: ProvidersApi
|
|
306
324
|
"""API for registering providers for Nextcloud"""
|
|
325
|
+
events_listener: EventsListenerAPI
|
|
326
|
+
"""API for registering Events listeners for ExApps"""
|
|
327
|
+
occ_commands: OccCommandsAPI
|
|
328
|
+
"""API for registering OCC command for ExApps"""
|
|
307
329
|
|
|
308
330
|
def __init__(self, **kwargs):
|
|
309
331
|
"""The parameters will be taken from the environment.
|
|
@@ -316,24 +338,38 @@ class NextcloudApp(_NextcloudBasic):
|
|
|
316
338
|
self.preferences_ex = PreferencesExAPI(self._session)
|
|
317
339
|
self.ui = UiApi(self._session)
|
|
318
340
|
self.providers = ProvidersApi(self._session)
|
|
341
|
+
self.events_listener = EventsListenerAPI(self._session)
|
|
342
|
+
self.occ_commands = OccCommandsAPI(self._session)
|
|
319
343
|
|
|
320
|
-
|
|
344
|
+
@property
|
|
345
|
+
def enabled_state(self) -> bool:
|
|
346
|
+
"""Returns ``True`` if ExApp is enabled, ``False`` otherwise."""
|
|
347
|
+
with contextlib.suppress(Exception):
|
|
348
|
+
return bool(self._session.ocs("GET", "/ocs/v1.php/apps/app_api/ex-app/state"))
|
|
349
|
+
return False
|
|
350
|
+
|
|
351
|
+
def log(self, log_lvl: LogLvl, content: str, fast_send: bool = False) -> None:
|
|
321
352
|
"""Writes log to the Nextcloud log file."""
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
353
|
+
int_log_lvl = int(log_lvl)
|
|
354
|
+
if int_log_lvl < 0 or int_log_lvl > 4:
|
|
355
|
+
raise ValueError("Invalid `log_lvl` value")
|
|
356
|
+
if not fast_send:
|
|
357
|
+
if self.check_capabilities("app_api"):
|
|
358
|
+
return
|
|
359
|
+
if int_log_lvl < self.capabilities["app_api"].get("loglevel", 0):
|
|
360
|
+
return
|
|
361
|
+
with contextlib.suppress(Exception):
|
|
362
|
+
self._session.ocs("POST", f"{self._session.ae_url}/log", json={"level": int_log_lvl, "message": content})
|
|
327
363
|
|
|
328
364
|
def users_list(self) -> list[str]:
|
|
329
|
-
"""Returns list of users on the Nextcloud instance.
|
|
365
|
+
"""Returns list of users on the Nextcloud instance."""
|
|
330
366
|
return self._session.ocs("GET", f"{self._session.ae_url}/users")
|
|
331
367
|
|
|
332
368
|
@property
|
|
333
369
|
def user(self) -> str:
|
|
334
370
|
"""Property containing the current user ID.
|
|
335
371
|
|
|
336
|
-
**
|
|
372
|
+
**ExApps** can change user ID they impersonate with **set_user** method.
|
|
337
373
|
"""
|
|
338
374
|
return self._session.user
|
|
339
375
|
|
|
@@ -421,6 +457,10 @@ class AsyncNextcloudApp(_AsyncNextcloudBasic):
|
|
|
421
457
|
"""Nextcloud UI API for ExApps"""
|
|
422
458
|
providers: AsyncProvidersApi
|
|
423
459
|
"""API for registering providers for Nextcloud"""
|
|
460
|
+
events_listener: AsyncEventsListenerAPI
|
|
461
|
+
"""API for registering Events listeners for ExApps"""
|
|
462
|
+
occ_commands: AsyncOccCommandsAPI
|
|
463
|
+
"""API for registering OCC command for ExApps"""
|
|
424
464
|
|
|
425
465
|
def __init__(self, **kwargs):
|
|
426
466
|
"""The parameters will be taken from the environment.
|
|
@@ -433,24 +473,40 @@ class AsyncNextcloudApp(_AsyncNextcloudBasic):
|
|
|
433
473
|
self.preferences_ex = AsyncPreferencesExAPI(self._session)
|
|
434
474
|
self.ui = AsyncUiApi(self._session)
|
|
435
475
|
self.providers = AsyncProvidersApi(self._session)
|
|
476
|
+
self.events_listener = AsyncEventsListenerAPI(self._session)
|
|
477
|
+
self.occ_commands = AsyncOccCommandsAPI(self._session)
|
|
478
|
+
|
|
479
|
+
@property
|
|
480
|
+
async def enabled_state(self) -> bool:
|
|
481
|
+
"""Returns ``True`` if ExApp is enabled, ``False`` otherwise."""
|
|
482
|
+
with contextlib.suppress(Exception):
|
|
483
|
+
return bool(await self._session.ocs("GET", "/ocs/v1.php/apps/app_api/ex-app/state"))
|
|
484
|
+
return False
|
|
436
485
|
|
|
437
|
-
async def log(self, log_lvl: LogLvl, content: str) -> None:
|
|
486
|
+
async def log(self, log_lvl: LogLvl, content: str, fast_send: bool = False) -> None:
|
|
438
487
|
"""Writes log to the Nextcloud log file."""
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
488
|
+
int_log_lvl = int(log_lvl)
|
|
489
|
+
if int_log_lvl < 0 or int_log_lvl > 4:
|
|
490
|
+
raise ValueError("Invalid `log_lvl` value")
|
|
491
|
+
if not fast_send:
|
|
492
|
+
if await self.check_capabilities("app_api"):
|
|
493
|
+
return
|
|
494
|
+
if int_log_lvl < (await self.capabilities)["app_api"].get("loglevel", 0):
|
|
495
|
+
return
|
|
496
|
+
with contextlib.suppress(Exception):
|
|
497
|
+
await self._session.ocs(
|
|
498
|
+
"POST", f"{self._session.ae_url}/log", json={"level": int_log_lvl, "message": content}
|
|
499
|
+
)
|
|
444
500
|
|
|
445
501
|
async def users_list(self) -> list[str]:
|
|
446
|
-
"""Returns list of users on the Nextcloud instance.
|
|
502
|
+
"""Returns list of users on the Nextcloud instance."""
|
|
447
503
|
return await self._session.ocs("GET", f"{self._session.ae_url}/users")
|
|
448
504
|
|
|
449
505
|
@property
|
|
450
506
|
async def user(self) -> str:
|
|
451
507
|
"""Property containing the current user ID.
|
|
452
508
|
|
|
453
|
-
**
|
|
509
|
+
**ExApps** can change user ID they impersonate with **set_user** method.
|
|
454
510
|
"""
|
|
455
511
|
return await self._session.user
|
|
456
512
|
|
nc_py_api/talk_bot.py
CHANGED
|
@@ -29,6 +29,11 @@ class TalkBotMessage:
|
|
|
29
29
|
def __init__(self, raw_data: dict):
|
|
30
30
|
self._raw_data = raw_data
|
|
31
31
|
|
|
32
|
+
@property
|
|
33
|
+
def message_type(self) -> str:
|
|
34
|
+
"""The type of message like Join, Leave, Create, Activity, etc."""
|
|
35
|
+
return self._raw_data["type"]
|
|
36
|
+
|
|
32
37
|
@property
|
|
33
38
|
def actor_id(self) -> str:
|
|
34
39
|
"""One of the attendee types followed by the ``/`` character and a unique identifier within the given type.
|
nc_py_api/users.py
CHANGED
|
@@ -369,8 +369,8 @@ class _AsyncUsersAPI:
|
|
|
369
369
|
|
|
370
370
|
|
|
371
371
|
def _create(user_id: str, display_name: str | None, **kwargs) -> dict[str, typing.Any]:
|
|
372
|
-
password = kwargs.get("password"
|
|
373
|
-
email = kwargs.get("email"
|
|
372
|
+
password = kwargs.get("password")
|
|
373
|
+
email = kwargs.get("email")
|
|
374
374
|
if not password and not email:
|
|
375
375
|
raise ValueError("Either password or email must be set")
|
|
376
376
|
data = {"userid": user_id}
|
|
@@ -378,5 +378,5 @@ def _create(user_id: str, display_name: str | None, **kwargs) -> dict[str, typin
|
|
|
378
378
|
if k in kwargs:
|
|
379
379
|
data[k] = kwargs[k]
|
|
380
380
|
if display_name is not None:
|
|
381
|
-
data["
|
|
381
|
+
data["displayName"] = display_name
|
|
382
382
|
return data
|
nc_py_api/webhooks.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Nextcloud Webhooks API."""
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
|
|
5
|
+
from ._misc import clear_from_params_empty # , require_capabilities
|
|
6
|
+
from ._session import AppConfig, AsyncNcSessionBasic, NcSessionBasic
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclasses.dataclass
|
|
10
|
+
class WebhookInfo:
|
|
11
|
+
"""Information about the Webhook."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, raw_data: dict):
|
|
14
|
+
self._raw_data = raw_data
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def webhook_id(self) -> int:
|
|
18
|
+
"""`ID` of the webhook."""
|
|
19
|
+
return self._raw_data["id"]
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def app_id(self) -> str:
|
|
23
|
+
"""`ID` of the ExApp that registered webhook."""
|
|
24
|
+
return self._raw_data["appId"] if self._raw_data["appId"] else ""
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def user_id(self) -> str:
|
|
28
|
+
"""`UserID` if webhook was registered in user context."""
|
|
29
|
+
return self._raw_data["userId"] if self._raw_data["userId"] else ""
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def http_method(self) -> str:
|
|
33
|
+
"""HTTP method used to call webhook."""
|
|
34
|
+
return self._raw_data["httpMethod"]
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def uri(self) -> str:
|
|
38
|
+
"""URL address that will be called for this webhook."""
|
|
39
|
+
return self._raw_data["uri"]
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def event(self) -> str:
|
|
43
|
+
"""Nextcloud PHP event that triggers this webhook."""
|
|
44
|
+
return self._raw_data["event"]
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def event_filter(self):
|
|
48
|
+
"""Mongo filter to apply to the serialized data to decide if firing."""
|
|
49
|
+
return self._raw_data["eventFilter"]
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def user_id_filter(self) -> str:
|
|
53
|
+
"""Currently unknown."""
|
|
54
|
+
return self._raw_data["userIdFilter"]
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def headers(self) -> dict:
|
|
58
|
+
"""Headers that should be added to request when calling webhook."""
|
|
59
|
+
return self._raw_data["headers"] if self._raw_data["headers"] else {}
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def auth_method(self) -> str:
|
|
63
|
+
"""Currently unknown."""
|
|
64
|
+
return self._raw_data["authMethod"]
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def auth_data(self) -> dict:
|
|
68
|
+
"""Currently unknown."""
|
|
69
|
+
return self._raw_data["authData"] if self._raw_data["authData"] else {}
|
|
70
|
+
|
|
71
|
+
def __repr__(self):
|
|
72
|
+
return f"<{self.__class__.__name__} id={self.webhook_id}, event={self.event}>"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class _WebhooksAPI:
|
|
76
|
+
"""The class provides the application management API on the Nextcloud server."""
|
|
77
|
+
|
|
78
|
+
_ep_base: str = "/ocs/v1.php/apps/webhook_listeners/api/v1/webhooks"
|
|
79
|
+
|
|
80
|
+
def __init__(self, session: NcSessionBasic):
|
|
81
|
+
self._session = session
|
|
82
|
+
|
|
83
|
+
def get_list(self, uri_filter: str = "") -> list[WebhookInfo]:
|
|
84
|
+
params = {"uri": uri_filter} if uri_filter else {}
|
|
85
|
+
return [WebhookInfo(i) for i in self._session.ocs("GET", f"{self._ep_base}", params=params)]
|
|
86
|
+
|
|
87
|
+
def get_entry(self, webhook_id: int) -> WebhookInfo:
|
|
88
|
+
return WebhookInfo(self._session.ocs("GET", f"{self._ep_base}/{webhook_id}"))
|
|
89
|
+
|
|
90
|
+
def register(
|
|
91
|
+
self,
|
|
92
|
+
http_method: str,
|
|
93
|
+
uri: str,
|
|
94
|
+
event: str,
|
|
95
|
+
event_filter: dict | None = None,
|
|
96
|
+
user_id_filter: str = "",
|
|
97
|
+
headers: dict | None = None,
|
|
98
|
+
auth_method: str = "none",
|
|
99
|
+
auth_data: dict | None = None,
|
|
100
|
+
):
|
|
101
|
+
params = {
|
|
102
|
+
"httpMethod": http_method,
|
|
103
|
+
"uri": uri,
|
|
104
|
+
"event": event,
|
|
105
|
+
"eventFilter": event_filter,
|
|
106
|
+
"userIdFilter": user_id_filter,
|
|
107
|
+
"headers": headers,
|
|
108
|
+
"authMethod": auth_method,
|
|
109
|
+
"authData": auth_data,
|
|
110
|
+
}
|
|
111
|
+
clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
|
|
112
|
+
return WebhookInfo(self._session.ocs("POST", f"{self._ep_base}", json=params))
|
|
113
|
+
|
|
114
|
+
def update(
|
|
115
|
+
self,
|
|
116
|
+
webhook_id: int,
|
|
117
|
+
http_method: str,
|
|
118
|
+
uri: str,
|
|
119
|
+
event: str,
|
|
120
|
+
event_filter: dict | None = None,
|
|
121
|
+
user_id_filter: str = "",
|
|
122
|
+
headers: dict | None = None,
|
|
123
|
+
auth_method: str = "none",
|
|
124
|
+
auth_data: dict | None = None,
|
|
125
|
+
):
|
|
126
|
+
params = {
|
|
127
|
+
"id": webhook_id,
|
|
128
|
+
"httpMethod": http_method,
|
|
129
|
+
"uri": uri,
|
|
130
|
+
"event": event,
|
|
131
|
+
"eventFilter": event_filter,
|
|
132
|
+
"userIdFilter": user_id_filter,
|
|
133
|
+
"headers": headers,
|
|
134
|
+
"authMethod": auth_method,
|
|
135
|
+
"authData": auth_data,
|
|
136
|
+
}
|
|
137
|
+
clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
|
|
138
|
+
return WebhookInfo(self._session.ocs("POST", f"{self._ep_base}/{webhook_id}", json=params))
|
|
139
|
+
|
|
140
|
+
def unregister(self, webhook_id: int) -> bool:
|
|
141
|
+
return self._session.ocs("DELETE", f"{self._ep_base}/{webhook_id}")
|
|
142
|
+
|
|
143
|
+
def unregister_all(self, appid: str = "") -> int:
|
|
144
|
+
if not appid and isinstance(self._session.cfg, AppConfig):
|
|
145
|
+
appid = self._session.cfg.app_name
|
|
146
|
+
else:
|
|
147
|
+
raise ValueError("The `appid` parameter cannot be empty for non-ExApp use.")
|
|
148
|
+
return self._session.ocs("DELETE", f"{self._ep_base}/byappid/{appid}")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class _AsyncWebhooksAPI:
|
|
152
|
+
"""The class provides the async application management API on the Nextcloud server."""
|
|
153
|
+
|
|
154
|
+
_ep_base: str = "/ocs/v1.php/webhooks"
|
|
155
|
+
|
|
156
|
+
def __init__(self, session: AsyncNcSessionBasic):
|
|
157
|
+
self._session = session
|
|
158
|
+
|
|
159
|
+
async def get_list(self, uri_filter: str = "") -> list[WebhookInfo]:
|
|
160
|
+
params = {"uri": uri_filter} if uri_filter else {}
|
|
161
|
+
return [WebhookInfo(i) for i in await self._session.ocs("GET", f"{self._ep_base}", params=params)]
|
|
162
|
+
|
|
163
|
+
async def get_entry(self, webhook_id: int) -> WebhookInfo:
|
|
164
|
+
return WebhookInfo(await self._session.ocs("GET", f"{self._ep_base}/{webhook_id}"))
|
|
165
|
+
|
|
166
|
+
async def register(
|
|
167
|
+
self,
|
|
168
|
+
http_method: str,
|
|
169
|
+
uri: str,
|
|
170
|
+
event: str,
|
|
171
|
+
event_filter: dict | None = None,
|
|
172
|
+
user_id_filter: str = "",
|
|
173
|
+
headers: dict | None = None,
|
|
174
|
+
auth_method: str = "none",
|
|
175
|
+
auth_data: dict | None = None,
|
|
176
|
+
):
|
|
177
|
+
params = {
|
|
178
|
+
"httpMethod": http_method,
|
|
179
|
+
"uri": uri,
|
|
180
|
+
"event": event,
|
|
181
|
+
"eventFilter": event_filter,
|
|
182
|
+
"userIdFilter": user_id_filter,
|
|
183
|
+
"headers": headers,
|
|
184
|
+
"authMethod": auth_method,
|
|
185
|
+
"authData": auth_data,
|
|
186
|
+
}
|
|
187
|
+
clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
|
|
188
|
+
return WebhookInfo(await self._session.ocs("POST", f"{self._ep_base}", json=params))
|
|
189
|
+
|
|
190
|
+
async def update(
|
|
191
|
+
self,
|
|
192
|
+
webhook_id: int,
|
|
193
|
+
http_method: str,
|
|
194
|
+
uri: str,
|
|
195
|
+
event: str,
|
|
196
|
+
event_filter: dict | None = None,
|
|
197
|
+
user_id_filter: str = "",
|
|
198
|
+
headers: dict | None = None,
|
|
199
|
+
auth_method: str = "none",
|
|
200
|
+
auth_data: dict | None = None,
|
|
201
|
+
):
|
|
202
|
+
params = {
|
|
203
|
+
"id": webhook_id,
|
|
204
|
+
"httpMethod": http_method,
|
|
205
|
+
"uri": uri,
|
|
206
|
+
"event": event,
|
|
207
|
+
"eventFilter": event_filter,
|
|
208
|
+
"userIdFilter": user_id_filter,
|
|
209
|
+
"headers": headers,
|
|
210
|
+
"authMethod": auth_method,
|
|
211
|
+
"authData": auth_data,
|
|
212
|
+
}
|
|
213
|
+
clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
|
|
214
|
+
return WebhookInfo(await self._session.ocs("POST", f"{self._ep_base}/{webhook_id}", json=params))
|
|
215
|
+
|
|
216
|
+
async def unregister(self, webhook_id: int) -> bool:
|
|
217
|
+
return await self._session.ocs("DELETE", f"{self._ep_base}/{webhook_id}")
|
|
218
|
+
|
|
219
|
+
async def unregister_all(self, appid: str = "") -> int:
|
|
220
|
+
if not appid and isinstance(self._session.cfg, AppConfig):
|
|
221
|
+
appid = self._session.cfg.app_name
|
|
222
|
+
else:
|
|
223
|
+
raise ValueError("The `appid` parameter cannot be empty for non-ExApp use.")
|
|
224
|
+
return await self._session.ocs("DELETE", f"{self._ep_base}/byappid/{appid}")
|