kryten-robot 0.6.9__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.
@@ -0,0 +1,469 @@
1
+ Metadata-Version: 2.4
2
+ Name: kryten-robot
3
+ Version: 0.6.9
4
+ Summary: CyTube to NATS bridge connector - publishes CyTube events to NATS message bus
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: cytube,nats,bridge,connector,socketio,microservices
8
+ Author: Kryten Robot Team
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Communications :: Chat
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: System :: Distributed Computing
22
+ Requires-Dist: aiohttp (>=3.9.0)
23
+ Requires-Dist: nats-py (>=2.9.0,<3.0.0)
24
+ Requires-Dist: psutil (>=5.9.0,<6.0.0)
25
+ Requires-Dist: pydantic (>=2.0.0,<3.0.0)
26
+ Requires-Dist: pydantic-settings (>=2.0.0,<3.0.0)
27
+ Requires-Dist: python-socketio[asyncio-client] (>=5.11.0)
28
+ Requires-Dist: websockets (>=12.0)
29
+ Project-URL: Documentation, https://github.com/grobertson/kryten-robot/blob/main/README.md
30
+ Project-URL: Homepage, https://github.com/grobertson/kryten-robot
31
+ Project-URL: Repository, https://github.com/grobertson/kryten-robot
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Kryten-Robot
35
+
36
+ [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
37
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
38
+ [![PyPI Version](https://img.shields.io/pypi/v/kryten-robot.svg)](https://pypi.org/project/kryten-robot/)
39
+
40
+ **Kryten-Robot** is a CyTube to NATS bridge connector that connects to CyTube chat servers via Socket.IO and publishes all events to a NATS message bus. This enables building distributed microservice architectures around CyTube channels.
41
+
42
+ ## Overview
43
+
44
+ Kryten-Robot acts as the central bridge between CyTube and your microservices:
45
+
46
+ - **Connects** to CyTube servers via Socket.IO
47
+ - **Publishes** all CyTube events to NATS with structured subjects
48
+ - **Subscribes** to command subjects to control CyTube
49
+ - **Maintains** connection health with automatic reconnection
50
+ - **Tracks** channel state (users, playlist, emotes)
51
+ - **Exposes** state query API via NATS request/reply
52
+
53
+ ## Architecture
54
+
55
+ ```
56
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
57
+ │ CyTube │◄───────►│ Kryten-Robot │◄───────►│ NATS Server │
58
+ │ Server │ Socket │ (Bridge) │ Pub/ │ │
59
+ └─────────────┘ .IO └──────────────┘ Sub └─────────────────┘
60
+
61
+
62
+ ┌─────────────────────────────────────┴──────────┐
63
+ │ │
64
+ ┌─────▼──────┐ ┌──────────────┐ ┌──────────────┐
65
+ │ kryten-cli │ │kryten- │ │ Your Custom │
66
+ │ (Control) │ │userstats │ │ Microservice │
67
+ └────────────┘ └──────────────┘ └──────────────┘
68
+ ```
69
+
70
+ ## Features
71
+
72
+ - ✅ **Full CyTube Event Coverage**: All Socket.IO events published to NATS
73
+ - ✅ **Unified Command Pattern**: Single subject per service (`kryten.robot.command`)
74
+ - ✅ **State Management**: Real-time tracking of users, playlist, emotes
75
+ - ✅ **State Query API**: Request/reply for current channel state
76
+ - ✅ **Connection Resilience**: Automatic reconnection with exponential backoff
77
+ - ✅ **Health Monitoring**: HTTP health endpoint for orchestration
78
+ - ✅ **Correlation IDs**: Distributed tracing support
79
+ - ✅ **Structured Logging**: JSON logs with correlation context
80
+
81
+ ## Installation
82
+
83
+ ### From PyPI
84
+
85
+ ```bash
86
+ pip install kryten-robot
87
+ ```
88
+
89
+ ### As systemd Service (Linux)
90
+
91
+ For production deployments on Linux with systemd:
92
+
93
+ ```bash
94
+ # Clone the repository
95
+ git clone https://github.com/grobertson/kryten-robot.git
96
+ cd kryten-robot
97
+
98
+ # Run installation script (requires root)
99
+ sudo bash install.sh
100
+ ```
101
+
102
+ The installer will:
103
+ - Create system user and directories
104
+ - Set up Python virtual environment
105
+ - Install kryten-robot from PyPI
106
+ - Configure systemd service
107
+ - Create example config file
108
+
109
+ See `systemd/README.md` for detailed setup instructions.
110
+
111
+ ### From Source
112
+
113
+ ```bash
114
+ git clone https://github.com/grobertson/kryten-robot.git
115
+ cd kryten-robot
116
+ pip install -e .
117
+ ```
118
+
119
+ ### With Poetry
120
+
121
+ ```bash
122
+ poetry add kryten-robot
123
+ ```
124
+
125
+ ## Quick Start
126
+
127
+ ### 1. Create Configuration File
128
+
129
+ Create `config.json`:
130
+
131
+ ```json
132
+ {
133
+ "cytube": {
134
+ "domain": "cytu.be",
135
+ "channel": "your-channel",
136
+ "user": "your-bot-username",
137
+ "password": "your-bot-password"
138
+ },
139
+ "nats": {
140
+ "servers": ["nats://localhost:4222"],
141
+ "user": null,
142
+ "password": null,
143
+ "max_reconnect_attempts": 60,
144
+ "reconnect_time_wait": 2
145
+ },
146
+ "health": {
147
+ "enabled": true,
148
+ "host": "0.0.0.0",
149
+ "port": 28080
150
+ },
151
+ "commands": {
152
+ "enabled": true
153
+ },
154
+ "logging": {
155
+ "format": "text",
156
+ "correlation_id": true
157
+ },
158
+ "log_level": "INFO"
159
+ }
160
+ ```
161
+
162
+ ### 2. Run Kryten-Robot
163
+
164
+ ```bash
165
+ # Using the installed command
166
+ kryten-robot config.json
167
+
168
+ # Or with Python module
169
+ python -m kryten config.json
170
+
171
+ # With custom log level
172
+ kryten-robot config.json --log-level DEBUG
173
+ ```
174
+
175
+ ### 3. Verify Operation
176
+
177
+ Check health endpoint:
178
+ ```bash
179
+ curl http://localhost:28080/health
180
+ ```
181
+
182
+ Expected response:
183
+ ```json
184
+ {
185
+ "status": "healthy",
186
+ "cytube_connected": true,
187
+ "nats_connected": true,
188
+ "channel": "your-channel",
189
+ "uptime_seconds": 42.5
190
+ }
191
+ ```
192
+
193
+ ## NATS Subject Structure
194
+
195
+ ### Event Publishing
196
+
197
+ All CyTube events are published to:
198
+ ```
199
+ kryten.events.cytube.{channel}.{event_name}
200
+ ```
201
+
202
+ Examples:
203
+ - `kryten.events.cytube.420grindhouse.chatmsg` - Chat messages
204
+ - `kryten.events.cytube.420grindhouse.adduser` - User joins
205
+ - `kryten.events.cytube.420grindhouse.userleave` - User leaves
206
+ - `kryten.events.cytube.420grindhouse.changeMedia` - Video changes
207
+
208
+ ### Command Subscription
209
+
210
+ Kryten-Robot accepts commands on:
211
+ ```
212
+ kryten.robot.command
213
+ ```
214
+
215
+ Command payload:
216
+ ```json
217
+ {
218
+ "service": "robot",
219
+ "command": "state.userlist",
220
+ "correlation_id": "optional-trace-id"
221
+ }
222
+ ```
223
+
224
+ Available commands:
225
+ - `state.emotes` - Get all channel emotes
226
+ - `state.playlist` - Get current playlist
227
+ - `state.userlist` - Get all users in channel
228
+ - `state.user` - Get specific user info (requires `username` param)
229
+ - `state.profiles` - Get all user profiles
230
+ - `state.all` - Get complete channel state
231
+ - `system.health` - Get system health status
232
+ - `system.channels` - Get list of connected channels
233
+ - `system.version` - Get Kryten-Robot version
234
+
235
+ ## Configuration Reference
236
+
237
+ ### CyTube Section
238
+
239
+ | Field | Type | Required | Description |
240
+ |-------|------|----------|-------------|
241
+ | `domain` | string | Yes | CyTube server domain (e.g., "cytu.be") |
242
+ | `channel` | string | Yes | Channel name to connect to |
243
+ | `user` | string | Yes | Bot account username |
244
+ | `password` | string | Yes | Bot account password |
245
+
246
+ ### NATS Section
247
+
248
+ | Field | Type | Required | Default | Description |
249
+ |-------|------|----------|---------|-------------|
250
+ | `servers` | array | Yes | - | NATS server URLs |
251
+ | `user` | string | No | null | NATS authentication user |
252
+ | `password` | string | No | null | NATS authentication password |
253
+ | `max_reconnect_attempts` | int | No | 60 | Max reconnection attempts |
254
+ | `reconnect_time_wait` | int | No | 2 | Seconds between reconnect attempts |
255
+
256
+ ### Health Section
257
+
258
+ | Field | Type | Required | Default | Description |
259
+ |-------|------|----------|---------|-------------|
260
+ | `enabled` | bool | No | true | Enable HTTP health endpoint |
261
+ | `host` | string | No | "0.0.0.0" | Health endpoint bind address |
262
+ | `port` | int | No | 28080 | Health endpoint port |
263
+
264
+ ### Commands Section
265
+
266
+ | Field | Type | Required | Default | Description |
267
+ |-------|------|----------|---------|-------------|
268
+ | `enabled` | bool | No | true | Enable command subscriber |
269
+
270
+ ### Logging Section
271
+
272
+ | Field | Type | Required | Default | Description |
273
+ |-------|------|----------|---------|-------------|
274
+ | `format` | string | No | "text" | Log format: "text" or "json" |
275
+ | `correlation_id` | bool | No | true | Include correlation IDs |
276
+
277
+ ### Root Level
278
+
279
+ | Field | Type | Required | Default | Description |
280
+ |-------|------|----------|---------|-------------|
281
+ | `log_level` | string | No | "INFO" | Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL |
282
+
283
+ ## Running as a Service
284
+
285
+ ### Systemd (Linux)
286
+
287
+ See [systemd/README.md](systemd/README.md) for complete systemd service configuration.
288
+
289
+ Quick setup:
290
+ ```bash
291
+ sudo cp systemd/kryten-robot.service /etc/systemd/system/
292
+ sudo systemctl daemon-reload
293
+ sudo systemctl enable kryten-robot
294
+ sudo systemctl start kryten-robot
295
+ ```
296
+
297
+ ### Windows Service
298
+
299
+ Use NSSM (Non-Sucking Service Manager):
300
+ ```powershell
301
+ nssm install kryten-robot "C:\Python311\python.exe" "-m kryten config.json"
302
+ nssm set kryten-robot AppDirectory "C:\opt\kryten-robot"
303
+ nssm start kryten-robot
304
+ ```
305
+
306
+ ## Development
307
+
308
+ ### Setup Development Environment
309
+
310
+ ```bash
311
+ git clone https://github.com/grobertson/kryten-robot.git
312
+ cd kryten-robot
313
+
314
+ # Create virtual environment
315
+ python -m venv .venv
316
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
317
+
318
+ # Install in editable mode with dev dependencies
319
+ pip install -e ".[dev]"
320
+ ```
321
+
322
+ ### Running Tests
323
+
324
+ ```bash
325
+ pytest
326
+ ```
327
+
328
+ With coverage:
329
+ ```bash
330
+ pytest --cov=kryten --cov-report=html
331
+ ```
332
+
333
+ ### Code Quality
334
+
335
+ ```bash
336
+ # Format code
337
+ black kryten/
338
+
339
+ # Lint
340
+ ruff check kryten/
341
+
342
+ # Type checking
343
+ mypy kryten/
344
+ ```
345
+
346
+ ## Integration Examples
347
+
348
+ ### Using kryten-py Library
349
+
350
+ ```python
351
+ from kryten import KrytenClient, KrytenConfig
352
+
353
+ config = KrytenConfig.from_json("config.json")
354
+
355
+ async with KrytenClient(config) as client:
356
+ # Query current userlist
357
+ response = await client.nats_request(
358
+ "kryten.robot.command",
359
+ {"service": "robot", "command": "state.userlist"}
360
+ )
361
+
362
+ if response["success"]:
363
+ users = response["data"]["users"]
364
+ print(f"Users online: {len(users)}")
365
+ ```
366
+
367
+ ### Using kryten-cli
368
+
369
+ ```bash
370
+ # Get current playlist
371
+ kryten list queue
372
+
373
+ # Get all users
374
+ kryten list users
375
+
376
+ # Get channel emotes
377
+ kryten list emotes
378
+ ```
379
+
380
+ ## Architecture Documentation
381
+
382
+ - **[KRYTEN_ARCHITECTURE.md](KRYTEN_ARCHITECTURE.md)** - System architecture and patterns
383
+ - **[STATE_QUERY_IMPLEMENTATION.md](STATE_QUERY_IMPLEMENTATION.md)** - State query API details
384
+ - **[LIFECYCLE_EVENTS.md](LIFECYCLE_EVENTS.md)** - Lifecycle event handling
385
+ - **[AUDIT_LOGGING.md](AUDIT_LOGGING.md)** - Audit logging implementation
386
+
387
+ ## Troubleshooting
388
+
389
+ ### Connection Issues
390
+
391
+ **Problem**: Kryten-Robot won't connect to CyTube
392
+ - Verify credentials in config.json
393
+ - Check if channel name is correct
394
+ - Ensure CyTube server is accessible
395
+
396
+ **Problem**: NATS connection failures
397
+ - Verify NATS server is running: `nats-server -v`
398
+ - Check NATS server URL in config
399
+ - Test NATS connectivity: `nats-cli pub test "hello"`
400
+
401
+ ### Performance Issues
402
+
403
+ **Problem**: High memory usage
404
+ - Check for excessive event backlog
405
+ - Verify NATS consumers are processing events
406
+ - Monitor with health endpoint: `curl http://localhost:28080/health`
407
+
408
+ **Problem**: Events not being published
409
+ - Check log level is INFO or DEBUG
410
+ - Verify NATS subjects with `nats-cli sub "kryten.events.>"`
411
+ - Check correlation logs for event flow
412
+
413
+ ## Requirements
414
+
415
+ - Python 3.11 or higher
416
+ - NATS server 2.9.0 or higher
417
+ - CyTube server (any version with Socket.IO support)
418
+
419
+ ## Related Projects
420
+
421
+ - **[kryten-py](https://github.com/grobertson/kryten-py)** - Python library for building Kryten microservices
422
+ - **[kryten-userstats](https://github.com/grobertson/kryten-userstats)** - User statistics tracking service
423
+ - **[kryten-cli](https://github.com/grobertson/kryten-cli)** - Command-line control interface
424
+
425
+ ## Contributing
426
+
427
+ Contributions welcome! Please:
428
+
429
+ 1. Fork the repository
430
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
431
+ 3. Make your changes with tests
432
+ 4. Run tests and linting (`pytest && black . && ruff check .`)
433
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
434
+ 6. Push to branch (`git push origin feature/amazing-feature`)
435
+ 7. Open a Pull Request
436
+
437
+ ## License
438
+
439
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
440
+
441
+ ## Support
442
+
443
+ - **Issues**: [GitHub Issues](https://github.com/grobertson/kryten-robot/issues)
444
+ - **Documentation**: [GitHub Repository](https://github.com/grobertson/kryten-robot)
445
+
446
+ ## Changelog
447
+
448
+ ### v0.5.0 (2025-12-08)
449
+
450
+ **Major Changes:**
451
+ - ✅ Unified command pattern: All services now use `kryten.{service}.command`
452
+ - ✅ State query API: Request/reply interface for channel state
453
+ - ✅ Fixed startup banner bug with wildcard normalization
454
+ - ✅ PyPI packaging: Now installable via `pip install kryten-robot`
455
+
456
+ **API Updates:**
457
+ - Changed: Command subjects from `kryten.commands.cytube.*` to `kryten.robot.command`
458
+ - Added: State query commands (state.emotes, state.playlist, etc.)
459
+ - Added: System health command via NATS
460
+
461
+ **Documentation:**
462
+ - Added: KRYTEN_ARCHITECTURE.md with comprehensive architecture overview
463
+ - Updated: All examples to use unified command pattern
464
+ - Added: PyPI publication workflow
465
+
466
+ ---
467
+
468
+ **Built with ❤️ for the CyTube community**
469
+
@@ -0,0 +1,32 @@
1
+ kryten/__init__.py,sha256=wmWGGCaEunygK-ZKnxLn0vtLraw4_eAv3d4LxePDZ-g,3296
2
+ kryten/__main__.py,sha256=jfGeDvGegP6QeaC9U1tgeDP8FF2di2Sbbauj3GA0cyQ,35784
3
+ kryten/application_state.py,sha256=qDfa5SSCGJ_eBUZAvK_dzKPb6kbGmlrD_19DaGD-P9s,3667
4
+ kryten/audit_logger.py,sha256=pVmztGyh28G7VhBUsz4SkX1BKvHK5SlFnguKxnfeLlM,8395
5
+ kryten/command_subscriber.py,sha256=sb-GVXfbsLKGPNpSzwSD_Pj51sGkbU345XnSBPpoUa8,15069
6
+ kryten/config.example.json,sha256=Bu62yNSKngYR_KZOaBBxyGpmoC02wf05qP2_KaQi0_o,818
7
+ kryten/CONFIG.md,sha256=XE8k8jh0lKlWlQS4s8S0lpTh18lg7Yd8OrJGojUB174,12205
8
+ kryten/config.py,sha256=VRe-i-pkltgHXd-a_v3gvLUJJ9LXrVKoBPV4FvCB_Vo,16966
9
+ kryten/connection_watchdog.py,sha256=bv23gIX3lo1vDMYFUgXdhbyxu6lKoTUz63nT0IxdIv4,6748
10
+ kryten/correlation.py,sha256=Wnq5YgTazV5hKjrKyWUf2YZUmNciYuyRZWzeopdbJkc,7745
11
+ kryten/cytube_connector.py,sha256=JZRquOuKYL6nKtCRXuz-zHH-zBcLrBSQ56LkDMK5kJg,27812
12
+ kryten/cytube_event_sender.py,sha256=z5E_pvaKyE_u-k3ajr5pJb3rivo20FftP3y2bKvQroQ,49796
13
+ kryten/errors.py,sha256=LJW2JoO5whjoLaaXw1g2-yh_ZfVoQTkDLE8itZzsl2Q,4619
14
+ kryten/event_publisher.py,sha256=WN7LGUxnKT5ilz6JXg-Y3faputOjDBV0oDYbKNMr78E,16135
15
+ kryten/health_monitor.py,sha256=eHp2MgT3HwiOMUutUB_XllmK2q1Co6zZ1ZVriSediMA,17527
16
+ kryten/lifecycle_events.py,sha256=68ujG7TrQCW0TAT3f4NM7oBCRUYNRaxPXFl2U2giUwg,10273
17
+ kryten/logging_config.py,sha256=wEruMpptebKwSFo3-YO2lIzpyB_oh74KKtmAis8AH98,10254
18
+ kryten/nats_client.py,sha256=X-SGlEh1swG6O__KYNWeBeWUNP16AbQFlPnwhMCycfY,15464
19
+ kryten/raw_event.py,sha256=1CM48e-kD2y2Y5B0q_bnQRbN6URuOzbglcRZYOZ2Fkk,5170
20
+ kryten/service_registry.py,sha256=CzQkL6t-XPN1FGvwe5jh3HZ0NcnEPaCV5EElxi19ow4,14334
21
+ kryten/shutdown_handler.py,sha256=UDRpbPzGdtWQ2e1592D6_LJ5atdxGC7zTCJ-rBSWz2s,14197
22
+ kryten/socket_io.py,sha256=3TOl2GblEOIT60b_EFYQgrvWo3LjucrIYnw529mb02s,28334
23
+ kryten/state_manager.py,sha256=PNAmlhdJ7Z7ibkMeAMNhN8_Wa3Nz1YOPsqHshWNGkGg,23439
24
+ kryten/state_query_handler.py,sha256=tv9E3QoCgB_C1-6WtRnNMqrF1q1LWq5eeCEtx5LhMBw,26897
25
+ kryten/state_updater.py,sha256=xXW5Dhc2tY1gQVRCgxWW5X2H2RQIWAATsXczQulrTz0,10977
26
+ kryten/stats_tracker.py,sha256=8w-Ybg03wrkCJnJCyXNjnVwQjlqdw3UBtGeVkcj1A0A,3459
27
+ kryten/subject_builder.py,sha256=eVsAoIjO1G5ko9xQHFU4iKBXukYXlsk_t88FxOUj5Ig,11193
28
+ kryten_robot-0.6.9.dist-info/entry_points.txt,sha256=MTAs-YdfHJ2N2wu5elqvwPuDb6PKM8NuFRxU1PFkmi4,52
29
+ kryten_robot-0.6.9.dist-info/licenses/LICENSE,sha256=4-aY9N48r38gULWwGnXZB0cFBoTcFneVGswDSK2bVi4,1095
30
+ kryten_robot-0.6.9.dist-info/METADATA,sha256=EBBU4jtDP4Qtbj1_Kf0fHlwHu80CyXK0EYQDdLJkHzo,13912
31
+ kryten_robot-0.6.9.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
32
+ kryten_robot-0.6.9.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ kryten-robot=kryten.__main__:cli
3
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Kryten Robot Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.