You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.5 KiB
131 lines
3.5 KiB
import 'dart:convert';
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
/// Represents a Nostr event.
|
|
class NostrEvent {
|
|
/// Event ID (32-byte hex string).
|
|
final String id;
|
|
|
|
/// Public key of the event creator (pubkey).
|
|
final String pubkey;
|
|
|
|
/// Unix timestamp in seconds.
|
|
final int createdAt;
|
|
|
|
/// Event kind (integer).
|
|
final int kind;
|
|
|
|
/// Event tags (array of arrays).
|
|
final List<List<String>> tags;
|
|
|
|
/// Event content (string).
|
|
final String content;
|
|
|
|
/// Event signature (64-byte hex string).
|
|
final String sig;
|
|
|
|
/// Creates a [NostrEvent] with the provided values.
|
|
NostrEvent({
|
|
required this.id,
|
|
required this.pubkey,
|
|
required this.createdAt,
|
|
required this.kind,
|
|
required this.tags,
|
|
required this.content,
|
|
required this.sig,
|
|
});
|
|
|
|
/// Creates a [NostrEvent] from a JSON array (Nostr event format).
|
|
factory NostrEvent.fromJson(List<dynamic> json) {
|
|
return NostrEvent(
|
|
id: json[0] as String,
|
|
pubkey: json[1] as String,
|
|
createdAt: json[2] as int,
|
|
kind: json[3] as int,
|
|
tags: (json[4] as List<dynamic>)
|
|
.map((tag) => (tag as List<dynamic>).map((e) => e.toString()).toList())
|
|
.toList(),
|
|
content: json[5] as String,
|
|
sig: json[6] as String,
|
|
);
|
|
}
|
|
|
|
/// Converts the [NostrEvent] to a JSON array (Nostr event format).
|
|
List<dynamic> toJson() {
|
|
return [
|
|
id,
|
|
pubkey,
|
|
createdAt,
|
|
kind,
|
|
tags,
|
|
content,
|
|
sig,
|
|
];
|
|
}
|
|
|
|
/// Creates an event from content and signs it with a private key.
|
|
///
|
|
/// [content] - Event content.
|
|
/// [kind] - Event kind (default: 1 for text note).
|
|
/// [privateKey] - Private key in hex format for signing.
|
|
/// [tags] - Optional tags for the event.
|
|
factory NostrEvent.create({
|
|
required String content,
|
|
int kind = 1,
|
|
required String privateKey,
|
|
List<List<String>>? tags,
|
|
}) {
|
|
// Derive public key from private key (simplified)
|
|
final privateKeyBytes = _hexToBytes(privateKey);
|
|
final publicKeyBytes = sha256.convert(privateKeyBytes).bytes.sublist(0, 32);
|
|
final pubkey = publicKeyBytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
|
|
|
final createdAt = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
|
final eventTags = tags ?? [];
|
|
|
|
// Create event data for signing
|
|
final eventData = [
|
|
0,
|
|
pubkey,
|
|
createdAt,
|
|
kind,
|
|
eventTags,
|
|
content,
|
|
];
|
|
|
|
// Generate event ID (hash of event data)
|
|
final eventJson = jsonEncode(eventData);
|
|
final idBytes = sha256.convert(utf8.encode(eventJson)).bytes.sublist(0, 32);
|
|
final id = idBytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
|
|
|
// Generate signature (simplified - in real Nostr, use secp256k1)
|
|
final sigBytes = sha256.convert(utf8.encode(id + privateKey)).bytes.sublist(0, 32);
|
|
final sig = sigBytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
|
|
|
return NostrEvent(
|
|
id: id,
|
|
pubkey: pubkey,
|
|
createdAt: createdAt,
|
|
kind: kind,
|
|
tags: eventTags,
|
|
content: content,
|
|
sig: sig,
|
|
);
|
|
}
|
|
|
|
/// Converts hex string to bytes.
|
|
static List<int> _hexToBytes(String hex) {
|
|
final result = <int>[];
|
|
for (int i = 0; i < hex.length; i += 2) {
|
|
result.add(int.parse(hex.substring(i, i + 2), radix: 16));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'NostrEvent(id: ${id.substring(0, 8)}..., kind: $kind, content: ${content.substring(0, content.length > 20 ? 20 : content.length)}...)';
|
|
}
|
|
}
|
|
|