wirexaapi 1.0.0__tar.gz
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.
- wirexaapi-1.0.0/PKG-INFO +86 -0
- wirexaapi-1.0.0/README.md +64 -0
- wirexaapi-1.0.0/pyproject.toml +40 -0
- wirexaapi-1.0.0/setup.cfg +4 -0
- wirexaapi-1.0.0/wirexaapi/__init__.py +187 -0
- wirexaapi-1.0.0/wirexaapi.egg-info/PKG-INFO +86 -0
- wirexaapi-1.0.0/wirexaapi.egg-info/SOURCES.txt +8 -0
- wirexaapi-1.0.0/wirexaapi.egg-info/dependency_links.txt +1 -0
- wirexaapi-1.0.0/wirexaapi.egg-info/requires.txt +4 -0
- wirexaapi-1.0.0/wirexaapi.egg-info/top_level.txt +1 -0
wirexaapi-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wirexaapi
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Wirexa API SDK - SignalWire/Twilio compatible voice API client
|
|
5
|
+
Author-email: Wirexa Team <dev@wirexa.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/wirexa/wirexa-api
|
|
8
|
+
Project-URL: Repository, https://github.com/wirexa/wirexa-api
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests>=2.25.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
|
|
23
|
+
# Wirexa API SDK
|
|
24
|
+
|
|
25
|
+
A production-ready Python SDK for Wirexa API - a self-hosted telephony platform compatible with SignalWire and Twilio REST API.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install wirexaapi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from wirexaapi import Client
|
|
37
|
+
|
|
38
|
+
# Connect to your Wirexa API instance
|
|
39
|
+
client = Client(
|
|
40
|
+
project_id="your-project-id",
|
|
41
|
+
api_token="your-api-token",
|
|
42
|
+
signalwire_space_url="your-space.example.com:5000"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Make a call
|
|
46
|
+
call = client.calls.create(
|
|
47
|
+
to="+573001234567",
|
|
48
|
+
from_="+18005550100",
|
|
49
|
+
url="https://your-backend.com/xml",
|
|
50
|
+
method="POST",
|
|
51
|
+
timeout=28,
|
|
52
|
+
record=True,
|
|
53
|
+
status_callback="https://your-backend.com/status",
|
|
54
|
+
status_callback_event=["initiated", "ringing", "completed"],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
print(f"Call SID: {call.sid}")
|
|
58
|
+
print(f"Call Status: {call.status}")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Migration from SignalWire
|
|
62
|
+
|
|
63
|
+
If you're migrating from SignalWire SDK:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Before
|
|
67
|
+
from signalwire.rest import Client as SignalWireClient
|
|
68
|
+
|
|
69
|
+
# After
|
|
70
|
+
from wirexaapi import Client as SignalWireClient
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The rest of your code stays the same!
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- Full SignalWire/Twilio API compatibility
|
|
78
|
+
- Call creation, retrieval, and listing
|
|
79
|
+
- Machine Detection (AMD)
|
|
80
|
+
- Recording
|
|
81
|
+
- Status callbacks
|
|
82
|
+
- Async AMD support
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Wirexa API SDK
|
|
2
|
+
|
|
3
|
+
A production-ready Python SDK for Wirexa API - a self-hosted telephony platform compatible with SignalWire and Twilio REST API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install wirexaapi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from wirexaapi import Client
|
|
15
|
+
|
|
16
|
+
# Connect to your Wirexa API instance
|
|
17
|
+
client = Client(
|
|
18
|
+
project_id="your-project-id",
|
|
19
|
+
api_token="your-api-token",
|
|
20
|
+
signalwire_space_url="your-space.example.com:5000"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Make a call
|
|
24
|
+
call = client.calls.create(
|
|
25
|
+
to="+573001234567",
|
|
26
|
+
from_="+18005550100",
|
|
27
|
+
url="https://your-backend.com/xml",
|
|
28
|
+
method="POST",
|
|
29
|
+
timeout=28,
|
|
30
|
+
record=True,
|
|
31
|
+
status_callback="https://your-backend.com/status",
|
|
32
|
+
status_callback_event=["initiated", "ringing", "completed"],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
print(f"Call SID: {call.sid}")
|
|
36
|
+
print(f"Call Status: {call.status}")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Migration from SignalWire
|
|
40
|
+
|
|
41
|
+
If you're migrating from SignalWire SDK:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
# Before
|
|
45
|
+
from signalwire.rest import Client as SignalWireClient
|
|
46
|
+
|
|
47
|
+
# After
|
|
48
|
+
from wirexaapi import Client as SignalWireClient
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The rest of your code stays the same!
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- Full SignalWire/Twilio API compatibility
|
|
56
|
+
- Call creation, retrieval, and listing
|
|
57
|
+
- Machine Detection (AMD)
|
|
58
|
+
- Recording
|
|
59
|
+
- Status callbacks
|
|
60
|
+
- Async AMD support
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wirexaapi"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Wirexa API SDK - SignalWire/Twilio compatible voice API client"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Wirexa Team", email = "dev@wirexa.io"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.8",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"requests>=2.25.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/wirexa/wirexa-api"
|
|
31
|
+
Repository = "https://github.com/wirexa/wirexa-api"
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=7.0.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
where = ["."]
|
|
40
|
+
include = ["wirexaapi*"]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Wirexa API SDK - Drop-in replacement del SDK de SignalWire/Twilio.
|
|
3
|
+
|
|
4
|
+
CREDENCIALES (las obtienes del dashboard):
|
|
5
|
+
space_url = "your-space.example.com:5000"
|
|
6
|
+
project_id = "your-project-id"
|
|
7
|
+
api_token = "your-api-token"
|
|
8
|
+
|
|
9
|
+
USO:
|
|
10
|
+
|
|
11
|
+
ANTES (SignalWire original):
|
|
12
|
+
from signalwire.rest import Client
|
|
13
|
+
client = Client(project_id, api_token, signalwire_space_url=space_url)
|
|
14
|
+
|
|
15
|
+
DESPUÉS (Wirexa API):
|
|
16
|
+
from wirexaapi import Client
|
|
17
|
+
client = Client(project_id, api_token, signalwire_space_url=space_url)
|
|
18
|
+
|
|
19
|
+
El resto del código NO cambia.
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import requests
|
|
25
|
+
from typing import Optional
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("wirexaapi.http_client")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _CallsResource:
|
|
31
|
+
"""Imita el objeto client.calls del SDK."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, base_url: str, auth: tuple):
|
|
34
|
+
self._base = base_url
|
|
35
|
+
self._auth = auth
|
|
36
|
+
|
|
37
|
+
def create(
|
|
38
|
+
self,
|
|
39
|
+
to: str,
|
|
40
|
+
from_: str,
|
|
41
|
+
url: str,
|
|
42
|
+
method: str = "POST",
|
|
43
|
+
timeout: int = 28,
|
|
44
|
+
record: bool = False,
|
|
45
|
+
status_callback: Optional[str] = None,
|
|
46
|
+
status_callback_method: str = "POST",
|
|
47
|
+
status_callback_event: Optional[list] = None,
|
|
48
|
+
recording_status_callback: Optional[str] = None,
|
|
49
|
+
recording_status_callback_method: str = "POST",
|
|
50
|
+
machine_detection: Optional[str] = None,
|
|
51
|
+
machine_detection_timeout: int = 30,
|
|
52
|
+
async_amd: bool = False,
|
|
53
|
+
async_amd_status_callback: Optional[str] = None,
|
|
54
|
+
) -> "_CallInstance":
|
|
55
|
+
data = {
|
|
56
|
+
"To": to,
|
|
57
|
+
"From": from_,
|
|
58
|
+
"Url": url,
|
|
59
|
+
"Method": method,
|
|
60
|
+
"Timeout": str(timeout),
|
|
61
|
+
"Record": "true" if record else "false",
|
|
62
|
+
"StatusCallbackMethod": status_callback_method,
|
|
63
|
+
"RecordingStatusCallbackMethod": recording_status_callback_method,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if status_callback:
|
|
67
|
+
data["StatusCallback"] = status_callback
|
|
68
|
+
|
|
69
|
+
if status_callback_event:
|
|
70
|
+
data["StatusCallbackEvent"] = " ".join(status_callback_event)
|
|
71
|
+
|
|
72
|
+
if recording_status_callback:
|
|
73
|
+
data["RecordingStatusCallback"] = recording_status_callback
|
|
74
|
+
|
|
75
|
+
if machine_detection:
|
|
76
|
+
data["MachineDetection"] = machine_detection
|
|
77
|
+
data["MachineDetectionTimeout"] = str(machine_detection_timeout)
|
|
78
|
+
|
|
79
|
+
if async_amd:
|
|
80
|
+
data["AsyncAmd"] = "true"
|
|
81
|
+
if async_amd_status_callback:
|
|
82
|
+
data["AsyncAmdStatusCallback"] = async_amd_status_callback
|
|
83
|
+
|
|
84
|
+
endpoint = f"{self._base}/Calls.json"
|
|
85
|
+
|
|
86
|
+
logger.info("Request: POST %s", endpoint)
|
|
87
|
+
logger.info("Params: %s", data)
|
|
88
|
+
|
|
89
|
+
r = requests.post(endpoint, auth=self._auth, data=data, timeout=30)
|
|
90
|
+
|
|
91
|
+
logger.info("Response: HTTP %s", r.status_code)
|
|
92
|
+
logger.info("Response Body: %s", r.text[:500])
|
|
93
|
+
|
|
94
|
+
r.raise_for_status()
|
|
95
|
+
instance = _CallInstance(r.json())
|
|
96
|
+
|
|
97
|
+
logger.info("[calls.create] Call %s status: %s", instance.sid, instance.status)
|
|
98
|
+
|
|
99
|
+
return instance
|
|
100
|
+
|
|
101
|
+
def list(self, status: Optional[str] = None, limit: int = 50) -> list:
|
|
102
|
+
params = {"PageSize": limit}
|
|
103
|
+
if status:
|
|
104
|
+
params["Status"] = status
|
|
105
|
+
r = requests.get(f"{self._base}/Calls.json", auth=self._auth, params=params, timeout=10)
|
|
106
|
+
r.raise_for_status()
|
|
107
|
+
return [_CallInstance(c) for c in r.json().get("calls", [])]
|
|
108
|
+
|
|
109
|
+
def __call__(self, call_sid: str) -> "_CallContext":
|
|
110
|
+
return _CallContext(self._base, self._auth, call_sid)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class _CallContext:
|
|
114
|
+
"""Imita client.calls('CAxxxxx')"""
|
|
115
|
+
|
|
116
|
+
def __init__(self, base_url: str, auth: tuple, call_sid: str):
|
|
117
|
+
self._base = base_url
|
|
118
|
+
self._auth = auth
|
|
119
|
+
self._sid = call_sid
|
|
120
|
+
|
|
121
|
+
def fetch(self) -> "_CallInstance":
|
|
122
|
+
r = requests.get(f"{self._base}/Calls/{self._sid}.json", auth=self._auth, timeout=10)
|
|
123
|
+
r.raise_for_status()
|
|
124
|
+
return _CallInstance(r.json())
|
|
125
|
+
|
|
126
|
+
def update(self, status: Optional[str] = None, url: Optional[str] = None) -> "_CallInstance":
|
|
127
|
+
data = {}
|
|
128
|
+
if status:
|
|
129
|
+
data["Status"] = status
|
|
130
|
+
if url:
|
|
131
|
+
data["Url"] = url
|
|
132
|
+
r = requests.post(f"{self._base}/Calls/{self._sid}.json", auth=self._auth, data=data, timeout=10)
|
|
133
|
+
r.raise_for_status()
|
|
134
|
+
return _CallInstance(r.json())
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class _CallInstance:
|
|
138
|
+
"""Objeto retornado por calls.create() / fetch()."""
|
|
139
|
+
|
|
140
|
+
def __init__(self, data: dict):
|
|
141
|
+
self._data = data
|
|
142
|
+
self.sid = data.get("sid", "")
|
|
143
|
+
self.status = data.get("status", "")
|
|
144
|
+
self.to = data.get("to", "")
|
|
145
|
+
self.from_ = data.get("from", "")
|
|
146
|
+
self.direction = data.get("direction", "")
|
|
147
|
+
self.duration = data.get("duration")
|
|
148
|
+
self.answered_by = data.get("answered_by")
|
|
149
|
+
self.date_created = data.get("date_created")
|
|
150
|
+
|
|
151
|
+
def __repr__(self):
|
|
152
|
+
return f"<Call sid={self.sid!r} status={self.status!r}>"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class Client:
|
|
156
|
+
"""Cliente principal para Wirexa API.
|
|
157
|
+
|
|
158
|
+
Uso:
|
|
159
|
+
client = Client(
|
|
160
|
+
project_id,
|
|
161
|
+
api_token,
|
|
162
|
+
signalwire_space_url="your-space.example.com:5000"
|
|
163
|
+
)
|
|
164
|
+
call = client.calls.create(to=..., from_=..., url=...)
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
project_id: str,
|
|
170
|
+
api_token: str,
|
|
171
|
+
signalwire_space_url: str = "localhost:5000",
|
|
172
|
+
):
|
|
173
|
+
self.project_id = project_id
|
|
174
|
+
self.api_token = api_token
|
|
175
|
+
self.space_url = signalwire_space_url
|
|
176
|
+
|
|
177
|
+
protocol = "https" if "." in signalwire_space_url and ":" not in signalwire_space_url else "http"
|
|
178
|
+
self._base_url = (
|
|
179
|
+
f"{protocol}://{signalwire_space_url}"
|
|
180
|
+
f"/api/laml/2010-04-01/Accounts/{project_id}"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self._auth = (project_id, api_token)
|
|
184
|
+
self.calls = _CallsResource(self._base_url, self._auth)
|
|
185
|
+
|
|
186
|
+
def __repr__(self):
|
|
187
|
+
return f"<WirexaClient project={self.project_id!r} space={self.space_url!r}>"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wirexaapi
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Wirexa API SDK - SignalWire/Twilio compatible voice API client
|
|
5
|
+
Author-email: Wirexa Team <dev@wirexa.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/wirexa/wirexa-api
|
|
8
|
+
Project-URL: Repository, https://github.com/wirexa/wirexa-api
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests>=2.25.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
|
|
23
|
+
# Wirexa API SDK
|
|
24
|
+
|
|
25
|
+
A production-ready Python SDK for Wirexa API - a self-hosted telephony platform compatible with SignalWire and Twilio REST API.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install wirexaapi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from wirexaapi import Client
|
|
37
|
+
|
|
38
|
+
# Connect to your Wirexa API instance
|
|
39
|
+
client = Client(
|
|
40
|
+
project_id="your-project-id",
|
|
41
|
+
api_token="your-api-token",
|
|
42
|
+
signalwire_space_url="your-space.example.com:5000"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Make a call
|
|
46
|
+
call = client.calls.create(
|
|
47
|
+
to="+573001234567",
|
|
48
|
+
from_="+18005550100",
|
|
49
|
+
url="https://your-backend.com/xml",
|
|
50
|
+
method="POST",
|
|
51
|
+
timeout=28,
|
|
52
|
+
record=True,
|
|
53
|
+
status_callback="https://your-backend.com/status",
|
|
54
|
+
status_callback_event=["initiated", "ringing", "completed"],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
print(f"Call SID: {call.sid}")
|
|
58
|
+
print(f"Call Status: {call.status}")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Migration from SignalWire
|
|
62
|
+
|
|
63
|
+
If you're migrating from SignalWire SDK:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Before
|
|
67
|
+
from signalwire.rest import Client as SignalWireClient
|
|
68
|
+
|
|
69
|
+
# After
|
|
70
|
+
from wirexaapi import Client as SignalWireClient
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The rest of your code stays the same!
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- Full SignalWire/Twilio API compatibility
|
|
78
|
+
- Call creation, retrieval, and listing
|
|
79
|
+
- Machine Detection (AMD)
|
|
80
|
+
- Recording
|
|
81
|
+
- Status callbacks
|
|
82
|
+
- Async AMD support
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
wirexaapi
|