Spaces:
Runtime error
Runtime error
File size: 5,652 Bytes
46252cd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | """OpenWA Python SDK β client core.
The :class:`OpenWAClient` is the single entry point. It owns an
:class:`HttpExecutor` (which wraps :class:`httpx.Client` with an injectable
transport) and exposes domain resources as properties::
from openwa import OpenWAClient
client = OpenWAClient(
base_url="http://localhost:2785",
api_key="owa_k1_β¦",
)
client.sessions.start("my-session")
client.messages.send_text("my-session", {
"chatId": "628123456789@c.us",
"text": "Hello from the OpenWA SDK!",
})
Pass ``transport=httpx.MockTransport(handler)`` for testability β no global
monkey-patching required.
"""
from __future__ import annotations
import warnings
from types import TracebackType
from typing import Any, Mapping
from urllib.parse import urlparse
import httpx
from ._http import HttpExecutor, HttpMethod
from .resources import (
CallsResource,
CatalogResource,
ChannelsResource,
ChatsResource,
ContactsResource,
GroupsResource,
HealthResource,
LabelsResource,
MessagesResource,
ProfileResource,
SearchResource,
SessionsResource,
StatusResource,
TemplatesResource,
WebhooksResource,
)
from .types import AuthValidateResponse
_LOCALHOST_HOSTS = {"localhost", "127.0.0.1", "::1"}
def _warn_if_insecure_http(url: str) -> None:
"""Warn (not raise) when base_url is http:// and the host is not localhost.
The API key is sent as an X-API-Key header on every request β over plaintext http
to a non-local host that's cleartext on the wire. Warning (not refusing) keeps local
dev and TLS-terminating-proxy topologies working.
"""
try:
parsed = urlparse(url)
host = (parsed.hostname or "").strip("[]")
if parsed.scheme == "http" and host not in _LOCALHOST_HOSTS:
warnings.warn(
f"OpenWAClient: base_url uses an insecure http:// URL (host: {host}). "
"The API key will be sent in cleartext. Use https:// in production.",
stacklevel=3,
)
except Exception:
pass
class OpenWAClient:
def __init__(
self,
base_url: str,
api_key: str,
*,
timeout: float = 30.0,
default_headers: Mapping[str, str] | None = None,
transport: httpx.BaseTransport | None = None,
) -> None:
if not base_url:
raise ValueError("OpenWAClient: base_url is required")
if not api_key:
raise ValueError("OpenWAClient: api_key is required")
_warn_if_insecure_http(base_url)
self._http = HttpExecutor(
base_url=base_url,
api_key=api_key,
timeout=timeout,
default_headers=default_headers,
transport=transport,
)
# ββ Resources ββββββββββββββββββββββββββββββββββββββββββββββββββββ
@property
def sessions(self) -> SessionsResource:
return SessionsResource(self._http)
@property
def messages(self) -> MessagesResource:
return MessagesResource(self._http)
@property
def contacts(self) -> ContactsResource:
return ContactsResource(self._http)
@property
def groups(self) -> GroupsResource:
return GroupsResource(self._http)
@property
def webhooks(self) -> WebhooksResource:
return WebhooksResource(self._http)
@property
def chats(self) -> ChatsResource:
return ChatsResource(self._http)
@property
def status(self) -> StatusResource:
return StatusResource(self._http)
@property
def health(self) -> HealthResource:
return HealthResource(self._http)
@property
def labels(self) -> LabelsResource:
return LabelsResource(self._http)
@property
def channels(self) -> ChannelsResource:
return ChannelsResource(self._http)
@property
def catalog(self) -> CatalogResource:
return CatalogResource(self._http)
@property
def templates(self) -> TemplatesResource:
return TemplatesResource(self._http)
@property
def search(self) -> SearchResource:
return SearchResource(self._http)
@property
def profile(self) -> ProfileResource:
return ProfileResource(self._http)
@property
def calls(self) -> CallsResource:
return CallsResource(self._http)
# ββ Auth βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def auth(self) -> AuthValidateResponse:
return self._http.request("POST", "/api/auth/validate")
# ββ Raw request escape hatch βββββββββββββββββββββββββββββββββββββ
def request(
self,
method: str,
path: str,
*,
query: Mapping[str, Any] | None = None,
body: Any = None,
) -> Any:
"""Issue a raw request against the API (advanced use). ``path`` begins with ``/``."""
return self._http.request(method, path, query=query, body=body)
# ββ Lifecycle ββββββββββββββββββββββββββββββββββββββββββββββββββββ
def close(self) -> None:
self._http.close()
def __enter__(self) -> "OpenWAClient":
return self
def __exit__(self, *exc: Any) -> None:
self.close()
|