mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-17 09:54:19 +01:00
351 lines
9.8 KiB
Dart
351 lines
9.8 KiB
Dart
import 'dart:developer';
|
|
|
|
import 'package:bot_toast/bot_toast.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:nesigner_adapter/nesigner.dart';
|
|
import 'package:nesigner_adapter/nesigner_util.dart';
|
|
import 'package:nostr_sdk/event.dart';
|
|
import 'package:nostr_sdk/event_kind.dart';
|
|
import 'package:nostr_sdk/nip02/nip02.dart';
|
|
import 'package:nostr_sdk/nip07/nip07_signer.dart';
|
|
import 'package:nostr_sdk/nip19/nip19.dart';
|
|
import 'package:nostr_sdk/nip46/nostr_remote_signer.dart';
|
|
import 'package:nostr_sdk/nip46/nostr_remote_signer_info.dart';
|
|
import 'package:nostr_sdk/nip55/android_nostr_signer.dart';
|
|
import 'package:nostr_sdk/nip65/nip65.dart';
|
|
import 'package:nostr_sdk/nostr.dart';
|
|
import 'package:nostr_sdk/relay/relay.dart';
|
|
import 'package:nostr_sdk/relay/relay_base.dart';
|
|
import 'package:nostr_sdk/relay/relay_isolate.dart';
|
|
import 'package:nostr_sdk/relay/relay_mode.dart';
|
|
import 'package:nostr_sdk/relay/relay_status.dart';
|
|
import 'package:nostr_sdk/relay/relay_type.dart';
|
|
import 'package:nostr_sdk/relay_local/relay_local.dart';
|
|
import 'package:nostr_sdk/signer/local_nostr_signer.dart';
|
|
import 'package:nostr_sdk/signer/nostr_signer.dart';
|
|
import 'package:nostr_sdk/signer/pubkey_only_nostr_signer.dart';
|
|
import 'package:nostr_sdk/utils/platform_util.dart';
|
|
import 'package:nostr_sdk/utils/string_util.dart';
|
|
|
|
import '../const/client_connected.dart';
|
|
import '../main.dart';
|
|
import 'data_util.dart';
|
|
|
|
class RelayProvider extends ChangeNotifier {
|
|
static RelayProvider? _relayProvider;
|
|
|
|
List<String> relayAddrs = [];
|
|
|
|
Map<String, RelayStatus> relayStatusMap = {};
|
|
|
|
RelayStatus? relayStatusLocal;
|
|
|
|
Map<String, RelayStatus> _tempRelayStatusMap = {};
|
|
|
|
static RelayProvider getInstance() {
|
|
if (_relayProvider == null) {
|
|
_relayProvider = RelayProvider();
|
|
// _relayProvider!._load();
|
|
}
|
|
return _relayProvider!;
|
|
}
|
|
|
|
void loadRelayAddrs(String? content) {
|
|
var relays = parseRelayAddrs(content);
|
|
if (relays.isEmpty) {
|
|
relays = [
|
|
"wss://nos.lol",
|
|
"wss://nostr.wine",
|
|
"wss://atlas.nostr.land",
|
|
"wss://relay.damus.io",
|
|
"wss://nostr-relay.app",
|
|
"wss://nostr.oxtr.dev",
|
|
"wss://relayable.org",
|
|
"wss://relay.primal.net",
|
|
"wss://relay.nostr.bg",
|
|
"wss://relay.nostr.band",
|
|
"wss://yabu.me",
|
|
"wss://nostr.mom"
|
|
];
|
|
}
|
|
|
|
relayAddrs = relays;
|
|
}
|
|
|
|
List<String> parseRelayAddrs(String? content) {
|
|
List<String> relays = [];
|
|
if (StringUtil.isBlank(content)) {
|
|
return relays;
|
|
}
|
|
|
|
var relayStatuses = NIP02.parseContenToRelays(content!);
|
|
for (var relayStatus in relayStatuses) {
|
|
var addr = relayStatus.addr;
|
|
relays.add(addr);
|
|
|
|
var oldRelayStatus = relayStatusMap[addr];
|
|
if (oldRelayStatus != null) {
|
|
oldRelayStatus.readAccess = relayStatus.readAccess;
|
|
oldRelayStatus.writeAccess = relayStatus.writeAccess;
|
|
} else {
|
|
relayStatusMap[addr] = relayStatus;
|
|
}
|
|
}
|
|
|
|
return relays;
|
|
}
|
|
|
|
RelayStatus? getRelayStatus(String addr) {
|
|
return relayStatusMap[addr];
|
|
}
|
|
|
|
String relayNumStr() {
|
|
var normalLength = relayAddrs.length;
|
|
|
|
int connectedNum = 0;
|
|
var it = relayStatusMap.values;
|
|
for (var status in it) {
|
|
if (status.connected == ClientConneccted.CONNECTED) {
|
|
connectedNum++;
|
|
}
|
|
}
|
|
return "$connectedNum / $normalLength";
|
|
}
|
|
|
|
int total() {
|
|
return relayAddrs.length;
|
|
}
|
|
|
|
Future<Nostr?> genNostrWithKey(String key) async {
|
|
NostrSigner? nostrSigner;
|
|
if (Nip19.isPubkey(key)) {
|
|
nostrSigner = PubkeyOnlyNostrSigner(Nip19.decode(key));
|
|
} else if (AndroidNostrSigner.isAndroidNostrSignerKey(key)) {
|
|
var pubkey = AndroidNostrSigner.getPubkeyFromKey(key);
|
|
var package = AndroidNostrSigner.getPackageFromKey(key);
|
|
nostrSigner = AndroidNostrSigner(pubkey: pubkey, package: package);
|
|
} else if (NIP07Signer.isWebNostrSignerKey(key)) {
|
|
var pubkey = NIP07Signer.getPubkey(key);
|
|
nostrSigner = NIP07Signer(pubkey: pubkey);
|
|
} else if (NostrRemoteSignerInfo.isBunkerUrl(key)) {
|
|
var info = NostrRemoteSignerInfo.parseBunkerUrl(key);
|
|
if (info == null) {
|
|
return null;
|
|
}
|
|
|
|
bool hasConnected = false;
|
|
if (StringUtil.isNotBlank(info.userPubkey)) {
|
|
hasConnected = true;
|
|
}
|
|
|
|
nostrSigner = NostrRemoteSigner(RelayMode.BASE_MODE, info);
|
|
await (nostrSigner as NostrRemoteSigner)
|
|
.connect(sendConnectRequest: !hasConnected);
|
|
|
|
if (StringUtil.isBlank(info.userPubkey)) {
|
|
await nostrSigner.pullPubkey();
|
|
}
|
|
|
|
if (await nostrSigner.getPublicKey() == null) {
|
|
return null;
|
|
}
|
|
} else if (Nesigner.isNesignerKey(key)) {
|
|
var pinCode = Nesigner.getPinCodeFromKey(key);
|
|
var pubkey = Nesigner.getPubkeyFromKey(key);
|
|
nostrSigner = Nesigner(pinCode, pubkey: pubkey);
|
|
try {
|
|
if (!(await (nostrSigner as Nesigner).start())) {
|
|
return null;
|
|
}
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
} else {
|
|
try {
|
|
nostrSigner = LocalNostrSigner(key);
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (nostrSigner == null) {
|
|
return null;
|
|
}
|
|
|
|
return await genNostr(nostrSigner);
|
|
}
|
|
|
|
Future<Nostr?> genNostr(NostrSigner signer) async {
|
|
var pubkey = await signer.getPublicKey();
|
|
if (pubkey == null) {
|
|
return null;
|
|
}
|
|
|
|
var _nostr = Nostr(signer, pubkey, [], genTempRelay, onNotice: null);
|
|
log("nostr init over");
|
|
|
|
loadRelayAddrs(null);
|
|
|
|
for (var relayAddr in relayAddrs) {
|
|
log("begin to init $relayAddr");
|
|
var custRelay = genRelay(relayAddr);
|
|
try {
|
|
_nostr.addRelay(custRelay, init: true);
|
|
} catch (e) {
|
|
log("relay $relayAddr add to pool error ${e.toString()}");
|
|
}
|
|
}
|
|
|
|
return _nostr;
|
|
}
|
|
|
|
void onRelayStatusChange() {
|
|
notifyListeners();
|
|
}
|
|
|
|
void addRelay(String relayAddr) {
|
|
if (!relayAddrs.contains(relayAddr)) {
|
|
relayAddrs.add(relayAddr);
|
|
_doAddRelay(relayAddr);
|
|
}
|
|
}
|
|
|
|
void _doAddRelay(String relayAddr,
|
|
{bool init = false, int relayType = RelayType.NORMAL}) {
|
|
var custRelay = genRelay(relayAddr, relayType: relayType);
|
|
log("begin to init $relayAddr");
|
|
nostr!.addRelay(custRelay,
|
|
autoSubscribe: true, init: init, relayType: relayType);
|
|
}
|
|
|
|
void removeRelay(String relayAddr) {
|
|
if (relayAddrs.contains(relayAddr)) {
|
|
relayAddrs.remove(relayAddr);
|
|
relayStatusMap.remove(relayAddr);
|
|
nostr!.removeRelay(relayAddr);
|
|
}
|
|
}
|
|
|
|
List<String> getWritableRelays() {
|
|
List<String> list = [];
|
|
for (var addr in relayAddrs) {
|
|
var relayStatus = relayStatusMap[addr];
|
|
if (relayStatus != null && relayStatus.writeAccess) {
|
|
list.add(addr);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
List<RelayStatus> _getRelayStatuses() {
|
|
List<RelayStatus> relayStatuses = [];
|
|
for (var addr in relayAddrs) {
|
|
var relayStatus = relayStatusMap[addr];
|
|
if (relayStatus != null) {
|
|
relayStatuses.add(relayStatus);
|
|
}
|
|
}
|
|
return relayStatuses;
|
|
}
|
|
|
|
Relay genRelay(String relayAddr, {int relayType = RelayType.NORMAL}) {
|
|
var relayStatus = relayStatusMap[relayAddr];
|
|
if (relayStatus == null) {
|
|
relayStatus = RelayStatus(relayAddr, relayType: relayType);
|
|
relayStatusMap[relayAddr] = relayStatus;
|
|
}
|
|
|
|
return _doGenRelay(relayStatus);
|
|
}
|
|
|
|
Relay _doGenRelay(RelayStatus relayStatus) {
|
|
var relayAddr = relayStatus.addr;
|
|
|
|
return RelayBase(
|
|
relayAddr,
|
|
relayStatus,
|
|
)..relayStatusCallback = onRelayStatusChange;
|
|
}
|
|
|
|
void relayUpdateByContactListEvent(Event event) {
|
|
List<String> oldRelays = []..addAll(relayAddrs);
|
|
loadRelayAddrs(event.content);
|
|
_updateRelays(oldRelays);
|
|
}
|
|
|
|
void _updateRelays(List<String> oldRelays) {
|
|
List<String> needToRemove = [];
|
|
List<String> needToAdd = [];
|
|
for (var oldRelay in oldRelays) {
|
|
if (!relayAddrs.contains(oldRelay)) {
|
|
// new addrs don't contain old relay, need to remove
|
|
needToRemove.add(oldRelay);
|
|
}
|
|
}
|
|
for (var relayAddr in relayAddrs) {
|
|
if (!oldRelays.contains(relayAddr)) {
|
|
// old addrs don't contain new relay, need to add
|
|
needToAdd.add(relayAddr);
|
|
}
|
|
}
|
|
|
|
for (var relayAddr in needToRemove) {
|
|
relayStatusMap.remove(relayAddr);
|
|
nostr!.removeRelay(relayAddr);
|
|
}
|
|
for (var relayAddr in needToAdd) {
|
|
_doAddRelay(relayAddr);
|
|
}
|
|
}
|
|
|
|
void clear() {
|
|
// sharedPreferences.remove(DataKey.RELAY_LIST);
|
|
relayStatusMap.clear();
|
|
loadRelayAddrs(null);
|
|
_tempRelayStatusMap.clear();
|
|
}
|
|
|
|
List<RelayStatus> tempRelayStatus() {
|
|
List<RelayStatus> list = []..addAll(_tempRelayStatusMap.values);
|
|
return list;
|
|
}
|
|
|
|
Relay genTempRelay(String addr) {
|
|
var rs = _tempRelayStatusMap[addr];
|
|
if (rs == null) {
|
|
rs = RelayStatus(addr);
|
|
_tempRelayStatusMap[addr] = rs;
|
|
}
|
|
|
|
return _doGenRelay(rs);
|
|
}
|
|
|
|
void cleanTempRelays() {
|
|
List<String> needRemoveList = [];
|
|
var now = DateTime.now().millisecondsSinceEpoch;
|
|
for (var entry in _tempRelayStatusMap.entries) {
|
|
var addr = entry.key;
|
|
var status = entry.value;
|
|
|
|
if (now - status.connectTime.millisecondsSinceEpoch > 1000 * 60 * 10 &&
|
|
(status.lastNoteTime == null ||
|
|
((now - status.lastNoteTime!.millisecondsSinceEpoch) >
|
|
1000 * 60 * 10)) &&
|
|
(status.lastQueryTime == null ||
|
|
((now - status.lastQueryTime!.millisecondsSinceEpoch) >
|
|
1000 * 60 * 10))) {
|
|
// init time over 10 min
|
|
// last note time over 10 min
|
|
// last query time over 10 min
|
|
needRemoveList.add(addr);
|
|
}
|
|
}
|
|
|
|
for (var addr in needRemoveList) {
|
|
if (!nostr!.tempRelayHasSubscription(addr)) {
|
|
// don't contain subscription, remote!
|
|
_tempRelayStatusMap.remove(addr);
|
|
nostr!.removeTempRelay(addr);
|
|
}
|
|
}
|
|
}
|
|
}
|