nolag 2.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.
- nolag-2.0.0/PKG-INFO +329 -0
- nolag-2.0.0/README.md +295 -0
- nolag-2.0.0/TEST_AND_DEPLOY.md +0 -0
- nolag-2.0.0/nolag/__init__.py +84 -0
- nolag-2.0.0/nolag/api.py +268 -0
- nolag-2.0.0/nolag/api_types.py +330 -0
- nolag-2.0.0/nolag/client.py +862 -0
- nolag-2.0.0/nolag/types.py +104 -0
- nolag-2.0.0/nolag/webrtc.py +474 -0
- nolag-2.0.0/pyproject.toml +68 -0
- nolag-2.0.0/tests/__init__.py +1 -0
- nolag-2.0.0/tests/e2e/__init__.py +1 -0
- nolag-2.0.0/tests/e2e/test_kraken.py +785 -0
nolag-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nolag
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: NoLag real-time messaging SDK for Python
|
|
5
|
+
Project-URL: Homepage, https://nolag.app
|
|
6
|
+
Project-URL: Documentation, https://docs.nolag.app
|
|
7
|
+
Project-URL: Repository, https://github.com/NoLagApp/nolag-python
|
|
8
|
+
Author-email: NoLag <support@nolag.app>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: messaging,nolag,pubsub,real-time,websocket
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Communications
|
|
20
|
+
Classifier: Topic :: Internet
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
23
|
+
Requires-Dist: msgpack>=1.0.0
|
|
24
|
+
Requires-Dist: websockets>=12.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
31
|
+
Provides-Extra: webrtc
|
|
32
|
+
Requires-Dist: aiortc>=1.6.0; extra == 'webrtc'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# NoLag Python SDK
|
|
36
|
+
|
|
37
|
+
Real-time messaging SDK for Python applications.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install nolag
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Setup
|
|
46
|
+
|
|
47
|
+
Before using the SDK, you need to set up your NoLag project:
|
|
48
|
+
|
|
49
|
+
1. **Create a Project** in the [NoLag Dashboard](https://app.nolag.app)
|
|
50
|
+
2. **Create an App** with your desired topics
|
|
51
|
+
3. **Create Topics** in your App schema (e.g., `messages`, `status`, `commands`)
|
|
52
|
+
4. **Create Actors** to get access tokens (`at_xxx...`)
|
|
53
|
+
|
|
54
|
+
> **Note:** Topics must be defined in your App schema before you can subscribe or publish to them. Rooms are created dynamically at runtime.
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import asyncio
|
|
60
|
+
from nolag import NoLag, NoLagOptions
|
|
61
|
+
|
|
62
|
+
async def main():
|
|
63
|
+
# Create client with your actor token
|
|
64
|
+
client = NoLag("your-actor-token")
|
|
65
|
+
|
|
66
|
+
# Connect to NoLag
|
|
67
|
+
await client.connect()
|
|
68
|
+
|
|
69
|
+
# Subscribe to a topic
|
|
70
|
+
def on_message(data, meta):
|
|
71
|
+
print(f"Received: {data}")
|
|
72
|
+
|
|
73
|
+
await client.subscribe("my-topic", on_message)
|
|
74
|
+
|
|
75
|
+
# Publish a message
|
|
76
|
+
await client.emit("my-topic", {"hello": "world"})
|
|
77
|
+
|
|
78
|
+
# Keep running
|
|
79
|
+
await asyncio.sleep(60)
|
|
80
|
+
|
|
81
|
+
# Disconnect when done
|
|
82
|
+
await client.disconnect()
|
|
83
|
+
|
|
84
|
+
asyncio.run(main())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from nolag import NoLag, NoLagOptions, QoS
|
|
91
|
+
|
|
92
|
+
options = NoLagOptions(
|
|
93
|
+
url="wss://broker.nolag.app/ws", # Custom broker URL
|
|
94
|
+
reconnect=True, # Auto-reconnect on disconnect
|
|
95
|
+
reconnect_interval=5.0, # Seconds between reconnect attempts
|
|
96
|
+
max_reconnect_attempts=10, # Max reconnect attempts (0 = infinite)
|
|
97
|
+
heartbeat_interval=30.0, # Heartbeat interval (0 to disable)
|
|
98
|
+
qos=QoS.AT_LEAST_ONCE, # Default QoS level
|
|
99
|
+
debug=True, # Enable debug logging
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
client = NoLag("your-actor-token", options)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Subscribing to Topics
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from nolag import SubscribeOptions, QoS
|
|
109
|
+
|
|
110
|
+
# Basic subscription
|
|
111
|
+
await client.subscribe("chat/messages", lambda data, meta: print(data))
|
|
112
|
+
|
|
113
|
+
# With options
|
|
114
|
+
options = SubscribeOptions(
|
|
115
|
+
qos=QoS.EXACTLY_ONCE,
|
|
116
|
+
load_balance=True,
|
|
117
|
+
load_balance_group="workers"
|
|
118
|
+
)
|
|
119
|
+
await client.subscribe("tasks", handler, options)
|
|
120
|
+
|
|
121
|
+
# Unsubscribe
|
|
122
|
+
await client.unsubscribe("chat/messages")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Publishing Messages
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from nolag import EmitOptions, QoS
|
|
129
|
+
|
|
130
|
+
# Publish any data (dict, list, string, bytes, etc.)
|
|
131
|
+
await client.emit("chat/messages", {"text": "Hello!"})
|
|
132
|
+
|
|
133
|
+
# With options
|
|
134
|
+
options = EmitOptions(
|
|
135
|
+
qos=QoS.EXACTLY_ONCE,
|
|
136
|
+
retain=True # Retain last message for new subscribers
|
|
137
|
+
)
|
|
138
|
+
await client.emit("status", {"online": True}, options)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Connection Events
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from nolag import ConnectionStatus
|
|
145
|
+
|
|
146
|
+
# Listen for connection events
|
|
147
|
+
client.on("connected", lambda: print("Connected!"))
|
|
148
|
+
client.on("disconnected", lambda: print("Disconnected"))
|
|
149
|
+
client.on("reconnecting", lambda attempt: print(f"Reconnecting... attempt {attempt}"))
|
|
150
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
151
|
+
|
|
152
|
+
# Check connection status
|
|
153
|
+
if client.status == ConnectionStatus.CONNECTED:
|
|
154
|
+
print("We're connected!")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Presence
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
# Set your presence data
|
|
161
|
+
await client.set_presence({"status": "online", "typing": False})
|
|
162
|
+
|
|
163
|
+
# Get presence of all actors in a topic
|
|
164
|
+
presence_list = await client.get_presence("chat/room-1")
|
|
165
|
+
for actor in presence_list:
|
|
166
|
+
print(f"{actor.actor_token_id}: {actor.presence}")
|
|
167
|
+
|
|
168
|
+
# Listen for presence changes
|
|
169
|
+
client.on("presence", lambda topic, presence:
|
|
170
|
+
print(f"Presence update in {topic}: {presence}")
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Error Handling
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
import asyncio
|
|
178
|
+
from nolag import NoLag
|
|
179
|
+
|
|
180
|
+
async def main():
|
|
181
|
+
client = NoLag("your-actor-token")
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
await client.connect()
|
|
185
|
+
except Exception as e:
|
|
186
|
+
print(f"Connection failed: {e}")
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
# Handle errors during operation
|
|
190
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
await client.emit("topic", {"data": "value"})
|
|
194
|
+
except Exception as e:
|
|
195
|
+
print(f"Emit failed: {e}")
|
|
196
|
+
|
|
197
|
+
asyncio.run(main())
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## QoS Levels
|
|
201
|
+
|
|
202
|
+
| Level | Name | Description |
|
|
203
|
+
|-------|------|-------------|
|
|
204
|
+
| 0 | AT_MOST_ONCE | Fire and forget, no acknowledgment |
|
|
205
|
+
| 1 | AT_LEAST_ONCE | Guaranteed delivery, may have duplicates |
|
|
206
|
+
| 2 | EXACTLY_ONCE | Guaranteed exactly one delivery |
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from nolag import QoS
|
|
210
|
+
|
|
211
|
+
# Set default QoS in options
|
|
212
|
+
options = NoLagOptions(qos=QoS.EXACTLY_ONCE)
|
|
213
|
+
|
|
214
|
+
# Or per-message
|
|
215
|
+
await client.emit("important", data, EmitOptions(qos=QoS.EXACTLY_ONCE))
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Load Balancing
|
|
219
|
+
|
|
220
|
+
Distribute messages across multiple subscribers:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
# Enable load balancing for a subscription
|
|
224
|
+
await client.subscribe(
|
|
225
|
+
"tasks",
|
|
226
|
+
process_task,
|
|
227
|
+
SubscribeOptions(
|
|
228
|
+
load_balance=True,
|
|
229
|
+
load_balance_group="task-workers"
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Type Definitions
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from nolag import (
|
|
238
|
+
NoLag, # Main client class
|
|
239
|
+
NoLagOptions, # Connection options
|
|
240
|
+
SubscribeOptions, # Subscription options
|
|
241
|
+
EmitOptions, # Publish options
|
|
242
|
+
ConnectionStatus, # Connection status enum
|
|
243
|
+
ActorType, # Actor type enum
|
|
244
|
+
QoS, # QoS level enum
|
|
245
|
+
MessageMeta, # Message metadata
|
|
246
|
+
ActorPresence, # Presence info
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## REST API Client
|
|
251
|
+
|
|
252
|
+
The SDK also includes a REST API client for managing apps, rooms, and actors.
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
import asyncio
|
|
256
|
+
from nolag import NoLagApi, AppCreate, RoomCreate, ActorCreate
|
|
257
|
+
|
|
258
|
+
async def main():
|
|
259
|
+
# Create API client with project-scoped API key
|
|
260
|
+
async with NoLagApi("nlg_live_xxx.secret") as api:
|
|
261
|
+
# List all apps in your project
|
|
262
|
+
apps = await api.apps.list()
|
|
263
|
+
print(f"Found {len(apps.data)} apps")
|
|
264
|
+
|
|
265
|
+
# Create a new app
|
|
266
|
+
app = await api.apps.create(AppCreate(
|
|
267
|
+
name="my-chat-app",
|
|
268
|
+
description="A real-time chat application"
|
|
269
|
+
))
|
|
270
|
+
print(f"Created app: {app.app_id}")
|
|
271
|
+
|
|
272
|
+
# Create a room in the app
|
|
273
|
+
room = await api.rooms.create(app.app_id, RoomCreate(
|
|
274
|
+
name="general",
|
|
275
|
+
slug="general",
|
|
276
|
+
description="General chat room"
|
|
277
|
+
))
|
|
278
|
+
print(f"Created room: {room.room_id}")
|
|
279
|
+
|
|
280
|
+
# Create an actor (IMPORTANT: save the access token!)
|
|
281
|
+
actor = await api.actors.create(ActorCreate(
|
|
282
|
+
name="web-client",
|
|
283
|
+
actor_type="device"
|
|
284
|
+
))
|
|
285
|
+
print(f"Actor token (save this!): {actor.access_token}")
|
|
286
|
+
|
|
287
|
+
# Update an app
|
|
288
|
+
updated = await api.apps.update(app.app_id, AppUpdate(
|
|
289
|
+
description="Updated description"
|
|
290
|
+
))
|
|
291
|
+
|
|
292
|
+
# Delete resources
|
|
293
|
+
await api.rooms.delete(app.app_id, room.room_id)
|
|
294
|
+
await api.actors.delete(actor.actor_token_id)
|
|
295
|
+
await api.apps.delete(app.app_id)
|
|
296
|
+
|
|
297
|
+
asyncio.run(main())
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### API Types
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from nolag import (
|
|
304
|
+
# API Client
|
|
305
|
+
NoLagApi, # REST API client
|
|
306
|
+
NoLagApiError, # API error class
|
|
307
|
+
NoLagApiOptions, # API client options
|
|
308
|
+
|
|
309
|
+
# Resources
|
|
310
|
+
App, AppCreate, AppUpdate,
|
|
311
|
+
Room, RoomCreate, RoomUpdate,
|
|
312
|
+
Actor, ActorWithToken, ActorCreate, ActorUpdate,
|
|
313
|
+
|
|
314
|
+
# Utilities
|
|
315
|
+
ListOptions, # Pagination options
|
|
316
|
+
PaginatedResult, # Paginated response
|
|
317
|
+
)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Requirements
|
|
321
|
+
|
|
322
|
+
- Python 3.10+
|
|
323
|
+
- websockets >= 12.0
|
|
324
|
+
- msgpack >= 1.0.0
|
|
325
|
+
- aiohttp >= 3.9.0
|
|
326
|
+
|
|
327
|
+
## License
|
|
328
|
+
|
|
329
|
+
MIT
|
nolag-2.0.0/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# NoLag Python SDK
|
|
2
|
+
|
|
3
|
+
Real-time messaging SDK for Python applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install nolag
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Before using the SDK, you need to set up your NoLag project:
|
|
14
|
+
|
|
15
|
+
1. **Create a Project** in the [NoLag Dashboard](https://app.nolag.app)
|
|
16
|
+
2. **Create an App** with your desired topics
|
|
17
|
+
3. **Create Topics** in your App schema (e.g., `messages`, `status`, `commands`)
|
|
18
|
+
4. **Create Actors** to get access tokens (`at_xxx...`)
|
|
19
|
+
|
|
20
|
+
> **Note:** Topics must be defined in your App schema before you can subscribe or publish to them. Rooms are created dynamically at runtime.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import asyncio
|
|
26
|
+
from nolag import NoLag, NoLagOptions
|
|
27
|
+
|
|
28
|
+
async def main():
|
|
29
|
+
# Create client with your actor token
|
|
30
|
+
client = NoLag("your-actor-token")
|
|
31
|
+
|
|
32
|
+
# Connect to NoLag
|
|
33
|
+
await client.connect()
|
|
34
|
+
|
|
35
|
+
# Subscribe to a topic
|
|
36
|
+
def on_message(data, meta):
|
|
37
|
+
print(f"Received: {data}")
|
|
38
|
+
|
|
39
|
+
await client.subscribe("my-topic", on_message)
|
|
40
|
+
|
|
41
|
+
# Publish a message
|
|
42
|
+
await client.emit("my-topic", {"hello": "world"})
|
|
43
|
+
|
|
44
|
+
# Keep running
|
|
45
|
+
await asyncio.sleep(60)
|
|
46
|
+
|
|
47
|
+
# Disconnect when done
|
|
48
|
+
await client.disconnect()
|
|
49
|
+
|
|
50
|
+
asyncio.run(main())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from nolag import NoLag, NoLagOptions, QoS
|
|
57
|
+
|
|
58
|
+
options = NoLagOptions(
|
|
59
|
+
url="wss://broker.nolag.app/ws", # Custom broker URL
|
|
60
|
+
reconnect=True, # Auto-reconnect on disconnect
|
|
61
|
+
reconnect_interval=5.0, # Seconds between reconnect attempts
|
|
62
|
+
max_reconnect_attempts=10, # Max reconnect attempts (0 = infinite)
|
|
63
|
+
heartbeat_interval=30.0, # Heartbeat interval (0 to disable)
|
|
64
|
+
qos=QoS.AT_LEAST_ONCE, # Default QoS level
|
|
65
|
+
debug=True, # Enable debug logging
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
client = NoLag("your-actor-token", options)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Subscribing to Topics
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from nolag import SubscribeOptions, QoS
|
|
75
|
+
|
|
76
|
+
# Basic subscription
|
|
77
|
+
await client.subscribe("chat/messages", lambda data, meta: print(data))
|
|
78
|
+
|
|
79
|
+
# With options
|
|
80
|
+
options = SubscribeOptions(
|
|
81
|
+
qos=QoS.EXACTLY_ONCE,
|
|
82
|
+
load_balance=True,
|
|
83
|
+
load_balance_group="workers"
|
|
84
|
+
)
|
|
85
|
+
await client.subscribe("tasks", handler, options)
|
|
86
|
+
|
|
87
|
+
# Unsubscribe
|
|
88
|
+
await client.unsubscribe("chat/messages")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Publishing Messages
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from nolag import EmitOptions, QoS
|
|
95
|
+
|
|
96
|
+
# Publish any data (dict, list, string, bytes, etc.)
|
|
97
|
+
await client.emit("chat/messages", {"text": "Hello!"})
|
|
98
|
+
|
|
99
|
+
# With options
|
|
100
|
+
options = EmitOptions(
|
|
101
|
+
qos=QoS.EXACTLY_ONCE,
|
|
102
|
+
retain=True # Retain last message for new subscribers
|
|
103
|
+
)
|
|
104
|
+
await client.emit("status", {"online": True}, options)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Connection Events
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from nolag import ConnectionStatus
|
|
111
|
+
|
|
112
|
+
# Listen for connection events
|
|
113
|
+
client.on("connected", lambda: print("Connected!"))
|
|
114
|
+
client.on("disconnected", lambda: print("Disconnected"))
|
|
115
|
+
client.on("reconnecting", lambda attempt: print(f"Reconnecting... attempt {attempt}"))
|
|
116
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
117
|
+
|
|
118
|
+
# Check connection status
|
|
119
|
+
if client.status == ConnectionStatus.CONNECTED:
|
|
120
|
+
print("We're connected!")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Presence
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Set your presence data
|
|
127
|
+
await client.set_presence({"status": "online", "typing": False})
|
|
128
|
+
|
|
129
|
+
# Get presence of all actors in a topic
|
|
130
|
+
presence_list = await client.get_presence("chat/room-1")
|
|
131
|
+
for actor in presence_list:
|
|
132
|
+
print(f"{actor.actor_token_id}: {actor.presence}")
|
|
133
|
+
|
|
134
|
+
# Listen for presence changes
|
|
135
|
+
client.on("presence", lambda topic, presence:
|
|
136
|
+
print(f"Presence update in {topic}: {presence}")
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Error Handling
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
import asyncio
|
|
144
|
+
from nolag import NoLag
|
|
145
|
+
|
|
146
|
+
async def main():
|
|
147
|
+
client = NoLag("your-actor-token")
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
await client.connect()
|
|
151
|
+
except Exception as e:
|
|
152
|
+
print(f"Connection failed: {e}")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# Handle errors during operation
|
|
156
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
await client.emit("topic", {"data": "value"})
|
|
160
|
+
except Exception as e:
|
|
161
|
+
print(f"Emit failed: {e}")
|
|
162
|
+
|
|
163
|
+
asyncio.run(main())
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## QoS Levels
|
|
167
|
+
|
|
168
|
+
| Level | Name | Description |
|
|
169
|
+
|-------|------|-------------|
|
|
170
|
+
| 0 | AT_MOST_ONCE | Fire and forget, no acknowledgment |
|
|
171
|
+
| 1 | AT_LEAST_ONCE | Guaranteed delivery, may have duplicates |
|
|
172
|
+
| 2 | EXACTLY_ONCE | Guaranteed exactly one delivery |
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from nolag import QoS
|
|
176
|
+
|
|
177
|
+
# Set default QoS in options
|
|
178
|
+
options = NoLagOptions(qos=QoS.EXACTLY_ONCE)
|
|
179
|
+
|
|
180
|
+
# Or per-message
|
|
181
|
+
await client.emit("important", data, EmitOptions(qos=QoS.EXACTLY_ONCE))
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Load Balancing
|
|
185
|
+
|
|
186
|
+
Distribute messages across multiple subscribers:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# Enable load balancing for a subscription
|
|
190
|
+
await client.subscribe(
|
|
191
|
+
"tasks",
|
|
192
|
+
process_task,
|
|
193
|
+
SubscribeOptions(
|
|
194
|
+
load_balance=True,
|
|
195
|
+
load_balance_group="task-workers"
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Type Definitions
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from nolag import (
|
|
204
|
+
NoLag, # Main client class
|
|
205
|
+
NoLagOptions, # Connection options
|
|
206
|
+
SubscribeOptions, # Subscription options
|
|
207
|
+
EmitOptions, # Publish options
|
|
208
|
+
ConnectionStatus, # Connection status enum
|
|
209
|
+
ActorType, # Actor type enum
|
|
210
|
+
QoS, # QoS level enum
|
|
211
|
+
MessageMeta, # Message metadata
|
|
212
|
+
ActorPresence, # Presence info
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## REST API Client
|
|
217
|
+
|
|
218
|
+
The SDK also includes a REST API client for managing apps, rooms, and actors.
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
import asyncio
|
|
222
|
+
from nolag import NoLagApi, AppCreate, RoomCreate, ActorCreate
|
|
223
|
+
|
|
224
|
+
async def main():
|
|
225
|
+
# Create API client with project-scoped API key
|
|
226
|
+
async with NoLagApi("nlg_live_xxx.secret") as api:
|
|
227
|
+
# List all apps in your project
|
|
228
|
+
apps = await api.apps.list()
|
|
229
|
+
print(f"Found {len(apps.data)} apps")
|
|
230
|
+
|
|
231
|
+
# Create a new app
|
|
232
|
+
app = await api.apps.create(AppCreate(
|
|
233
|
+
name="my-chat-app",
|
|
234
|
+
description="A real-time chat application"
|
|
235
|
+
))
|
|
236
|
+
print(f"Created app: {app.app_id}")
|
|
237
|
+
|
|
238
|
+
# Create a room in the app
|
|
239
|
+
room = await api.rooms.create(app.app_id, RoomCreate(
|
|
240
|
+
name="general",
|
|
241
|
+
slug="general",
|
|
242
|
+
description="General chat room"
|
|
243
|
+
))
|
|
244
|
+
print(f"Created room: {room.room_id}")
|
|
245
|
+
|
|
246
|
+
# Create an actor (IMPORTANT: save the access token!)
|
|
247
|
+
actor = await api.actors.create(ActorCreate(
|
|
248
|
+
name="web-client",
|
|
249
|
+
actor_type="device"
|
|
250
|
+
))
|
|
251
|
+
print(f"Actor token (save this!): {actor.access_token}")
|
|
252
|
+
|
|
253
|
+
# Update an app
|
|
254
|
+
updated = await api.apps.update(app.app_id, AppUpdate(
|
|
255
|
+
description="Updated description"
|
|
256
|
+
))
|
|
257
|
+
|
|
258
|
+
# Delete resources
|
|
259
|
+
await api.rooms.delete(app.app_id, room.room_id)
|
|
260
|
+
await api.actors.delete(actor.actor_token_id)
|
|
261
|
+
await api.apps.delete(app.app_id)
|
|
262
|
+
|
|
263
|
+
asyncio.run(main())
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### API Types
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from nolag import (
|
|
270
|
+
# API Client
|
|
271
|
+
NoLagApi, # REST API client
|
|
272
|
+
NoLagApiError, # API error class
|
|
273
|
+
NoLagApiOptions, # API client options
|
|
274
|
+
|
|
275
|
+
# Resources
|
|
276
|
+
App, AppCreate, AppUpdate,
|
|
277
|
+
Room, RoomCreate, RoomUpdate,
|
|
278
|
+
Actor, ActorWithToken, ActorCreate, ActorUpdate,
|
|
279
|
+
|
|
280
|
+
# Utilities
|
|
281
|
+
ListOptions, # Pagination options
|
|
282
|
+
PaginatedResult, # Paginated response
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Requirements
|
|
287
|
+
|
|
288
|
+
- Python 3.10+
|
|
289
|
+
- websockets >= 12.0
|
|
290
|
+
- msgpack >= 1.0.0
|
|
291
|
+
- aiohttp >= 3.9.0
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT
|
|
Binary file
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NoLag Python SDK
|
|
3
|
+
Real-time messaging for Python applications
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .client import NoLag
|
|
7
|
+
from .types import (
|
|
8
|
+
NoLagOptions,
|
|
9
|
+
ConnectionStatus,
|
|
10
|
+
ActorType,
|
|
11
|
+
QoS,
|
|
12
|
+
SubscribeOptions,
|
|
13
|
+
EmitOptions,
|
|
14
|
+
MessageMeta,
|
|
15
|
+
ActorPresence,
|
|
16
|
+
LobbyPresenceEvent,
|
|
17
|
+
LobbyPresenceState,
|
|
18
|
+
LobbyPresenceHandler,
|
|
19
|
+
)
|
|
20
|
+
from .api import NoLagApi, NoLagApiError
|
|
21
|
+
from .api_types import (
|
|
22
|
+
NoLagApiOptions,
|
|
23
|
+
ListOptions,
|
|
24
|
+
PaginatedResult,
|
|
25
|
+
ApiError,
|
|
26
|
+
App,
|
|
27
|
+
AppCreate,
|
|
28
|
+
AppUpdate,
|
|
29
|
+
Room,
|
|
30
|
+
RoomCreate,
|
|
31
|
+
RoomUpdate,
|
|
32
|
+
Actor,
|
|
33
|
+
ActorWithToken,
|
|
34
|
+
ActorCreate,
|
|
35
|
+
ActorUpdate,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# WebRTC support (optional, requires aiortc)
|
|
39
|
+
try:
|
|
40
|
+
from .webrtc import WebRTCManager, WebRTCOptions, is_webrtc_available
|
|
41
|
+
_WEBRTC_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
_WEBRTC_AVAILABLE = False
|
|
44
|
+
WebRTCManager = None
|
|
45
|
+
WebRTCOptions = None
|
|
46
|
+
is_webrtc_available = lambda: False
|
|
47
|
+
|
|
48
|
+
__version__ = "2.0.0"
|
|
49
|
+
__all__ = [
|
|
50
|
+
# WebSocket Client
|
|
51
|
+
"NoLag",
|
|
52
|
+
"NoLagOptions",
|
|
53
|
+
"ConnectionStatus",
|
|
54
|
+
"ActorType",
|
|
55
|
+
"QoS",
|
|
56
|
+
"SubscribeOptions",
|
|
57
|
+
"EmitOptions",
|
|
58
|
+
"MessageMeta",
|
|
59
|
+
"ActorPresence",
|
|
60
|
+
"LobbyPresenceEvent",
|
|
61
|
+
"LobbyPresenceState",
|
|
62
|
+
"LobbyPresenceHandler",
|
|
63
|
+
# REST API Client
|
|
64
|
+
"NoLagApi",
|
|
65
|
+
"NoLagApiError",
|
|
66
|
+
"NoLagApiOptions",
|
|
67
|
+
"ListOptions",
|
|
68
|
+
"PaginatedResult",
|
|
69
|
+
"ApiError",
|
|
70
|
+
"App",
|
|
71
|
+
"AppCreate",
|
|
72
|
+
"AppUpdate",
|
|
73
|
+
"Room",
|
|
74
|
+
"RoomCreate",
|
|
75
|
+
"RoomUpdate",
|
|
76
|
+
"Actor",
|
|
77
|
+
"ActorWithToken",
|
|
78
|
+
"ActorCreate",
|
|
79
|
+
"ActorUpdate",
|
|
80
|
+
# WebRTC (optional)
|
|
81
|
+
"WebRTCManager",
|
|
82
|
+
"WebRTCOptions",
|
|
83
|
+
"is_webrtc_available",
|
|
84
|
+
]
|