mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-11-14 16:17:18 +01:00
fix: remote dialogs and rest
This commit is contained in:
1 parent
901612c3f6
commit
4fd2d3d3be
18 files changed
+2118
-173
No files matched your search
@@ -21,6 +21,8 @@ from urllib.parse import quote, urlencode, urlsplit
|
||||
from .errors import DonutConnectionError, DonutError, error_for_status
|
||||
from .models import (
|
||||
AgentClick,
|
||||
AgentDialogAnswer,
|
||||
AgentHold,
|
||||
AgentTyping,
|
||||
ApiBulkCreateResponse,
|
||||
ApiGroupResponse,
|
||||
@@ -57,6 +59,7 @@ from .models import (
|
||||
ImportProxiesResponse,
|
||||
LocatorDescription,
|
||||
LocatorResolution,
|
||||
PageDialog,
|
||||
PerceptionPage,
|
||||
PickedElement,
|
||||
ProfileImportBatchResult,
|
||||
@@ -666,6 +669,32 @@ class DonutClient:
|
||||
body=_body(locator=dict(locator), button=button, click_count=click_count),
|
||||
)
|
||||
|
||||
def agent_hold(
|
||||
self,
|
||||
profile_id: str,
|
||||
*,
|
||||
locator: Optional[LocatorDescription] = None,
|
||||
selector: Optional[str] = None,
|
||||
duration_ms: Optional[float] = None,
|
||||
button: Optional[str] = None,
|
||||
) -> AgentHold:
|
||||
"""POST /v1/profiles/{id}/agent/hold
|
||||
|
||||
Name the element with ``locator`` or ``selector``. ``duration_ms`` is
|
||||
100 to 30000, default 3000; ``button`` is ``"left"`` (the default),
|
||||
``"middle"`` or ``"right"``.
|
||||
"""
|
||||
return self._request(
|
||||
"POST",
|
||||
f"/v1/profiles/{_segment(profile_id)}/agent/hold",
|
||||
body=_body(
|
||||
locator=dict(locator) if locator is not None else None,
|
||||
selector=selector,
|
||||
duration_ms=duration_ms,
|
||||
button=button,
|
||||
),
|
||||
)
|
||||
|
||||
def agent_type(
|
||||
self,
|
||||
profile_id: str,
|
||||
@@ -739,6 +768,34 @@ class DonutClient:
|
||||
body=_body(timeout_ms=timeout_ms),
|
||||
)
|
||||
|
||||
def agent_dialog(self, profile_id: str) -> PageDialog:
|
||||
"""GET /v1/profiles/{id}/agent/dialog
|
||||
|
||||
Whether a native dialog holds the page, and which. Never refused for
|
||||
the dialog: while one is open, every other agent call raises
|
||||
``DIALOG_OPEN``.
|
||||
"""
|
||||
return self._request("GET", f"/v1/profiles/{_segment(profile_id)}/agent/dialog")
|
||||
|
||||
def agent_handle_dialog(
|
||||
self,
|
||||
profile_id: str,
|
||||
*,
|
||||
accept: bool,
|
||||
prompt_text: Optional[str] = None,
|
||||
) -> AgentDialogAnswer:
|
||||
"""POST /v1/profiles/{id}/agent/handle-dialog
|
||||
|
||||
``accept`` true is OK, false is Cancel. An accepted prompt takes
|
||||
``prompt_text``, or keeps its default text. Raises ``NO_DIALOG_OPEN``
|
||||
when nothing is open.
|
||||
"""
|
||||
return self._request(
|
||||
"POST",
|
||||
f"/v1/profiles/{_segment(profile_id)}/agent/handle-dialog",
|
||||
body=_body(accept=accept, prompt_text=prompt_text),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Remote sessions
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -69,7 +69,10 @@ OPERATIONS: Dict[Operation, str] = {
|
||||
("GET", "/v1/profiles/{id}/fingerprint"): "get_profile_fingerprint",
|
||||
("PUT", "/v1/profiles/{id}"): "update_profile",
|
||||
("POST", "/v1/profiles/{id}/agent/click"): "agent_click",
|
||||
("GET", "/v1/profiles/{id}/agent/dialog"): "agent_dialog",
|
||||
("POST", "/v1/profiles/{id}/agent/extract"): "agent_extract",
|
||||
("POST", "/v1/profiles/{id}/agent/handle-dialog"): "agent_handle_dialog",
|
||||
("POST", "/v1/profiles/{id}/agent/hold"): "agent_hold",
|
||||
("POST", "/v1/profiles/{id}/agent/perceive"): "agent_perceive",
|
||||
("POST", "/v1/profiles/{id}/agent/pick"): "agent_pick",
|
||||
("POST", "/v1/profiles/{id}/agent/resolve-locator"): "agent_resolve_locator",
|
||||
|
||||
@@ -9,8 +9,8 @@ Two spellings live side by side because the app sends both. Most bodies are
|
||||
snake_case; the browser-facing agent types (``LocatorDescription``,
|
||||
``LocatorCandidate``, ``PerceptionPage`` and friends) carry the browser's own
|
||||
camelCase, because they are handed through from the browser rather than
|
||||
restated. ``AgentClick`` and ``AgentTyping`` are the exceptions inside the
|
||||
agent surface: they are snake_case with a single ``match`` key. The types below
|
||||
restated. ``AgentClick``, ``AgentHold`` and ``AgentTyping`` are the exceptions
|
||||
inside the agent surface: they are snake_case with a single ``match`` key. The types below
|
||||
follow the wire rather than tidying it, so a value read from one call can be
|
||||
passed straight into the next.
|
||||
"""
|
||||
@@ -94,7 +94,11 @@ __all__ = [
|
||||
"Extraction",
|
||||
"PickedElement",
|
||||
"AgentClick",
|
||||
"AgentHold",
|
||||
"AgentTyping",
|
||||
"OpenDialog",
|
||||
"PageDialog",
|
||||
"AgentDialogAnswer",
|
||||
]
|
||||
|
||||
# The app's own JSON for a proxy's settings. Declared `Object` in the OpenAPI
|
||||
@@ -931,3 +935,64 @@ class AgentTyping(_AgentTypingRequired, total=False):
|
||||
"""
|
||||
|
||||
corrections: int
|
||||
|
||||
|
||||
class _AgentHoldRequired(TypedDict):
|
||||
held: bool
|
||||
engine: str
|
||||
navigated: bool
|
||||
button: str
|
||||
held_ms: int
|
||||
|
||||
|
||||
class AgentHold(_AgentHoldRequired, total=False):
|
||||
"""What a press and hold did. Note the snake_case body and the ``match`` key.
|
||||
|
||||
``match`` is the element a locator settled on, absent when a selector named
|
||||
it. ``held_ms`` is how long the button was down.
|
||||
"""
|
||||
|
||||
match: LocatorCandidate
|
||||
|
||||
|
||||
class OpenDialog(TypedDict):
|
||||
"""A native dialog (alert, confirm, prompt, "leave this page?") on a page.
|
||||
|
||||
``type`` is ``alert``, ``confirm``, ``prompt`` or ``beforeunload``;
|
||||
``default_prompt`` is the text a prompt puts in its field; ``url`` is the
|
||||
page that opened it.
|
||||
"""
|
||||
|
||||
type: str
|
||||
message: str
|
||||
default_prompt: str
|
||||
url: str
|
||||
|
||||
|
||||
class _PageDialogRequired(TypedDict):
|
||||
open: Optional[bool]
|
||||
|
||||
|
||||
class PageDialog(_PageDialogRequired, total=False):
|
||||
"""Whether a native dialog holds the page.
|
||||
|
||||
``open`` is true while a dialog holds the page, false when none does, and
|
||||
``None`` when the page answers nothing and no dialog was seen opening on
|
||||
it; ``page_responding`` is then false. A navigation ends that.
|
||||
"""
|
||||
|
||||
dialog: OpenDialog
|
||||
page_responding: bool
|
||||
note: str
|
||||
|
||||
|
||||
class _AgentDialogAnswerRequired(TypedDict):
|
||||
accepted: bool
|
||||
dialog: OpenDialog
|
||||
|
||||
|
||||
class AgentDialogAnswer(_AgentDialogAnswerRequired, total=False):
|
||||
"""What answering a dialog did. ``next`` is a dialog the page opened in
|
||||
answer, which holds the page now."""
|
||||
|
||||
next: OpenDialog
|
||||
@@ -365,6 +365,36 @@ CASES: List[Case] = [
|
||||
{},
|
||||
"/v1/profiles/{id}/agent/pick",
|
||||
),
|
||||
(
|
||||
"agent_hold",
|
||||
("p1",),
|
||||
{"selector": "#press", "duration_ms": 4000.0},
|
||||
"POST",
|
||||
"/v1/profiles/p1/agent/hold",
|
||||
{"selector": "#press", "duration_ms": 4000.0},
|
||||
{},
|
||||
"/v1/profiles/{id}/agent/hold",
|
||||
),
|
||||
(
|
||||
"agent_dialog",
|
||||
("p1",),
|
||||
{},
|
||||
"GET",
|
||||
"/v1/profiles/p1/agent/dialog",
|
||||
None,
|
||||
{},
|
||||
"/v1/profiles/{id}/agent/dialog",
|
||||
),
|
||||
(
|
||||
"agent_handle_dialog",
|
||||
("p1",),
|
||||
{"accept": True, "prompt_text": "Bob"},
|
||||
"POST",
|
||||
"/v1/profiles/p1/agent/handle-dialog",
|
||||
{"accept": True, "prompt_text": "Bob"},
|
||||
{},
|
||||
"/v1/profiles/{id}/agent/handle-dialog",
|
||||
),
|
||||
# -- remote sessions ---------------------------------------------------
|
||||
(
|
||||
"list_remote_sessions",
|
||||
|
||||
Reference in new issue
Block a user