invoked more relays. NIP not followed yet tho. Printed info for application through menu

This commit is contained in:
vishalxl 2022-08-26 00:05:07 +05:30
parent 8264e9acb0
commit 6057e16005
5 changed files with 224 additions and 138 deletions

View File

@ -116,6 +116,7 @@ Future<void> main(List<String> arguments) async {
}
if( gEventsFilename != "") {
print("\n");
stdout.write('Reading events from the given file.......');
List<Event> eventsFromFile = readEventsFromFile(gEventsFilename);
@ -154,19 +155,21 @@ Future<void> main(List<String> arguments) async {
return;
}
getUserEvents(gListRelayUrls, userPublicKey, 3000, 0);
// the default in case no arguments are given is:
// get a user's events, then from its type 3 event, gets events of its follows,
// then get the events of user-id's mentioned in p-tags of received events
// then display them all
getUserEvents(defaultServerUrl, userPublicKey, 1000, 0);
/* getUserEvents("wss://relay.damus.io", userPublicKey, 1000, 0);
/* getUserEvents(defaultServerUrl, userPublicKey, 1000, 0);
getUserEvents("wss://relay.damus.io", userPublicKey, 1000, 0);
getUserEvents("wss://nostr-relay.wlvs.space", userPublicKey, 1000, 0);
getUserEvents("wss://nostr-pub.wellorder.net ", userPublicKey, 1000, 0);
getUserEvents("wss://relay.damus.io", userPublicKey, 1000, 0);
*/
const int numWaitSeconds = 2500;
*/
stdout.write('Waiting for user posts to come in.....');
Future.delayed(const Duration(milliseconds: numWaitSeconds), () {
// count user events
@ -180,8 +183,8 @@ Future<void> main(List<String> arguments) async {
// if contact list was found, get user's feed, and keep the contact list for later use
List<String> contactList = [];
if (contactEvent != null ) {
if(gDebug > 0) print("In main: found kind 3 contact list: \n ${contactEvent.originalJson}");
contactList = getContactFeed(contactEvent.eventData.contactList, 300);
if(gDebug > 0) print("In main: found contact list: \n ${contactEvent.originalJson}");
contactList = getContactFeed(gListRelayUrls, contactEvent.eventData.contactList, 4000);
if( !gContactLists.containsKey(userPublicKey)) {
gContactLists[userPublicKey] = contactEvent.eventData.contactList;
@ -199,8 +202,8 @@ Future<void> main(List<String> arguments) async {
stdout.write("received $numFeedEvents posts from the follows\n");
// get mentioned ptags, and then get the events for those users
List<String> pTags = getpTags(getRecievedEvents());
getMultiUserEvents(defaultServerUrl, pTags, 300);
List<String> pTags = getpTags(getRecievedEvents(), 300);
getMultiUserEvents(defaultServerUrl, pTags, 5000);
stdout.write('Waiting for rest of posts to come in.....');
Future.delayed(const Duration(milliseconds: numWaitSeconds * 2), () {

View File

@ -31,7 +31,7 @@ Future<void> sendMessage(Tree node, String replyToId, String replyKind, String c
String sig = sign(userPrivateKey, id, "12345612345612345612345612345612");
String toSendMessage = '["EVENT", {"id": "$id","pubkey": "$userPublicKey","created_at": $createdAt,"kind": $replyKind,"tags": [$strTags],"content": "$content","sig": "$sig"}]';
relays.sendMessage(toSendMessage, defaultServerUrl);
relays.sendRequest(defaultServerUrl, toSendMessage);
}
Future<void> sendChatMessage(Tree node, String channelId, String messageToSend) async {
@ -44,7 +44,7 @@ Future<void> sendChatMessage(Tree node, String channelId, String messageToSend)
String sig = sign(userPrivateKey, id, "12345612345612345612345612345612");
String toSendMessage = '["EVENT", {"id": "$id","pubkey": "$userPublicKey","created_at": $createdAt,"kind": $replyKind,"tags": [$strTags],"content": "$messageToSend","sig": "$sig"}]';
relays.sendMessage(toSendMessage, defaultServerUrl);
relays.sendRequest(defaultServerUrl, toSendMessage);
}
Future<void> sendEvent(Tree node, Event e) async {
@ -71,7 +71,7 @@ Future<void> sendEvent(Tree node, Event e) async {
String sig = sign(userPrivateKey, id, "12345612345612345612345612345612");
String toSendMessage = '["EVENT", {"id":"$id","pubkey":"$userPublicKey","created_at":$createdAt,"kind":${e.eventData.kind.toString()},"tags":[$strTags],"content":"$content","sig":"$sig"}]';
relays.sendMessage(toSendMessage, defaultServerUrl);
relays.sendRequest(defaultServerUrl, toSendMessage);
}
@ -129,6 +129,12 @@ Future<void> otherMenuUi(Tree node, var contactList) async {
break;
case 2:
// in case the program was invoked with --pubkey, then user can't send messages
if( userPrivateKey == "") {
print("Since no user private key has been supplied, posts/messages can't be sent. Invoke with --prikey \n");
break;
}
stdout.write("Enter username or first few letters of user's public key( or full public key): ");
String? $tempUserName = stdin.readLineSync();
String userName = $tempUserName??"";
@ -251,7 +257,22 @@ Future<void> otherMenuUi(Tree node, var contactList) async {
break;
case 7:
print("Applicatoin Info: TBD");
print("\n\n");
printUnderlined("Application stats");
print("\n");
relays.printInfo();
print("\n");
printUnderlined("Posts");
print("Total number of posts: ${node.count()}");
print("\n");
printUnderlined("User Info");
if( userPrivateKey.length == 64) {
print("You are signed in, and your public key is: $userPublicKey");
} else {
print("You are not signed in, and are using public key: $userPublicKey");
}
print("Your name as seen in metadata event is: ${getAuthorName(userPublicKey)}");
break;
case 8:
@ -283,6 +304,12 @@ Future<void> chatMenuUi(Tree node, var contactList) async {
node.printAllChannelsInfo();
break;
case 2:
// in case the program was invoked with --pubkey, then user can't send messages
if( userPrivateKey == "") {
print("Since no user private key has been supplied, posts/messages can't be sent. Invoke with --prikey \n");
break;
}
bool showChannelOption = true;
stdout.write("\nType unique channel id or name, or their 1st few letters; or type 'x' to go back to exit channel: ");
String? $tempUserInput = stdin.readLineSync();
@ -334,6 +361,8 @@ Future<void> mainMenuUi(Tree node, var contactList) async {
gDebug = 0;
// at the very beginning, show the tree as it is, and them show the options menu
node.printTree(0, DateTime.now().subtract(Duration(days:gNumLastDays)), selectAll);
//relays.printInfo();
bool userContinue = true;
while(userContinue) {
// align the text again in case the window size has been changed

View File

@ -17,6 +17,8 @@ bool gTranslate = false; // translate flag
int gDebug = 0;
void printUnderlined(String x) => { print("$x\n${getNumDashes(x.length)}")};
void printDepth(int d) {
for( int i = 0; i < gSpacesPerDepth * d + gNumLeftMarginSpaces; i++) {
stdout.write(" ");
@ -418,17 +420,57 @@ class ChatRoom {
}
List<String> getpTags(List<Event> events) {
List<String> pTags = [];
void addToHistogram(Map<String, int> histogram, List<String> pTags) {
Set tempPtags = {};
pTags.retainWhere((x) => tempPtags.add(x));
for(int i = 0; i < pTags.length; i++ ) {
String pTag = pTags[i];
if( histogram.containsKey(pTag)) {
int? val = histogram[pTag];
if( val != null) {
histogram[pTag] = ++val;
} else {
}
} else {
histogram[pTag] = 1;
}
}
//return histogram;
}
class HistogramEntry {
String str;
int count;
HistogramEntry(this.str, this.count);
static int histogramSorter(HistogramEntry a, HistogramEntry b) {
if( a.count < b.count ) {
return 1;
} if( a.count == b.count ) {
return 0;
} else {
return -1;
}
}
}
// return the numMostFrequent number of most frequent p tags ( user pubkeys) in the given events
List<String> getpTags(List<Event> events, int numMostFrequent) {
List<HistogramEntry> listHistogram = [];
Map<String, int> histogramMap = {};
for(int i = 0; i < events.length; i++) {
pTags.addAll(events[i].eventData.pTags);
addToHistogram(histogramMap, events[i].eventData.pTags);
}
// remove duplicate pTags events
Set tempPtags = {};
pTags.retainWhere((x) => tempPtags.add(x));
histogramMap.forEach((key, value) {listHistogram.add(HistogramEntry(key, value));/* print("added to list of histogramEntry $key $value"); */});
listHistogram.sort(HistogramEntry.histogramSorter);
List<String> ptags = [];
for( int i = 0; i < listHistogram.length && i < numMostFrequent; i++ ) {
//print ( "${listHistogram[i].str} ${listHistogram[i].count} ");
ptags.add(listHistogram[i].str);
}
return pTags;
return ptags;
}
// If given event is kind 0 event, then populates gKindONames with that info

View File

@ -5,31 +5,52 @@ import 'package:nostr_console/settings.dart';
import 'package:web_socket_channel/io.dart';
class Relay {
String url;
IOWebSocketChannel socket;
List<String> users; // is used so that duplicate requests aren't sent for same user for this same relay
int numReceived;
Relay(this.url, this.socket, this.users, this.numReceived);
void printInfo() {
//print("In Relay: printInfo");
print("$url ${getNumSpaces(45 - url.length)} $numReceived ${users.length}");
}
}
/*
* @class Relays Contains connections to all relays.
*/
class Relays {
Map<String, IOWebSocketChannel > relays;
List<String> users; // is used so that duplicate requests aren't sent for same user
List<Event> rEvents = []; // current events received. can be used by others. Is flushed between consumption
Map<String, Relay> relays;
List<Event> rEvents = []; // current events received. can be used by others. Is cleared after consumption
Set<String> uniqueIdsRecieved = {} ; // id of events received. only for internal usage, so that duplicate events are rejected
Relays(this.relays, this.users, this.rEvents, this.uniqueIdsRecieved);
Relays(this.relays, this.rEvents, this.uniqueIdsRecieved);
factory Relays.relay(String relay) {
IOWebSocketChannel fws = IOWebSocketChannel.connect(relay);
print('In Relay.relay: connecting to relay $relay');
Map<String, IOWebSocketChannel> r = {};
r[relay] = fws;
return Relays(r, [], [], {});
void printInfo() {
printUnderlined("Server connection info");
print(" Server Url Num events received: Num users requested");
for( var key in relays.keys) {
relays[key]?.printInfo();
}
}
factory Relays.relay(String relayUrl) {
IOWebSocketChannel fws = IOWebSocketChannel.connect(relayUrl);
print('In Relay.relay: connecting to relay $relayUrl');
Map<String, Relay> mapRelay = {};
Relay relayObject = Relay( relayUrl, fws, [], 0);
mapRelay[relayUrl] = relayObject;
return Relays(mapRelay, [], {});
}
/*
* @connect Connect to given relay and get all events for the given publicKey and insert the
* received events in the given List<Event>
*/
void getUserEvents(String relay, String publicKey, int numEventsToGet, int sinceWhen) {
void getUserEvents(String relayUrl, String publicKey, int numEventsToGet, int sinceWhen) {
for(int i = 0; i < gBots.length; i++) {
if( publicKey == gBots[i]) {
//print("In gerUserEvents: ignoring bot: $publicKey");
@ -37,94 +58,105 @@ class Relays {
}
}
// following is too restrictive casuse changed sinceWhen is not considered. TODO improve it
for(int i = 0; i < users.length; i++) {
if( users[i] == publicKey) {
return;
if( relays.containsKey(relayUrl)) {
List<String>? users = relays[relayUrl]?.users;
if( users != null) {
// following is too restrictive casuse changed sinceWhen is not considered. TODO improve it
for(int i = 0; i < users.length; i++) {
if( users[i] == publicKey) {
return;
}
}
users.add(publicKey);
}
}
users.add(publicKey);
String request = getUserRequest(publicKey, numEventsToGet, sinceWhen);
sendRequest(relay, request);
sendRequest(relayUrl, request);
}
/*
* @connect Connect to given relay and get all events for multiple users/publicKey and insert the
* received events in the given List<Event>
*/
void getMultiUserEvents(String relay, List<String> publicKeys, int numEventsToGet) {
void getMultiUserEvents(String relayUrl, List<String> publicKeys, int numEventsToGet) {
List<String> reqKeys = [];
// following is too restrictive. TODO improve it
for(int i = 0; i < publicKeys.length; i++) {
if( users.any( (u) => u == publicKeys[i])) {
continue;
if( relays.containsKey(relayUrl)) {
List<String>? users = relays[relayUrl]?.users;
if( users != null) {
// following is too restrictive. TODO improve it
for(int i = 0; i < publicKeys.length; i++) {
if( users.any( (u) => u == publicKeys[i])) {
continue;
}
if( gBots.any( (bot) => bot == publicKeys[i] )) {
continue;
}
users.add(publicKeys[i]);
reqKeys.add(publicKeys[i]);
}
}
if( gBots.any( (bot) => bot == publicKeys[i] )) {
//print("In getMultiUserEvents: ignoring a bot");
continue;
}
users.add(publicKeys[i]);
reqKeys.add(publicKeys[i]);
}
} // if relay exists and has a user list
String request = getMultiUserRequest(reqKeys, numEventsToGet);
sendRequest(relay, request);
sendRequest(relayUrl, request);
}
/*
* Send the given string to the given relay. Is used to send both requests, and to send evnets.
*/
void sendRequest(String relay, String request) {
if(relay == "" ) {
if( gDebug != 0) print ("Invalid or empty relay given");
return;
}
if( gDebug > 0) print ("\nIn relay.sendRequest for relay $relay and $request = $request");
if( gDebug > 0) print ("\nIn relay.sendRequest for relay $relay");
IOWebSocketChannel? fws;
if(relays.containsKey(relay)) {
fws = relays[relay];
fws = relays[relay]?.socket;
}
else {
if(gDebug !=0) print('connecting to $relay');
try {
fws = IOWebSocketChannel.connect(relay);
relays[relay] = fws;
fws.stream.listen(
(d) {
Event e;
try {
dynamic json = jsonDecode(d);
if( json.length < 3) {
return;
}
String id = json[2]['id'] as String;
if( uniqueIdsRecieved.contains(id)) {
if( gDebug > 0) print("In relay: received duplicate event id : $id");
return;
} else {
uniqueIdsRecieved.add(id);
}
IOWebSocketChannel fws2 = IOWebSocketChannel.connect(relay);
Relay newRelay = Relay(relay, fws2, [], 0);
relays[relay] = newRelay;
fws = fws2;
fws2.stream.listen(
(d) {
Event e;
try {
dynamic json = jsonDecode(d);
if( json.length < 3) {
return;
}
String id = json[2]['id'] as String;
if( uniqueIdsRecieved.contains(id)) {
if( gDebug > 0) print("In relay: received duplicate event id : $id");
return;
} else {
uniqueIdsRecieved.add(id);
}
e = Event.fromJson(d, relay);
if(gDebug >= 2) print("adding event to list");
rEvents.add(e);
} on FormatException {
print( 'exception in fromJson for event');
return;
} catch(err) {
print('exception generic $err for relay $relay');
return;
}
},
onError: (err) { print("\n${warningColor}Warning: In SendRequest creating connection onError. Kindly check your internet connection or change the relay by command line --relay=<relay wss url>"); print(colorEndMarker); },
onDone: () { if( gDebug != 0) print('Info: In onDone'); }
);
e = Event.fromJson(d, relay);
if(gDebug >= 2) print("adding event to list");
rEvents.add(e);
newRelay.numReceived++;
} on FormatException {
print( 'exception in fromJson for event');
return;
} catch(err) {
print('exception generic $err for relay $relay');
return;
}
},
onError: (err) { print("\n${warningColor}Warning: In SendRequest creating connection onError. Kindly check your internet connection or change the relay by command line --relay=<relay wss url>"); print(colorEndMarker); },
onDone: () { if( gDebug != 0) print('Info: In onDone'); }
);
} on WebSocketException {
print('WebSocketException exception for relay $relay');
return;
@ -134,52 +166,13 @@ class Relays {
}
}
if(gDebug > 0) print('sending request: $request to $relay\n');
if(gDebug > 0) print('\nSending request: \n$request\n to $relay\n\n');
fws?.sink.add(request);
}
void sendMessage(String message, String relay) {
IOWebSocketChannel? fws;
if(relays.containsKey(relay)) {
fws = relays[relay];
}
else {
if(gDebug !=0) ('connecting to $relay');
try {
fws = IOWebSocketChannel.connect(relay);
relays[relay] = fws;
fws.stream.listen(
(d) {
// need to put a processor even here, otherwise events will get ignored
Event e;
try {
e = Event.fromJson(d, relay);
if(gDebug >= 2) print("adding event to list");
rEvents.add(e);
} on FormatException {
print( 'exception in fromJson for event');
}
}, //
onError: (err) { print("\n${warningColor}Warning: In SendRequest creating connection onError. Kindly check your internet connection or change the relay by command line --relay=<relay wss url>"); print(colorEndMarker); },
onDone: () { if( gDebug != 0) print('in onDone'); }
);
} on WebSocketException {
print('WebSocketException exception');
return;
} catch(e) {
print('exception generic $e');
return;
}
}
if(gDebug > 0) print('sending message: $message to $relay\n');
fws?.sink.add(message);
}
IOWebSocketChannel? getWS(String relay) {
return relays[relay];
return relays[relay]?.socket;
}
void printStatus() {
@ -187,13 +180,13 @@ class Relays {
relays.forEach((key, value) {
print("for relay: $key");
print("$value\n");
String? reason = value.closeReason;
String? reason = value.socket.closeReason;
print( reason??"reason not found");
});
}
}
Relays relays = Relays({}, [], [], {});
Relays relays = Relays({}, [], {});
String getUserRequest(String publicKey, int numUserEvents, int sinceWhen) {
String strTime = "";
@ -219,7 +212,7 @@ String getMultiUserRequest(List<String> publicKeys, int numUserEvents) {
return strSubscription1 + s + strSubscription2;
}
List<String> getContactFeed(List<Contact> contacts, numEventsToGet) {
List<String> getContactFeed(List<String> relayUrls, List<Contact> contacts, numEventsToGet) {
// maps from relay url to list of users that it supplies events for
Map<String, List<String> > mContacts = {};
@ -237,24 +230,34 @@ List<String> getContactFeed(List<Contact> contacts, numEventsToGet) {
}
// send request for the users events to the relays
mContacts.forEach((key, value) { relays.getMultiUserEvents(key, value, numEventsToGet);}) ;
mContacts.forEach((key, value) {
relays.getMultiUserEvents(key, value, numEventsToGet);
relayUrls.forEach((relayUrl) {
relays.getMultiUserEvents(relayUrl, value, numEventsToGet);
});
});
// return contact list for use by caller
return contactList;
}
void getUserEvents(serverUrl, publicKey, numUserEvents, sinceWhen) {
relays.getUserEvents(serverUrl, publicKey, numUserEvents, sinceWhen);
void getUserEvents(List<String> serverUrls, publicKey, numUserEvents, sinceWhen) {
serverUrls.forEach((serverUrl) {
relays.getUserEvents(serverUrl, publicKey, numUserEvents, sinceWhen);
});
}
void getMultiUserEvents(serverUrl, List<String> publicKeys, numUserEvents) {
if( gDebug > 0) print("Sending multi user request for ${publicKeys.length} users");
const int numMaxUserRequests = 15;
for( int i = 0; i < publicKeys.length; i+= numMaxUserRequests) {
int getUserRequests = numMaxUserRequests;
if( publicKeys.length - i <= numMaxUserRequests) {
getUserRequests = publicKeys.length - i;
}
//print("sending request form $i to ${i + getUserRequests} ");
//print(" sending request form $i to ${i + getUserRequests} ");
List<String> partialList = publicKeys.sublist(i, i + getUserRequests);
relays.getMultiUserEvents(serverUrl, partialList, numUserEvents);
}

View File

@ -2,9 +2,18 @@
// for debugging
String gCheckEventId = "a77adfe7fb19195de2f2b3393698ea37d48671b93acd47f2f3a89c60f7a012ca";
const int numWaitSeconds = 3000;
// global counters of total events read or processed
int numFileEvents = 0, numUserEvents = 0, numFeedEvents = 0, numOtherEvents = 0;
List<String> gListRelayUrls = [defaultServerUrl,
"wss://nostr-verified.wellorder.net",
"wss://nostr-relay.wlvs.space",
"wss://nostr-pub.wellorder.net",
"wss://relay.damus.io"];
// name of executable
const String exename = "nostr_console";