used unique names for subscription strings, used set of events rather than list, and called multiple relays for third call in main

This commit is contained in:
Vishal 2022-08-28 05:33:01 +05:30
parent b87cb0346b
commit 01b5b82228
6 changed files with 95 additions and 73 deletions

View File

@ -146,7 +146,7 @@ Future<void> main(List<String> arguments) async {
if( gEventsFilename != "") {
print("\n");
stdout.write('Reading events from ${whetherDefault}file.......');
List<Event> eventsFromFile = readEventsFromFile(gEventsFilename);
Set<Event> eventsFromFile = readEventsFromFile(gEventsFilename);
setRelaysIntialEvents(eventsFromFile);
eventsFromFile.forEach((element) { element.eventData.kind == 1? numFileEvents++: numFileEvents;});
print("read $numFileEvents posts from file $gEventsFilename");
@ -155,7 +155,7 @@ Future<void> main(List<String> arguments) async {
stdout.write('Sending request and waiting for events...');
sendRequest(gListRelayUrls, argResults[requestArg]);
Future.delayed(const Duration(milliseconds: numWaitSeconds * 2), () {
List<Event> receivedEvents = getRecievedEvents();
Set<Event> receivedEvents = getRecievedEvents();
stdout.write("received ${receivedEvents.length - numFileEvents} events\n");
// remove bots
@ -220,7 +220,7 @@ Future<void> main(List<String> arguments) async {
// get mentioned ptags, and then get the events for those users
List<String> pTags = getpTags(getRecievedEvents(), 300);
getMultiUserEvents(defaultServerUrl, pTags, 5000);
getMultiUserEvents(gListRelayUrls, pTags, 5000);
stdout.write('Waiting for rest of posts to come in.....');
Future.delayed(const Duration(milliseconds: numWaitSeconds * 2), () {

View File

@ -12,11 +12,11 @@ Future<void> processNotifications(Tree node) async {
const int waitMilliSeconds = 400;
Future.delayed(const Duration(milliseconds: waitMilliSeconds), () {
List<String> newEventsId = node.insertEvents(getRecievedEvents());
Set<String> newEventIdsSet = node.insertEvents(getRecievedEvents());
String nameToDisplay = userPrivateKey.length == 64?
"$gCommentColor${getAuthorName(userPublicKey)}$colorEndMarker":
"${gWarningColor}You are not signed in$colorEndMarker but are using public key $userPublicKey";
node.printNotifications(newEventsId, nameToDisplay);
node.printNotifications(newEventIdsSet, nameToDisplay);
clearEvents();
});

View File

@ -416,6 +416,11 @@ class Event {
Event(this.event, this.id, this.eventData, this.seenOnRelays, this.originalJson);
@override
bool operator ==( other) {
return (other is Event) && eventData.id == other.eventData.id;
}
factory Event.fromJson(String d, String relay) {
try {
dynamic json = jsonDecode(d);
@ -487,11 +492,11 @@ class HistogramEntry {
}
// return the numMostFrequent number of most frequent p tags ( user pubkeys) in the given events
List<String> getpTags(List<Event> events, int numMostFrequent) {
List<String> getpTags(Set<Event> events, int numMostFrequent) {
List<HistogramEntry> listHistogram = [];
Map<String, int> histogramMap = {};
for(int i = 0; i < events.length; i++) {
addToHistogram(histogramMap, events[i].eventData.pTags);
for(var event in events) {
addToHistogram(histogramMap, event.eventData.pTags);
}
histogramMap.forEach((key, value) {listHistogram.add(HistogramEntry(key, value));/* print("added to list of histogramEntry $key $value"); */});
@ -505,8 +510,8 @@ List<String> getpTags(List<Event> events, int numMostFrequent) {
return ptags;
}
List<Event> readEventsFromFile(String filename) {
List<Event> events = [];
Set<Event> readEventsFromFile(String filename) {
Set<Event> events = {};
final File file = File(filename);
// sync read
@ -524,24 +529,19 @@ List<Event> readEventsFromFile(String filename) {
}
// From the list of events provided, lookup the lastst contact information for the given user/pubkey
Event? getContactEvent(List<Event> events, String pubkey) {
Event? getContactEvent(Set<Event> events, String pubkey) {
// get the latest kind 3 event for the user, which lists his 'follows' list
int latestContactsTime = 0, latestContactIndex = -1;
for( int i = 0; i < events.length; i++) {
var e = events[i];
Event? latestContactEvent = null;
int latestContactsTime = 0;
for( var e in events) {
if( e.eventData.pubkey == pubkey && e.eventData.kind == 3 && latestContactsTime < e.eventData.createdAt) {
latestContactIndex = i;
latestContactsTime = e.eventData.createdAt;
latestContactEvent = e;
}
}
// if contact list was found, get user's feed, and keep the contact list for later use
if (latestContactIndex != -1) {
return events[latestContactIndex];
}
return null;
return latestContactEvent;
}
// for the user userPubkey, returns the relay of its contact contactPubkey

View File

@ -10,7 +10,8 @@ class Relay {
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);
int numRequestsSent;
Relay(this.url, this.socket, this.users, this.numReceived, this.numRequestsSent);
void printInfo() {
//print("In Relay: printInfo");
@ -23,7 +24,7 @@ class Relay {
*/
class Relays {
Map<String, Relay> relays;
List<Event> rEvents = []; // current events received. can be used by others. Is cleared after consumption
Set<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.rEvents, this.uniqueIdsRecieved);
@ -40,10 +41,10 @@ class Relays {
IOWebSocketChannel fws = IOWebSocketChannel.connect(relayUrl);
print('In Relay.relay: connecting to relay $relayUrl');
Map<String, Relay> mapRelay = {};
Relay relayObject = Relay( relayUrl, fws, [], 0);
Relay relayObject = Relay( relayUrl, fws, [], 0, 0);
mapRelay[relayUrl] = relayObject;
return Relays(mapRelay, [], {});
return Relays(mapRelay, {}, {});
}
/*
@ -58,7 +59,10 @@ class Relays {
}
}
String subscriptionId = "single_user" + (relays[relayUrl]?.numRequestsSent??"").toString();
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
@ -70,7 +74,8 @@ class Relays {
users.add(publicKey);
}
}
String request = getUserRequest(publicKey, numEventsToGet, sinceWhen);
String request = getUserRequest(subscriptionId, publicKey, numEventsToGet, sinceWhen);
sendRequest(relayUrl, request);
}
@ -98,7 +103,9 @@ class Relays {
}
} // if relay exists and has a user list
String request = getMultiUserRequest(reqKeys, numEventsToGet);
String subscriptionId = "multiple_user" + (relays[relayUrl]?.numRequestsSent??"").toString();
String request = getMultiUserRequest( subscriptionId, reqKeys, numEventsToGet);
sendRequest(relayUrl, request);
}
@ -116,13 +123,14 @@ class Relays {
IOWebSocketChannel? fws;
if(relays.containsKey(relay)) {
fws = relays[relay]?.socket;
relays[relay]?.numRequestsSent++;
}
else {
if(gDebug !=0) print('connecting to $relay');
try {
IOWebSocketChannel fws2 = IOWebSocketChannel.connect(relay);
Relay newRelay = Relay(relay, fws2, [], 0);
Relay newRelay = Relay(relay, fws2, [], 0, 1);
relays[relay] = newRelay;
fws = fws2;
fws2.stream.listen(
@ -135,7 +143,7 @@ class Relays {
}
String id = json[2]['id'] as String;
if( uniqueIdsRecieved.contains(id)) {
if( gDebug > 0) print("In relay: received duplicate event id : $id");
//if( gDebug > 0) print("In relay: received duplicate event id : $id");
return;
} else {
uniqueIdsRecieved.add(id);
@ -166,6 +174,7 @@ class Relays {
}
}
if(gDebug > 0) print('\nSending request: \n$request\n to $relay\n\n');
fws?.sink.add(request);
}
@ -186,20 +195,20 @@ class Relays {
}
}
Relays relays = Relays({}, [], {});
Relays relays = Relays({}, {}, {});
String getUserRequest(String publicKey, int numUserEvents, int sinceWhen) {
String getUserRequest(String subscriptionId, String publicKey, int numUserEvents, int sinceWhen) {
String strTime = "";
if( sinceWhen != 0) {
strTime = ', "since": ${sinceWhen.toString()}';
}
var strSubscription1 = '["REQ","single_user",{ "authors": ["';
var strSubscription1 = '["REQ","$subscriptionId",{ "authors": ["';
var strSubscription2 ='"], "limit": $numUserEvents $strTime } ]';
return strSubscription1 + publicKey + strSubscription2;
}
String getMultiUserRequest(List<String> publicKeys, int numUserEvents) {
var strSubscription1 = '["REQ","multiple_user",{ "authors": [';
String getMultiUserRequest(String subscriptionId, List<String> publicKeys, int numUserEvents) {
var strSubscription1 = '["REQ","$subscriptionId",{ "authors": [';
var strSubscription2 ='], "limit": $numUserEvents } ]';
String s = "";
@ -249,17 +258,20 @@ void getUserEvents(List<String> serverUrls, publicKey, numUserEvents, sinceWhen)
});
}
void getMultiUserEvents(serverUrl, List<String> publicKeys, numUserEvents) {
void getMultiUserEvents(List<String> serverUrls, 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;
for(var serverUrl in serverUrls) {
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} ");
List<String> partialList = publicKeys.sublist(i, i + getUserRequests);
relays.getMultiUserEvents(serverUrl, partialList, numUserEvents);
}
//print(" sending request form $i to ${i + getUserRequests} ");
List<String> partialList = publicKeys.sublist(i, i + getUserRequests);
relays.getMultiUserEvents(serverUrl, partialList, numUserEvents);
}
}
@ -269,16 +281,16 @@ void sendRequest(List<String> serverUrls, request) {
}
}
List<Event> getRecievedEvents() {
Set<Event> getRecievedEvents() {
return relays.rEvents;
}
void clearEvents() {
relays.rEvents = [];
relays.rEvents.clear();
if( gDebug > 0) print("clearEvents(): returning");
}
void setRelaysIntialEvents(eventsFromFile) {
void setRelaysIntialEvents(Set<Event> eventsFromFile) {
relays.rEvents = eventsFromFile;
}

View File

@ -105,6 +105,8 @@ List<String> gBots = [ "3b57518d02e6acfd5eb7198530b2e351e5a52278fb2499d14b66db2
const String gDefaultEventsFilename = "all_nostr_events.txt";
String gEventsFilename = ""; // is set in arguments, and if set, then file is read from and written to
bool gDontWriteOldEvents = true;
const int gPurgeBeforeDays = 100;
const String gUsage = """$exename version $version
The nostr console client built using dart.

View File

@ -22,7 +22,7 @@ class Tree {
// @method create top level Tree from events.
// first create a map. then process each element in the map by adding it to its parent ( if its a child tree)
factory Tree.fromEvents(List<Event> events) {
factory Tree.fromEvents(Set<Event> events) {
if( events.isEmpty) {
return Tree(Event("","",EventData("non","", 0, 0, "", [], [], [], [[]], {}), [""], "[json]"), [], {}, [], false, {});
}
@ -163,12 +163,12 @@ class Tree {
/*
* @insertEvents inserts the given new events into the tree, and returns the id the ones actually inserted so that they can be printed as notifications
*/
List<String> insertEvents(List<Event> newEvents) {
Set<String> insertEvents(Set<Event> newEventsSetToProcess) {
List<String> newEventsId = [];
Set<String> newEventIdsSet = {};
// add the event to the Tree
newEvents.forEach((newEvent) {
newEventsSetToProcess.forEach((newEvent) {
// don't process if the event is already present in the map
// this condition also excludes any duplicate events sent as newEvents
if( allChildEventsMap.containsKey(newEvent.eventData.id)) {
@ -180,7 +180,7 @@ class Tree {
String reactedTo = processReaction(newEvent);
if( reactedTo != "") {
newEventsId.add(newEvent.eventData.id); // add here to process/give notification about this new reaction
//newEventsId.add(newEvent.eventData.id); // add here to process/give notification about this new reaction
if(gDebug > 0) print("In insertEvents: got a new reaction by: ${newEvent.eventData.id} to $reactedTo");
} else {
if(gDebug > 0) print("In insertEvents: For new reaction ${newEvent.eventData.id} could not find reactedTo or reaction was already present by this reactor");
@ -203,11 +203,11 @@ class Tree {
if( gDebug > 0) print("In insertEvents: adding event to main children map");
allChildEventsMap[newEvent.eventData.id] = Tree(newEvent, [], {}, [], false, {});
newEventsId.add(newEvent.eventData.id);
newEventIdsSet.add(newEvent.eventData.id);
});
// now go over the newly inserted event, and add its to the tree. only for kind 1 events
newEventsId.forEach((newId) {
newEventIdsSet.forEach((newId) {
Tree? newTree = allChildEventsMap[newId]; // this should return true because we just inserted this event in the allEvents in block above
if( newTree != null) {
@ -258,9 +258,9 @@ class Tree {
}
});
if(gDebug > 0) print("In insertEvents: Found new ${newEventsId.length} events. ");
if(gDebug > 0) print("In end of insertEvents: Returning ${newEventIdsSet.length} new notification-type event: $newEventIdsSet ");
return newEventsId;
return newEventIdsSet;
}
@ -268,21 +268,19 @@ class Tree {
* @printNotifications Add the given events to the Tree, and print the events as notifications
* It should be ensured that these are only kind 1 events
*/
void printNotifications(List<String> newEventsId, String userName) {
// remove duplicates
Set temp = {};
newEventsId.retainWhere((event) => temp.add(newEventsId));
void printNotifications(Set<String> newEventIdsSet, String userName) {
if( gDebug > 0) print("Info: in printNotifications: num new evetns = ${newEventIdsSet.length}");
String strToWrite = "Notifications: ";
int countNotificationEvents = 0;
for( int i =0 ; i < newEventsId.length; i++) {
int k = (allChildEventsMap[newEventsId[i]]?.e.eventData.kind??-1);
for( var newEventId in newEventIdsSet) {
int k = (allChildEventsMap[newEventId]?.e.eventData.kind??-1);
if( k == 7 || k == 1 || k == 42 || k == 40) {
countNotificationEvents++;
}
if( allChildEventsMap.containsKey(newEventsId[i])) {
if( gDebug > 0) print( "id = ${ (allChildEventsMap[newEventsId[i]]?.e.eventData.id??-1)}");
if( allChildEventsMap.containsKey(newEventId)) {
if( gDebug > 0) print( "id = ${ (allChildEventsMap[newEventId]?.e.eventData.id??-1)}");
} else {
if( gDebug > 0) print( "Info: could not find event id in map."); // this wont later be processed
}
@ -290,7 +288,7 @@ class Tree {
}
// TODO don't print notifications for events that are too old
if(gDebug > 0) print("Info: In printNotifications: newEventsId = $newEventsId count17 = $countNotificationEvents");
if(gDebug > 0) print("Info: In printNotifications: newEventsId = $newEventIdsSet count17 = $countNotificationEvents");
if( countNotificationEvents == 0) {
strToWrite += "No new replies/posts.\n";
@ -300,14 +298,14 @@ class Tree {
return;
}
// TODO call count() less
strToWrite += "Number of new replies/posts = ${newEventsId.length}\n";
strToWrite += "Number of new replies/posts = ${newEventIdsSet.length}\n";
stdout.write("${getNumDashes(strToWrite.length -1 )}\n$strToWrite");
stdout.write("Total posts : ${count()}\n");
stdout.write("Signed in as : $userName\n");
stdout.write("\nHere are the threads with new replies or new likes: \n\n");
List<Tree> topTrees = []; // collect all top tress to display in this list. only unique tress will be displayed
newEventsId.forEach((eventID) {
List<Tree> topNotificationTree = []; // collect all top tress to display in this list. only unique tress will be displayed
newEventIdsSet.forEach((eventID) {
Tree ?t = allChildEventsMap[eventID];
if( t == null) {
@ -319,7 +317,7 @@ class Tree {
case 1:
t.e.eventData.isNotification = true;
Tree topTree = getTopTree(t);
topTrees.add(topTree);
topNotificationTree.add(topTree);
break;
case 7:
Event event = t.e;
@ -333,7 +331,7 @@ class Tree {
if( reactedToTree != null) {
reactedToTree.e.eventData.newLikes.add( reactorId);
Tree topTree = getTopTree(reactedToTree);
topTrees.add(topTree);
topNotificationTree.add(topTree);
} else {
if(gDebug > 0) print("Could not find reactedTo tree");
}
@ -350,9 +348,12 @@ class Tree {
// remove duplicate top trees
Set ids = {};
topTrees.retainWhere((t) => ids.add(t.e.eventData.id));
topNotificationTree.retainWhere((t) => ids.add(t.e.eventData.id));
topTrees.forEach( (t) { t.printTree(0, 0, selectAll); });
topNotificationTree.forEach( (t) {
t.printTree(0, 0, selectAll);
print("\n");
});
print("\n");
}
@ -521,6 +522,13 @@ class Tree {
for( var k in allChildEventsMap.keys) {
Tree? t = allChildEventsMap[k];
if( t != null) {
// only write if its not too old
if( gDontWriteOldEvents) {
if( t.e.eventData.createdAt < (DateTime.now().subtract(Duration(days: gPurgeBeforeDays)).millisecondsSinceEpoch ~/ 1000)) {
continue;
}
}
String line = "${t.e.originalJson}\n";
nLinesStr += line;
eventCounter++;
@ -1006,7 +1014,7 @@ String processReaction(Event event) {
}
// will go over the list of events, and update the global gReactions appropriately
void processReactions(List<Event> events) {
void processReactions(Set<Event> events) {
for (Event event in events) {
processReaction(event);
}
@ -1016,7 +1024,7 @@ void processReactions(List<Event> events) {
/*
* @function getTree Creates a Tree out of these received List of events.
*/
Future<Tree> getTree(List<Event> events) async {
Future<Tree> getTree(Set<Event> events) async {
if( events.isEmpty) {
print("Warning: In printEventsAsTree: events length = 0");
return Tree(Event("","",EventData("non","", 0, 0, "", [], [], [], [[]], {}), [""], "[json]"), [], {}, [], true, {});