refactor: rename NIP-100 to NIP-AC for WebRTC calls
Rename package nip100WebRtcCalls -> nipACWebRtcCalls and NIP-100.md -> NIP-AC.md across all modules. https://claude.ai/code/session_017hZm7yu7CzmcQgZGSaqSXS
This commit is contained in:
@@ -96,12 +96,6 @@ import com.vitorpamplona.quartz.nip03Timestamp.VerificationStateCache
|
|||||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionIndex
|
import com.vitorpamplona.quartz.nip09Deletions.DeletionIndex
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallAnswerEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallHangupEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallIceCandidateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallOfferEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRejectEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRenegotiateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||||
|
|||||||
+6
-6
@@ -28,12 +28,6 @@ import com.vitorpamplona.amethyst.model.Note
|
|||||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.IEvent
|
import com.vitorpamplona.quartz.nip01Core.core.IEvent
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallAnswerEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallHangupEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallIceCandidateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallOfferEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRejectEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRenegotiateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
@@ -43,6 +37,12 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
|||||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache
|
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallIceCandidateEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRejectEvent
|
||||||
|
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRenegotiateEvent
|
||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
NIP-100
|
|
||||||
=======
|
|
||||||
|
|
||||||
WebRTC Calls
|
|
||||||
------------
|
|
||||||
|
|
||||||
`draft` `optional`
|
|
||||||
|
|
||||||
This NIP defines a protocol for establishing peer-to-peer voice and video calls between Nostr users using WebRTC, with Nostr relays serving as the signaling transport.
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
Nostr users currently lack a way to make real-time voice or video calls without relying on centralized services. By using Nostr relays for WebRTC signaling and public STUN servers for NAT traversal, calls can be established in a fully decentralized manner — no custom server infrastructure is required. Once a WebRTC peer connection is established, the relay is no longer involved in the media stream.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The protocol works as follows:
|
|
||||||
|
|
||||||
1. **Caller** creates a signed call offer event containing an SDP offer
|
|
||||||
2. The event is **gift-wrapped** ([NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md)) and published to relays
|
|
||||||
3. **Callee** unwraps the event, verifies the signature, and decides whether to accept
|
|
||||||
4. If accepted, callee sends back a gift-wrapped call answer event containing an SDP answer
|
|
||||||
5. Both parties exchange **ICE candidates** as gift-wrapped events for NAT traversal
|
|
||||||
6. A **direct WebRTC peer connection** is established for audio/video
|
|
||||||
|
|
||||||
All signaling events MUST be gift-wrapped using [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md) for metadata privacy. Events are signed by the sender's key and wrapped directly (without the seal layer) — the gift wrap's random ephemeral key already hides the sender from relay operators.
|
|
||||||
|
|
||||||
## Event Kinds
|
|
||||||
|
|
||||||
| Kind | Name | Description |
|
|
||||||
|-------|---------------------|----------------------------------------------|
|
|
||||||
| 25050 | Call Offer | SDP offer initiating a call |
|
|
||||||
| 25051 | Call Answer | SDP answer accepting a call |
|
|
||||||
| 25052 | ICE Candidate | ICE candidate for NAT traversal |
|
|
||||||
| 25053 | Call Hangup | Terminates an active or pending call |
|
|
||||||
| 25054 | Call Reject | Rejects an incoming call |
|
|
||||||
| 25055 | Call Renegotiate | New SDP offer for mid-call changes |
|
|
||||||
|
|
||||||
## Tags
|
|
||||||
|
|
||||||
All signaling events MUST include:
|
|
||||||
|
|
||||||
| Tag | Description | Required |
|
|
||||||
|---------------|-------------------------------------------------------|----------|
|
|
||||||
| `p` | Hex pubkey of the recipient | YES |
|
|
||||||
| `call-id` | UUID identifying the call session | YES |
|
|
||||||
| `expiration` | Unix timestamp ([NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md)), SHOULD be ~5 minutes from `created_at` | YES |
|
|
||||||
| `alt` | Human-readable description ([NIP-31](https://github.com/nostr-protocol/nips/blob/master/31.md)) | YES |
|
|
||||||
|
|
||||||
Additional tags for **Call Offer** (kind 25050):
|
|
||||||
|
|
||||||
| Tag | Description | Required |
|
|
||||||
|---------------|-------------------------------------------------------|----------|
|
|
||||||
| `call-type` | `"voice"` or `"video"` | YES |
|
|
||||||
|
|
||||||
## Event Structures
|
|
||||||
|
|
||||||
### Call Offer (kind 25050)
|
|
||||||
|
|
||||||
The `content` field contains the SDP offer string.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25050,
|
|
||||||
"pubkey": "<caller-hex-pubkey>",
|
|
||||||
"created_at": 1234567890,
|
|
||||||
"content": "v=0\r\no=- 4611731400430051336 2 IN IP4 127.0.0.1\r\n...",
|
|
||||||
"tags": [
|
|
||||||
["p", "<callee-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["call-type", "video"],
|
|
||||||
["expiration", "1234568190"],
|
|
||||||
["alt", "WebRTC call offer"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Call Answer (kind 25051)
|
|
||||||
|
|
||||||
The `content` field contains the SDP answer string.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25051,
|
|
||||||
"pubkey": "<callee-hex-pubkey>",
|
|
||||||
"created_at": 1234567895,
|
|
||||||
"content": "v=0\r\no=- 4611731400430051337 2 IN IP4 127.0.0.1\r\n...",
|
|
||||||
"tags": [
|
|
||||||
["p", "<caller-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["expiration", "1234568195"],
|
|
||||||
["alt", "WebRTC call answer"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ICE Candidate (kind 25052)
|
|
||||||
|
|
||||||
The `content` field contains the ICE candidate as a JSON string with the fields `candidate`, `sdpMid`, and `sdpMLineIndex`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25052,
|
|
||||||
"pubkey": "<sender-hex-pubkey>",
|
|
||||||
"created_at": 1234567896,
|
|
||||||
"content": "{\"candidate\":\"candidate:842163049 1 udp 1677729535 203.0.113.1 44323 typ srflx raddr 0.0.0.0 rport 0 generation 0\",\"sdpMid\":\"0\",\"sdpMLineIndex\":0}",
|
|
||||||
"tags": [
|
|
||||||
["p", "<peer-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["expiration", "1234568196"],
|
|
||||||
["alt", "WebRTC ICE candidate"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Call Hangup (kind 25053)
|
|
||||||
|
|
||||||
The `content` field MAY contain a human-readable reason or be empty.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25053,
|
|
||||||
"pubkey": "<sender-hex-pubkey>",
|
|
||||||
"created_at": 1234568000,
|
|
||||||
"content": "",
|
|
||||||
"tags": [
|
|
||||||
["p", "<peer-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["expiration", "1234568300"],
|
|
||||||
["alt", "WebRTC call hangup"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Call Reject (kind 25054)
|
|
||||||
|
|
||||||
The `content` field MAY contain a reason or be empty.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25054,
|
|
||||||
"pubkey": "<callee-hex-pubkey>",
|
|
||||||
"created_at": 1234567893,
|
|
||||||
"content": "",
|
|
||||||
"tags": [
|
|
||||||
["p", "<caller-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["expiration", "1234568193"],
|
|
||||||
["alt", "WebRTC call rejection"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Call Renegotiate (kind 25055)
|
|
||||||
|
|
||||||
Used for mid-call changes such as toggling video on/off. The `content` field contains a new SDP offer.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"kind": 25055,
|
|
||||||
"pubkey": "<sender-hex-pubkey>",
|
|
||||||
"created_at": 1234568100,
|
|
||||||
"content": "v=0\r\no=- 4611731400430051338 3 IN IP4 127.0.0.1\r\n...",
|
|
||||||
"tags": [
|
|
||||||
["p", "<peer-hex-pubkey>"],
|
|
||||||
["call-id", "550e8400-e29b-41d4-a716-446655440000"],
|
|
||||||
["expiration", "1234568400"],
|
|
||||||
["alt", "WebRTC call renegotiation"]
|
|
||||||
],
|
|
||||||
"id": "<event-id>",
|
|
||||||
"sig": "<signature>"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Encryption and Delivery
|
|
||||||
|
|
||||||
All signaling events MUST be delivered using [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md) Gift Wraps:
|
|
||||||
|
|
||||||
1. **Sign** the signaling event with the sender's key
|
|
||||||
2. **Gift-wrap** the signed event directly using `GiftWrapEvent` (kind 1059) with NIP-44 encryption
|
|
||||||
3. **Publish** the gift wrap to the recipient's relay list
|
|
||||||
|
|
||||||
The seal layer (`SealedRumorEvent`) is NOT used. The gift wrap already provides:
|
|
||||||
|
|
||||||
- **NIP-44 encryption** — content is unreadable to relay operators
|
|
||||||
- **Random ephemeral pubkey** — the relay cannot identify the sender
|
|
||||||
- **`p` tag** — reveals only the recipient (necessary for delivery)
|
|
||||||
|
|
||||||
Recipients unwrap the gift, verify the inner event's signature against the sender's pubkey, and then process the signaling message.
|
|
||||||
|
|
||||||
## Protocol Flow
|
|
||||||
|
|
||||||
### Initiating a Call
|
|
||||||
|
|
||||||
```
|
|
||||||
Caller Relay Callee
|
|
||||||
| | |
|
|
||||||
|-- GiftWrap(CallOffer) ------->| |
|
|
||||||
| |-- GiftWrap(CallOffer) ------->|
|
|
||||||
| | |
|
|
||||||
| | [Callee unwraps, verifies signature]
|
|
||||||
| | [Checks: is caller followed?]
|
|
||||||
| | [YES → ring / NO → ignore]
|
|
||||||
| | |
|
|
||||||
|<-- GiftWrap(CallAnswer) ------|<-- GiftWrap(CallAnswer) ------|
|
|
||||||
| | |
|
|
||||||
|<-> GiftWrap(IceCandidate) <-->|<-> GiftWrap(IceCandidate) <-->|
|
|
||||||
| | |
|
|
||||||
|============= WebRTC P2P Connection Established ===============|
|
|
||||||
| (relay no longer involved) |
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ending a Call
|
|
||||||
|
|
||||||
Either party may send a `CallHangup` (kind 25053) at any time. The recipient SHOULD close the WebRTC peer connection and release media resources upon receiving it.
|
|
||||||
|
|
||||||
### Rejecting a Call
|
|
||||||
|
|
||||||
The callee may send a `CallReject` (kind 25054) instead of a `CallAnswer`. The caller SHOULD stop ringing and display a "call rejected" state.
|
|
||||||
|
|
||||||
## Spam Prevention
|
|
||||||
|
|
||||||
Clients SHOULD implement call filtering:
|
|
||||||
|
|
||||||
- **Follow-gated ringing**: Only display incoming call notifications for users in the recipient's follow list. Calls from non-followed users SHOULD be silently ignored.
|
|
||||||
- **Rate limiting**: Clients SHOULD ignore duplicate call offers from the same pubkey within a short window.
|
|
||||||
- **Expiration enforcement**: Clients MUST check the `expiration` tag and discard signaling events that have expired.
|
|
||||||
|
|
||||||
## NAT Traversal
|
|
||||||
|
|
||||||
This NIP does not mandate specific STUN or TURN servers. Clients SHOULD:
|
|
||||||
|
|
||||||
- Ship with a default set of public STUN servers (e.g., `stun:stun.l.google.com:19302`)
|
|
||||||
- Allow users to configure custom TURN servers for restrictive network environments
|
|
||||||
- Use trickle ICE (sending candidates as they are discovered) rather than waiting for all candidates before sending the offer/answer
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
- The `call-id` tag MUST be a UUID that is unique per call session. All signaling events for the same call share the same `call-id`.
|
|
||||||
- Events SHOULD have short expiration times (~5 minutes) since signaling data is ephemeral and has no long-term value.
|
|
||||||
- Clients SHOULD implement a ringing timeout (e.g., 60 seconds). If no answer is received, the call transitions to a "timed out" state.
|
|
||||||
- Clients SHOULD use a foreground service or equivalent mechanism to keep calls active when the app is backgrounded.
|
|
||||||
- The WebRTC `PeerConnection` SHOULD use Unified Plan SDP semantics.
|
|
||||||
- Clients MAY support call renegotiation (kind 25055) for toggling video on/off mid-call without tearing down the connection.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [NIP-01: Basic Protocol](https://github.com/nostr-protocol/nips/blob/master/01.md) — Event structure
|
|
||||||
- [NIP-31: Alt Tag](https://github.com/nostr-protocol/nips/blob/master/31.md) — Human-readable event descriptions
|
|
||||||
- [NIP-40: Expiration](https://github.com/nostr-protocol/nips/blob/master/40.md) — Event expiration timestamps
|
|
||||||
- [NIP-44: Encryption](https://github.com/nostr-protocol/nips/blob/master/44.md) — XChaCha20-Poly1305 encryption
|
|
||||||
- [NIP-59: Gift Wraps](https://github.com/nostr-protocol/nips/blob/master/59.md) — Encrypted event delivery
|
|
||||||
- [WebRTC Specification](https://www.w3.org/TR/webrtc/) — Peer-to-peer real-time communication
|
|
||||||
- [RFC 8445: ICE](https://datatracker.ietf.org/doc/html/rfc8445) — Interactive Connectivity Establishment
|
|
||||||
- [nostr-protocol/nips#771](https://github.com/nostr-protocol/nips/issues/771) — WebRTC signaling discussion
|
|
||||||
-113
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls
|
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallAnswerEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallHangupEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallIceCandidateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallOfferEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRejectEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRenegotiateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallType
|
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
|
||||||
|
|
||||||
class WebRtcCallFactory {
|
|
||||||
data class Result(
|
|
||||||
val msg: Event,
|
|
||||||
val wrap: GiftWrapEvent,
|
|
||||||
)
|
|
||||||
|
|
||||||
suspend fun createCallOffer(
|
|
||||||
sdpOffer: String,
|
|
||||||
calleePubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
callType: CallType,
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallOfferEvent.build(sdpOffer, calleePubKey, callId, callType)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = calleePubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createCallAnswer(
|
|
||||||
sdpAnswer: String,
|
|
||||||
callerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallAnswerEvent.build(sdpAnswer, callerPubKey, callId)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = callerPubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createIceCandidate(
|
|
||||||
candidateJson: String,
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallIceCandidateEvent.build(candidateJson, peerPubKey, callId)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = peerPubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createHangup(
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
reason: String = "",
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallHangupEvent.build(peerPubKey, callId, reason)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = peerPubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createReject(
|
|
||||||
callerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
reason: String = "",
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallRejectEvent.build(callerPubKey, callId, reason)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = callerPubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createRenegotiate(
|
|
||||||
sdpOffer: String,
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
signer: NostrSigner,
|
|
||||||
): Result {
|
|
||||||
val template = CallRenegotiateEvent.build(sdpOffer, peerPubKey, callId)
|
|
||||||
val signed = signer.sign(template)
|
|
||||||
val wrap = GiftWrapEvent.create(event = signed, recipientPubKey = peerPubKey)
|
|
||||||
return Result(signed, wrap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallAnswerEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun sdpAnswer() = content
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25051
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC call answer"
|
|
||||||
const val EXPIRATION_SECONDS = 300L
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
sdpAnswer: String,
|
|
||||||
callerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallAnswerEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, sdpAnswer, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(callerPubKey)
|
|
||||||
callId(callId)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallHangupEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun reason() = content.ifEmpty { null }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25053
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC call hangup"
|
|
||||||
const val EXPIRATION_SECONDS = 300L
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
reason: String = "",
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallHangupEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, reason, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(peerPubKey)
|
|
||||||
callId(callId)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallIceCandidateEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun candidateJson() = content
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25052
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC ICE candidate"
|
|
||||||
const val EXPIRATION_SECONDS = 300L
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
candidateJson: String,
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallIceCandidateEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, candidateJson, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(peerPubKey)
|
|
||||||
callId(callId)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-74
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallType
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallTypeTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callType
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallOfferEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun callType() = tags.firstNotNullOfOrNull(CallTypeTag::parse)
|
|
||||||
|
|
||||||
fun sdpOffer() = content
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25050
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC call offer"
|
|
||||||
const val EXPIRATION_SECONDS = 300L // 5 minutes
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
sdpOffer: String,
|
|
||||||
calleePubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
type: CallType,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallOfferEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, sdpOffer, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(calleePubKey)
|
|
||||||
callId(callId)
|
|
||||||
callType(type)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallRejectEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun reason() = content.ifEmpty { null }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25054
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC call rejection"
|
|
||||||
const val EXPIRATION_SECONDS = 300L
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
callerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
reason: String = "",
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallRejectEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, reason, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(callerPubKey)
|
|
||||||
callId(callId)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.events
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.CallIdTag
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.tags.callId
|
|
||||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class CallRenegotiateEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
|
||||||
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
|
|
||||||
|
|
||||||
fun sdpOffer() = content
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 25055
|
|
||||||
const val ALT_DESCRIPTION = "WebRTC call renegotiation"
|
|
||||||
const val EXPIRATION_SECONDS = 300L
|
|
||||||
|
|
||||||
fun build(
|
|
||||||
sdpOffer: String,
|
|
||||||
peerPubKey: HexKey,
|
|
||||||
callId: String,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
initializer: TagArrayBuilder<CallRenegotiateEvent>.() -> Unit = {},
|
|
||||||
) = eventTemplate(KIND, sdpOffer, createdAt) {
|
|
||||||
alt(ALT_DESCRIPTION)
|
|
||||||
pTag(peerPubKey)
|
|
||||||
callId(callId)
|
|
||||||
expiration(createdAt + EXPIRATION_SECONDS)
|
|
||||||
initializer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.tags
|
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
|
||||||
import com.vitorpamplona.quartz.utils.ensure
|
|
||||||
|
|
||||||
class CallIdTag {
|
|
||||||
companion object {
|
|
||||||
const val TAG_NAME = "call-id"
|
|
||||||
|
|
||||||
fun parse(tag: Array<String>): String? {
|
|
||||||
ensure(tag.has(1)) { return null }
|
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
|
||||||
ensure(tag[1].isNotEmpty()) { return null }
|
|
||||||
return tag[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun assemble(callId: String) = arrayOf(TAG_NAME, callId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.tags
|
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
|
||||||
import com.vitorpamplona.quartz.utils.ensure
|
|
||||||
|
|
||||||
enum class CallType(
|
|
||||||
val value: String,
|
|
||||||
) {
|
|
||||||
VOICE("voice"),
|
|
||||||
VIDEO("video"),
|
|
||||||
;
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromString(value: String): CallType? =
|
|
||||||
when (value) {
|
|
||||||
"voice" -> VOICE
|
|
||||||
"video" -> VIDEO
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CallTypeTag {
|
|
||||||
companion object {
|
|
||||||
const val TAG_NAME = "call-type"
|
|
||||||
|
|
||||||
fun parse(tag: Array<String>): CallType? {
|
|
||||||
ensure(tag.has(1)) { return null }
|
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
|
||||||
return CallType.fromString(tag[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
fun assemble(callType: CallType) = arrayOf(TAG_NAME, callType.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Vitor Pamplona
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.vitorpamplona.quartz.nip100WebRtcCalls.tags
|
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
|
||||||
|
|
||||||
fun <T : Event> TagArrayBuilder<T>.callId(callId: String) = addUnique(CallIdTag.assemble(callId))
|
|
||||||
|
|
||||||
fun <T : Event> TagArrayBuilder<T>.callType(callType: CallType) = addUnique(CallTypeTag.assemble(callType))
|
|
||||||
@@ -50,12 +50,6 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
|||||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallAnswerEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallHangupEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallIceCandidateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallOfferEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRejectEvent
|
|
||||||
import com.vitorpamplona.quartz.nip100WebRtcCalls.events.CallRenegotiateEvent
|
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip15Marketplace.auction.AuctionEvent
|
import com.vitorpamplona.quartz.nip15Marketplace.auction.AuctionEvent
|
||||||
import com.vitorpamplona.quartz.nip15Marketplace.bid.BidEvent
|
import com.vitorpamplona.quartz.nip15Marketplace.bid.BidEvent
|
||||||
|
|||||||
Reference in New Issue
Block a user