-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
970 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import 'package:nostr/nostr.dart'; | ||
|
||
/// Basic Event Kinds | ||
/// 0: set_metadata: the content is set to a stringified JSON object {name: <username>, about: <string>, picture: <url, string>} describing the user who created the event. A relay may delete past set_metadata events once it gets a new one for the same pubkey. | ||
/// 1: text_note: the content is set to the plaintext content of a note (anything the user wants to say). Do not use Markdown! Clients should not have to guess how to interpret content like [](). Use different event kinds for parsable content. | ||
/// 2: recommend_server: the content is set to the URL (e.g., wss://somerelay.com) of a relay the event creator wants to recommend to its followers. | ||
class Nip1 { | ||
static Event setMetadata(String content, String privkey) { | ||
return Event.from(kind: 0, tags: [], content: content, privkey: privkey); | ||
} | ||
|
||
static Event textNote(String content, String privkey) { | ||
return Event.from(kind: 1, tags: [], content: content, privkey: privkey); | ||
} | ||
|
||
static Event recommendServer(String content, String privkey) { | ||
return Event.from(kind: 2, tags: [], content: content, privkey: privkey); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import 'dart:convert'; | ||
import 'package:nostr/nostr.dart'; | ||
|
||
/// Mapping Nostr keys to DNS-based internet identifiers | ||
class Nip5 { | ||
/// decode setmetadata event | ||
/// { | ||
/// "pubkey": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9", | ||
/// "kind": 0, | ||
/// "content": "{\"name\": \"bob\", \"nip05\": \"[email protected]\"}" | ||
/// } | ||
static Future<DNS?> decode(Event event) async { | ||
if (event.kind == 0) { | ||
try { | ||
Map map = jsonDecode(event.content); | ||
String dns = map['nip05']; | ||
List<dynamic> relays = map['relays']; | ||
if (dns.isNotEmpty) { | ||
List<dynamic> parts = dns.split('@'); | ||
String name = parts[0]; | ||
String domain = parts[1]; | ||
return DNS(name, domain, event.pubkey, relays.map((e) => e.toString()).toList()); | ||
} | ||
} catch (e) { | ||
throw Exception(e.toString()); | ||
} | ||
} | ||
throw Exception("${event.kind} is not nip1 compatible"); | ||
} | ||
|
||
/// encode set metadata event | ||
static Event encode( | ||
String name, String domain, List<String> relays, String privkey) { | ||
if (isValidName(name) && isValidDomain(domain)) { | ||
String content = generateContent(name, domain, relays); | ||
return Nip1.setMetadata(content, privkey); | ||
} else { | ||
throw Exception("not a valid name or domain!"); | ||
} | ||
} | ||
|
||
static bool isValidName(String input) { | ||
RegExp regExp = RegExp(r'^[a-z0-9_]+$'); | ||
return regExp.hasMatch(input); | ||
} | ||
|
||
static bool isValidDomain(String domain) { | ||
RegExp regExp = RegExp( | ||
r'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', | ||
caseSensitive: false, | ||
); | ||
return regExp.hasMatch(domain); | ||
} | ||
|
||
static String generateContent( | ||
String name, String domain, List<String> relays) { | ||
Map<String, dynamic> map = { | ||
'name': name, | ||
'nip05': '$name@$domain', | ||
'relays': relays, | ||
}; | ||
return jsonEncode(map); | ||
} | ||
} | ||
|
||
/// | ||
class DNS { | ||
String name; | ||
|
||
String domain; | ||
|
||
String pubkey; | ||
|
||
List<String> relays; | ||
|
||
/// Default constructor | ||
DNS(this.name, this.domain, this.pubkey, this.relays); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
///This NIP describes how to use "e" and "p" tags in text events, | ||
///especially those that are replies to other text events. | ||
///It helps clients thread the replies into a tree rooted at the original event. | ||
class Nip10 { | ||
///{ | ||
/// "tags": [ | ||
/// ["e", <kind_40_event_id>, <relay-url>, "root"], | ||
/// ["e", <kind_42_event_id>, <relay-url>, "reply"], | ||
/// ["p", <pubkey>, <relay-url>], | ||
/// ... | ||
/// ], | ||
/// ... | ||
/// } | ||
static Thread fromTags(List<List<String>> tags) { | ||
ETag root = ETag('', '', ''); | ||
List<ETag> etags = []; | ||
List<PTag> ptags = []; | ||
for (var tag in tags) { | ||
if (tag[0] == "p") ptags.add(PTag(tag[1], tag[2])); | ||
if (tag[0] == "e") { | ||
if (tag[3] == 'root') { | ||
root = ETag(tag[1], tag[2], tag[3]); | ||
} else { | ||
etags.add(ETag(tag[1], tag[2], tag[3])); | ||
} | ||
} | ||
} | ||
return Thread(root, etags, ptags); | ||
} | ||
|
||
static ETag rootTag(String eventId, String relay) { | ||
return ETag(eventId, relay, 'root'); | ||
} | ||
|
||
static List<List<String>> toTags(Thread thread) { | ||
List<List<String>> result = []; | ||
result.add( | ||
["e", thread.root.eventId, thread.root.relayURL, thread.root.marker]); | ||
for (var etag in thread.etags) { | ||
result.add(["e", etag.eventId, etag.relayURL, etag.marker]); | ||
} | ||
for (var ptag in thread.ptags) { | ||
result.add(["p", ptag.pubkey, ptag.relayURL]); | ||
} | ||
return result; | ||
} | ||
} | ||
|
||
class ETag { | ||
String eventId; | ||
String relayURL; | ||
String marker; // root, reply, mention | ||
|
||
ETag(this.eventId, this.relayURL, this.marker); | ||
} | ||
|
||
class PTag { | ||
String pubkey; | ||
String relayURL; | ||
|
||
PTag(this.pubkey, this.relayURL); | ||
} | ||
|
||
class Thread { | ||
ETag root; | ||
List<ETag> etags; | ||
List<PTag> ptags; | ||
Thread(this.root, this.etags, this.ptags); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import 'package:bech32/bech32.dart'; | ||
import 'package:convert/convert.dart'; | ||
|
||
/// bech32-encoded entities | ||
class Nip19 { | ||
static encodePubkey(String pubkey) { | ||
return bech32Encode("npub", pubkey); | ||
} | ||
|
||
static encodePrivkey(String privkey) { | ||
return bech32Encode("nsec", privkey); | ||
} | ||
|
||
static encodeNote(String noteid) { | ||
return bech32Encode("note", noteid); | ||
} | ||
|
||
static String decodePubkey(String data) { | ||
Map map = bech32Decode(data); | ||
if (map["prefix"] == "npub") { | ||
return map["data"]; | ||
} else { | ||
return ""; | ||
} | ||
} | ||
|
||
static String decodePrivkey(String data) { | ||
Map map = bech32Decode(data); | ||
if (map["prefix"] == "nsec") { | ||
return map["data"]; | ||
} else { | ||
return ""; | ||
} | ||
} | ||
|
||
static String decodeNote(String data) { | ||
Map map = bech32Decode(data); | ||
if (map["prefix"] == "note") { | ||
return map["data"]; | ||
} else { | ||
return ""; | ||
} | ||
} | ||
} | ||
|
||
/// help functions | ||
String bech32Encode(String prefix, String hexData) { | ||
final data = hex.decode(hexData); | ||
final convertedData = convertBits(data, 8, 5, true); | ||
final bech32Data = Bech32(prefix, convertedData); | ||
return bech32.encode(bech32Data); | ||
} | ||
|
||
Map<String, String> bech32Decode(String bech32Data) { | ||
final decodedData = bech32.decode(bech32Data); | ||
final convertedData = convertBits(decodedData.data, 5, 8, false); | ||
final hexData = hex.encode(convertedData); | ||
|
||
return {'prefix': decodedData.hrp, 'data': hexData}; | ||
} | ||
|
||
List<int> convertBits(List<int> data, int fromBits, int toBits, bool pad) { | ||
var acc = 0; | ||
var bits = 0; | ||
final maxv = (1 << toBits) - 1; | ||
final result = <int>[]; | ||
|
||
for (final value in data) { | ||
if (value < 0 || value >> fromBits != 0) { | ||
throw Exception('Invalid value: $value'); | ||
} | ||
acc = (acc << fromBits) | value; | ||
bits += fromBits; | ||
|
||
while (bits >= toBits) { | ||
bits -= toBits; | ||
result.add((acc >> bits) & maxv); | ||
} | ||
} | ||
|
||
if (pad) { | ||
if (bits > 0) { | ||
result.add((acc << (toBits - bits)) & maxv); | ||
} | ||
} else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) { | ||
throw Exception('Invalid data'); | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import 'dart:convert'; | ||
import 'package:nostr/nostr.dart'; | ||
|
||
class Nip20 { | ||
static OKEvent? getOk(String okPayload) { | ||
var ok = Message.deserialize(okPayload); | ||
if(ok.type == 'OK'){ | ||
var object = jsonDecode(ok.message); | ||
return OKEvent(object[0], object[1], object[2]); | ||
} | ||
} | ||
} | ||
|
||
class OKEvent { | ||
String eventId; | ||
bool status; | ||
String message; | ||
|
||
OKEvent(this.eventId, this.status, this.message); | ||
} |
Oops, something went wrong.