fixes tests, added logging library, lot of other code refactoring.

This commit is contained in:
Vishal 2022-08-29 04:59:28 +05:30
parent cb24f7e2bf
commit 38ae89a3b2
7 changed files with 175 additions and 116 deletions

View File

@ -7,6 +7,7 @@ import 'package:nostr_console/relays.dart';
import 'package:nostr_console/console_ui.dart';
import 'package:nostr_console/settings.dart';
import 'package:args/args.dart';
import 'package:logging/logging.dart';
// program arguments
const String pubkeyArg = "pubkey";
@ -29,6 +30,12 @@ void printUsage() {
}
Future<void> main(List<String> arguments) async {
Logger.root.level = Level.ALL; // defaults to Level.INFO
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
final parser = ArgParser()..addOption(requestArg, abbr: 'q') ..addOption(pubkeyArg, abbr:"p")..addOption(prikeyArg, abbr:"k")
..addOption(lastdaysArg, abbr:"d") ..addOption(relayArg, abbr:"r")
..addFlag(helpArg, abbr:"h", defaultsTo: false)..addOption(alignArg, abbr:"a")
@ -142,7 +149,6 @@ Future<void> main(List<String> arguments) async {
}
}
if( gEventsFilename != "") {
print("\n");
stdout.write('Reading events from ${whetherDefault}file.......');
@ -151,23 +157,21 @@ Future<void> main(List<String> arguments) async {
eventsFromFile.forEach((element) { element.eventData.kind == 1? numFileEvents++: numFileEvents;});
print("read $numFileEvents posts from file $gEventsFilename");
}
if( argResults[requestArg] != null) {
stdout.write('Sending request and waiting for events...');
stdout.write('Sending request ${argResults[requestArg]} and waiting for events...');
sendRequest(gListRelayUrls, argResults[requestArg]);
Future.delayed(const Duration(milliseconds: numWaitSeconds * 2), () {
Set<Event> receivedEvents = getRecievedEvents();
stdout.write("received ${receivedEvents.length - numFileEvents} events\n");
// remove bots
receivedEvents.removeWhere((e) => gBots.contains(e.eventData.pubkey));
// create tree: will process reactions, remove bots, and then create main tree
Tree node = getTree(getRecievedEvents());
// create tree
Future<Tree> node = getTree(getRecievedEvents());
//clearEvents(); // cause we have consumed them above
node.then((value) {
clearEvents();
mainMenuUi(value, []);
});
if( gDebug > 0) stdout.write("Total events of kind 1 in created tree: ${node.count()} events\n");
clearEvents();
mainMenuUi(node, []);
// call main menu
});
@ -181,7 +185,7 @@ Future<void> main(List<String> arguments) async {
return;
}
getUserEvents(gListRelayUrls, userPublicKey, 3000, getSecondsDaysAgo(gEventsSinceDays));
getUserEvents(gListRelayUrls, userPublicKey, gLimitPerSubscription, getSecondsDaysAgo(gDaysToGetEventsFor));
// 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,
@ -193,6 +197,7 @@ Future<void> main(List<String> arguments) async {
getRecievedEvents().forEach((element) { element.eventData.kind == 1? numUserEvents++: numUserEvents;});
numUserEvents -= numFileEvents;
stdout.write("...received $numUserEvents posts made by the user\n");
if( gDebug > 0) log.info("Received user events.");
// get the latest kind 3 event for the user, which lists his 'follows' list
Event? contactEvent = getContactEvent(getRecievedEvents(), userPublicKey);
@ -201,7 +206,7 @@ Future<void> main(List<String> arguments) async {
List<String> contactList = [];
if (contactEvent != null ) {
if(gDebug > 0) print("In main: found contact list: \n ${contactEvent.originalJson}");
contactList = getContactFeed(gListRelayUrls, contactEvent.eventData.contactList, 4000, getSecondsDaysAgo(gEventsSinceDays));
contactList = getContactFeed(gListRelayUrls, contactEvent.eventData.contactList, gLimitPerSubscription, getSecondsDaysAgo(gDaysToGetEventsFor));
if( !gContactLists.containsKey(userPublicKey)) {
gContactLists[userPublicKey] = contactEvent.eventData.contactList;
@ -217,27 +222,27 @@ Future<void> main(List<String> arguments) async {
getRecievedEvents().forEach((element) { element.eventData.kind == 1? numFeedEvents++: numFeedEvents;});
numFeedEvents = numFeedEvents - numUserEvents - numFileEvents;
stdout.write("received $numFeedEvents posts from the follows\n");
if( gDebug > 0) log.info("Received feed.");
// get mentioned ptags, and then get the events for those users
List<String> pTags = getpTags(getRecievedEvents(), 300);
getMultiUserEvents(gListRelayUrls, pTags, 5000, getSecondsDaysAgo(gEventsSinceDays));
List<String> pTags = getpTags(getRecievedEvents(), gMaxPtagsToGet);
getMultiUserEvents(gListRelayUrls, pTags, gLimitPerSubscription, getSecondsDaysAgo(gDaysToGetEventsFor));
stdout.write('Waiting for rest of posts to come in.....');
Future.delayed(const Duration(milliseconds: numWaitSeconds * 2), () {
// count other events
getRecievedEvents().forEach((element) { element.eventData.kind == 1? numOtherEvents++: numOtherEvents;});
numOtherEvents = numOtherEvents - numFeedEvents - numUserEvents - numFileEvents;
stdout.write("received $numOtherEvents other posts\n");
if( gDebug > 0) log.info("Received ptag events events.");
// get all events in Tree form
Future<Tree> node = getTree(getRecievedEvents());
Tree node = getTree(getRecievedEvents());
// call the mein UI function
node.then((value) {
clearEvents();
mainMenuUi(value, contactList);
});
clearEvents();
mainMenuUi(node, contactList);
});
});
});

View File

@ -7,14 +7,34 @@ import 'package:nostr_console/settings.dart';
int gDebug = 0;
// translate
final translator = GoogleTranslator();
const int gNumTranslateDays = 1;// translate for this number of days
bool gTranslate = false; // translate flag
// Structure to store kind 0 event meta data for each user. Typically will have info from latest kind 0 event only.
class UserNameInfo {
int createdAt;
String name, about, picture;
UserNameInfo(this.createdAt, this.name, this.about, this.picture);
}
/*
* global user names from kind 0 events, mapped from public key to a 3 element array of [name, about, picture]
* JSON object {name: <username>, about: <string>, picture: <url, string>}
* only has info from latest kind 0 event
*/
Map<String, UserNameInfo> gKindONames = {};
// global reactions entry. Map of form <if of event reacted to, List of Reactors>
// reach Reactor is a list of 2-elements ( first is public id of reactor, second is comment)
Map< String, List<List<String>> > gReactions = {};
// global contact list of each user, including of the logged in user.
// maps from pubkey of a user, to the latest contact list of that user, which is the latest kind 3 message
// is updated as kind 3 events are received
Map< String, List<Contact>> gContactLists = {};
final translator = GoogleTranslator();
const int gNumTranslateDays = 1;// translate for this number of days
bool gTranslate = false; // translate flag
void printUnderlined(String x) => { print("$x\n${getNumDashes(x.length)}")};
@ -572,19 +592,20 @@ String getRelayOfUser(String userPubkey, String contactPubkey) {
}
// If given event is kind 0 event, then populates gKindONames with that info
void processKind0Event(Event e) {
// returns true if entry was created or modified, false otherwise
bool processKind0Event(Event e) {
if( e.eventData.kind != 0) {
return;
return false;
}
String content = e.eventData.content;
if( content.isEmpty) {
return;
return false;
}
String? name = "";
String? about = "";
String? picture = "";
String name = "";
String about = "";
String picture = "";
try {
dynamic json = jsonDecode(content);
@ -592,24 +613,30 @@ void processKind0Event(Event e) {
about = json["about"];
picture = json["picture"];
} catch(ex) {
if( gDebug != 0) print("Warning: In processKind0Event: caught exception for content: ${e.eventData.content}");
return;
}
if(name != Null) {
if( !gKindONames.containsKey(e.eventData.pubkey)) {
gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name??"", about??"", picture??"");
//print("Created meta data for name: $name about: $about picture: $picture");
} else {
int oldTime = gKindONames[e.eventData.pubkey]?.createdAt??0;
if( oldTime < e.eventData.createdAt) {
String oldName = gKindONames[e.eventData.pubkey]?.name??"";
gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name??"", about??"", picture??"");
//print("Updated meta data to name: $name from $oldName");
}
//if( gDebug != 0) print("Warning: In processKind0Event: caught exception for content: ${e.eventData.content}");
if( name.isEmpty) {
//return false;
}
}
bool newEntry = false, entryModified = false;
if( !gKindONames.containsKey(e.eventData.pubkey)) {
gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture);
newEntry = true;;
//print("Created meta data for name: $name about: $about picture: $picture");
} else {
int oldTime = gKindONames[e.eventData.pubkey]?.createdAt??0;
if( oldTime < e.eventData.createdAt) {
gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture);
entryModified = true;;
}
}
if(gDebug > 0) {
print("At end of processKind0Events: for name = $name ${newEntry? "added entry": ( entryModified?"modified entry": "No change done")} ");
}
return newEntry || entryModified;
}
// returns name by looking up global list gKindONames, which is populated by kind 0 events

View File

@ -122,6 +122,7 @@ class Relays {
IOWebSocketChannel? fws;
if(relays.containsKey(relay)) {
fws = relays[relay]?.socket;
relays[relay]?.numRequestsSent++;
}
@ -143,17 +144,18 @@ class Relays {
}
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);
}
uniqueIdsRecieved.add(id);
e = Event.fromJson(d, relay);
if(gDebug >= 2) print("adding event to list");
rEvents.add(e);
newRelay.numReceived++;
String receivedSubscription = json[1];
if( gDebug > 0) log.info("In relay listener: after adding element rEvents Size = ${rEvents.length} numReceived = ${newRelay.numReceived} for relay $relay for subscription $receivedSubscription");
} on FormatException {
print( 'exception in fromJson for event');
return;
@ -175,7 +177,7 @@ class Relays {
}
if(gDebug > 0) print('\nSending request: \n$request\n to $relay\n\n');
if(gDebug > 0) log.info('\nSending request: \n$request\n to $relay\n\n');
fws?.sink.add(request);
}
@ -265,12 +267,11 @@ void getUserEvents(List<String> serverUrls, String publicKey, int numUserEvents,
void getMultiUserEvents(List<String> serverUrls, List<String> publicKeys, int numUserEvents, int sinceWhen) {
if( gDebug > 0) print("Sending multi user request for ${publicKeys.length} users");
const int numMaxUserRequests = 15;
for(var serverUrl in serverUrls) {
for( int i = 0; i < publicKeys.length; i+= numMaxUserRequests) {
int getUserRequests = numMaxUserRequests;
if( publicKeys.length - i <= numMaxUserRequests) {
for( int i = 0; i < publicKeys.length; i+= gMaxAuthorsInOneRequest) {
int getUserRequests = gMaxAuthorsInOneRequest;
if( publicKeys.length - i <= gMaxAuthorsInOneRequest) {
getUserRequests = publicKeys.length - i;
}
//print(" sending request form $i to ${i + getUserRequests} ");

View File

@ -1,12 +1,26 @@
import 'package:logging/logging.dart';
const int gEventsSinceDays = 120;
final log = Logger('ExampleLogger');
// for debugging
String gCheckEventId = "a4479de655094679cdfb10f347521aa58f24717cdc5ddba89fb346453a8a99ed";
String gRemoteAdminPubkey = "";
const int numWaitSeconds = 4000;
const int numWaitSeconds = 3000;
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 gDontSaveBeforeDays = 100; // dont save events older than this many days if gDontWriteOldEvents flag is true
const int gDaysToGetEventsFor = 30; // when getting events, this is the since field (unless a fully formed request is given in command line)
const int gLimitPerSubscription = 20000;
// don't show notifications for events that are older than 5 days and come when program is running
// applicable only for notifications and not for search results. Search results set a flag in EventData and don't use this variable
const int gDontHighlightEventsOlderThan = 4;
const int gMaxAuthorsInOneRequest = 100; // number of author requests to send in one request
const int gMaxPtagsToGet = 100; // maximum number of p tags that are taken from the comments of feed ( the top most, most frequent)
// global counters of total events read or processed
int numFileEvents = 0, numUserEvents = 0, numFeedEvents = 0, numOtherEvents = 0;
@ -16,11 +30,13 @@ const String nostrRelayUnther = 'wss://nostr-relay.untethr.me';
const String relayNostrInfo = 'wss://relay.nostr.info';
String defaultServerUrl = relayNostrInfo;
List<String> gListRelayUrls = [defaultServerUrl,
"wss://nostr-verified.wellorder.net",
"wss://nostr-relay.wlvs.space",
List<String> gListRelayUrls = [ //defaultServerUrl,
// nostrRelayUnther,
// "wss://nostr-verified.wellorder.net",
"wss://nostr-relay.wlvs.space",
"wss://nostr-pub.wellorder.net",
"wss://relay.damus.io"];
"wss://relay.damus.io"
];
// name of executable
const String exename = "nostr_console";
@ -80,21 +96,9 @@ const String gDummyAccountPubkey = "Non";
const int gDefaultNumLastDays = 1;
int gNumLastDays = gDefaultNumLastDays;
class UserNameInfo {
int createdAt;
String name, about, picture;
UserNameInfo(this.createdAt, this.name, this.about, this.picture);
}
/*
* global user names from kind 0 events, mapped from public key to a 3 element array of [name, about, picture]
* JSON object {name: <username>, about: <string>, picture: <url, string>}
*/
Map<String, UserNameInfo> gKindONames = {};
// global reactions entry. Map of form <if of event reacted to, List of Reactors>
// reach Reactor is a list of 2-elements ( first is public id of reactor, second is comment)
Map< String, List<List<String>> > gReactions = {};
// UNUSED
String gRemoteAdminPubkey = "";
// bots ignored to reduce spam
List<String> gBots = [ "3b57518d02e6acfd5eb7198530b2e351e5a52278fb2499d14b66db2b5791c512", // robosats orderbook
@ -105,11 +109,6 @@ List<String> gBots = [ "3b57518d02e6acfd5eb7198530b2e351e5a52278fb2499d14b66db2
"6a9eb714c2889aa32e449cfbb7854bc9780feed4ff3d887e03910dcb22aa560a" // "bible bot"
];
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

@ -9,16 +9,20 @@ bool selectAll(Tree t) {
return true;
}
/*
* The actual tree holds only kind 1 events, or only posts
* This super-tree class holds other events too in its map, and in its chatRooms structure
*/
class Tree {
Event e;
List<Tree> children;
Map<String, Tree> allChildEventsMap;
Event e; // is dummy for very top level tree. Holds an event otherwise.
List<Tree> children; // only has kind 1 events
Map<String, Tree> allChildEventsMap; // has events of kind typesInEventMap
List<String> eventsWithoutParent;
bool whetherTopMost;
Map<String, ChatRoom> chatRooms = {};
Tree(this.e, this.children, this.allChildEventsMap, this.eventsWithoutParent, this.whetherTopMost, this.chatRooms);
static const List<int> typesInEventMap = [0, 1, 3, 7, 40, 42]; // 0 meta, 1 post, 3 follows list, 7 reactions
static const Set<int> typesInEventMap = {0, 1, 3, 7, 40, 42}; // 0 meta, 1 post, 3 follows list, 7 reactions
// @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)
@ -42,13 +46,18 @@ class Tree {
List<String> tempWithoutParent = [];
Map<String, ChatRoom> rooms = {};
if( gDebug > 0) print("In Tree from Events: size of tempChildEventsMap = ${tempChildEventsMap.length} ");
int numEventsNotPosts = 0; // just for debugging info
int numKind40Events = 0;
int numKind42Events = 0;
if( gDebug > 0) print("In Tree from Events: after adding all required events of type ${typesInEventMap} to tempChildEventsMap map, its size = ${tempChildEventsMap.length} ");
tempChildEventsMap.forEach((key, value) {
String eId = value.e.eventData.id;
int eKind = value.e.eventData.kind;
if(eKind == 42) {
numKind42Events++;
String chatRoomId = value.e.eventData.getChatRoomId();
if( chatRoomId != "") {
if( rooms.containsKey(chatRoomId)) {
@ -64,12 +73,13 @@ class Tree {
//if( gDebug > 0) print("Added new chat room object $chatRoomId and added message to it. ");
}
} else {
if( gDebug > 0) print("Could not get chat room id for event $eId, its original json: ");
//if( gDebug > 0) print("Could not get chat room id for event $eId, its original json: ");
//if( gDebug > 0) print(value.e.originalJson);
}
}
if(eKind == 40) {
numKind40Events++;
//print("Processing type 40");
String chatRoomId = eId;
try {
@ -91,7 +101,7 @@ class Tree {
}
ChatRoom room = ChatRoom(chatRoomId, roomName, roomAbout, "", []);
rooms[chatRoomId] = room;
if( gDebug > 0) print("Added new chat room $chatRoomId with name ${json['name']} .");
//if( gDebug > 0) print("Added new chat room $chatRoomId with name ${json['name']} .");
}
} on Exception catch(e) {
if( gDebug > 0) print("In From Event. Event type 40. Json Decode error for event id ${value.e.eventData.id}");
@ -100,12 +110,12 @@ class Tree {
// only posts, of kind 1, are added to the main tree structure
if( eKind != 1) {
numEventsNotPosts++;
return;
}
if(value.e.eventData.eTagsRest.isNotEmpty ) {
// is not a parent, find its parent and then add this element to that parent Tree
//stdout.write("added to parent a child\n");
String id = key;
String parentId = value.e.eventData.getParent();
if( tempChildEventsMap.containsKey(parentId)) {
@ -117,17 +127,17 @@ class Tree {
if(tempChildEventsMap.containsKey( parentId)) {
if( tempChildEventsMap[parentId]?.e.eventData.kind != 1) { // since parent can only be a kind 1 event
if( gDebug > 0) print("In Tree.fromEvents: got a kind 1 event whose parent is not a type 1 post: $id");
if( gDebug > 1) {
print("In Tree.fromEvents: Not adding: got a kind 1 event whose parent is not a type 1 post: $id . parent kind: ${tempChildEventsMap[parentId]?.e.eventData.kind}");
tempChildEventsMap[parentId]?.e.printEvent(0);
print("");
value.e.printEvent(1);
print("");
}
return;
}
tempChildEventsMap[parentId]?.addChildNode(value); // in this if condition this will get called
} else {
if( gDebug > 0 && key == "e9c0c91d52a2cf000bb2460406139a99dd5b7823165be435e96433a600be8e41" || parentId == "f377a303a852c8821069714f43b4eef5e341c03892eacf49abb594660b2fbb00") {
print (value.e.eventData.tags);
print("In from json: newId = $key parentid = $parentId for event id = ${value.e.eventData.id}");
print(tempChildEventsMap.containsKey(parentId));
print("----------------------------------------------/constructor from json");
}
// in case where the parent of the new event is not in the pool of all events,
// then we create a dummy event and put it at top ( or make this a top event?) TODO handle so that this can be replied to, and is fetched
@ -152,8 +162,12 @@ class Tree {
}
}
if( gDebug != 0) print("at end of tree from events: Total number of chat rooms: ${rooms.length}");
if(gDebug != 0) print("number of events without parent in fromEvents = ${tempWithoutParent.length}");
//if( gDebug != 0) print("In Tree FromEvents: at end of tree from events: Total number of chat rooms: ${rooms.length}");
if(gDebug != 0) print("In Tree FromEvents: number of events in map which are not kind 1 = ${numEventsNotPosts}");
if(gDebug != 0) print("In Tree FromEvents: number of events in map of kind 40 = ${numKind40Events}");
if(gDebug != 0) print("In Tree FromEvents: number of events in map of kind 42 = ${numKind42Events}");
if(gDebug != 0) print("In Tree FromEvents: number of events without parent in fromEvents = ${tempWithoutParent.length}");
// create a dummy top level tree and then create the main Tree object
Event dummy = Event("","", EventData("non","", 0, 1, "Dummy Top event. Should not be printed.", [], [], [], [[]], {}), [""], "[json]");
@ -203,7 +217,11 @@ class Tree {
if( gDebug > 0) print("In insertEvents: adding event to main children map");
allChildEventsMap[newEvent.eventData.id] = Tree(newEvent, [], {}, [], false, {});
newEventIdsSet.add(newEvent.eventData.id);
// add to new-notification list only if tis a recent event ( because relays may send old events, and we dont want to highlight stale messages)
if( newEvent.eventData.createdAt > getSecondsDaysAgo(gDontHighlightEventsOlderThan)) {
newEventIdsSet.add(newEvent.eventData.id);
}
});
// now go over the newly inserted event, and add its to the tree. only for kind 1 events
@ -340,7 +358,7 @@ class Tree {
}
break;
default:
if(gDebug > 0) print("got an event thats not 1 or 7(reaction). its id = ${t.e.eventData.kind} count17 = $countNotificationEvents");
if(gDebug > 0) print("got an event thats not 1 or 7(reaction). its kind = ${t.e.eventData.kind} count17 = $countNotificationEvents");
break;
}
}
@ -524,7 +542,7 @@ class Tree {
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)) {
if( t.e.eventData.createdAt < (DateTime.now().subtract(Duration(days: gDontSaveBeforeDays)).millisecondsSinceEpoch ~/ 1000)) {
continue;
}
}
@ -618,12 +636,14 @@ class Tree {
strTags += '["client","$clientName"]' ;
return strTags;
}
// counts all valid events in the tree: ignores the dummy nodes that are added for events which aren't yet known
int count() {
int totalCount = 0;
// ignore dummy events
if(e.eventData.pubkey != gDummyAccountPubkey) {
totalCount = 1;
if(!whetherTopMost) {
if( e.eventData.pubkey != gDummyAccountPubkey) // don't count dummy parents
totalCount = 1;
}
for(int i = 0; i < children.length; i++) {
totalCount += children[i].count(); // then add all the children
@ -1023,31 +1043,37 @@ void processReactions(Set<Event> events) {
/*
* @function getTree Creates a Tree out of these received List of events.
* Will remove duplicate events( which should not ideally exists because we have a set),
* populate global names, process reactions, remove bots, translate, and then create main tree
*/
Future<Tree> getTree(Set<Event> events) async {
Tree getTree(Set<Event> events) {
if( events.isEmpty) {
print("Warning: In printEventsAsTree: events length = 0");
return Tree(Event("","",EventData("non","", 0, 0, "", [], [], [], [[]], {}), [""], "[json]"), [], {}, [], true, {});
}
// populate the global with display names which can be later used by Event print
events.forEach( (x) => processKind0Event(x));
// remove all events other than kind 0 (meta data), 1(posts replies likes), 3 (contact list), 7(reactions), 40 and 42 (chat rooms)
events.removeWhere( (event) => !Tree.typesInEventMap.contains(event.eventData.kind));
// process NIP 25, or event reactions by adding them to a global map
print("In getTree: after removing unwanted kind, number of events remaining: ${events.length}");
// process kind 0 events about metadata
int totalKind0Processed = 0, notProcessed = 0;
events.forEach( (event) => processKind0Event(event)? totalKind0Processed++: notProcessed++);
if( gDebug > 0) print("In getTree: totalKind0Processed = $totalKind0Processed notProcessed = $notProcessed gKindONames.length = ${gKindONames.length}");
// process kind 7 events or reactions
processReactions(events);
// remove all events other than kind 0, 1, 3, 7 and 40 (chat rooms)
events.removeWhere( (item) => !Tree.typesInEventMap.contains(item.eventData.kind));
// remove bot events
events.removeWhere( (item) => gBots.contains(item.eventData.pubkey));
events.removeWhere( (event) => gBots.contains(event.eventData.pubkey));
// remove duplicate events
Set ids = {};
events.retainWhere((x) => ids.add(x.eventData.id));
events.retainWhere((event) => ids.add(event.eventData.id));
// translate and expand mentions for all
events.forEach( (e) => e.eventData.translateAndExpandMentions());
events.forEach( (event) => event.eventData.translateAndExpandMentions());
// create tree from events
Tree node = Tree.fromEvents(events);

View File

@ -18,3 +18,4 @@ dependencies:
intl: ^0.17.0
translator: ^0.1.7
web_socket_channel: ^2.2.0
logging: ^1.0.2

View File

@ -35,7 +35,7 @@ void main() {
Event exampleEvent2 = Event.fromJson('["EVENT","latest",{"id":"f3a267ecbb631012da618de620bc1fe265f6429f412359bf02330b437cf88e67","pubkey":"e37d948a0eee45e6cd113faaad934fcf17a97de2236c655b70650d4252daa9d3","created_at":1659722463,"kind":1,"tags":[["e","167063f491c41b7b8f79bc74f318e8a8b0a802bf8364b8bb7d19c887d59ec5de"]],"content":"I dont get the technical stuff about relays and things","sig":"9f68031687214a24862226f291e3baadd956dc14ba9c5c552f8c881a40aacd34feda667ef4e4b09711cd43950eec2d272d5b11bd7636de5f457f38f31eaff398"}]', "");
Event exampleEvent3 = Event.fromJson('["EVENT","latest",{"id":"dfc5765da281c0ad99cb8693fc98c87f0f86ad56042a414f06f19d41c1315fc3","pubkey":"e37d948a0eee45e6cd113faaad934fcf17a97de2236c655b70650d4252daa9d3","created_at":1659722537,"kind":1,"tags":[["e","167063f491c41b7b8f79bc74f318e8a8b0a802bf8364b8bb7d19c887d59ec5de"],["e","f3a267ecbb631012da618de620bc1fe265f6429f412359bf02330b437cf88e67"]],"content":"different clients make sense to me. I can use different clients to access nostr but is just one giant soup like twitter","sig":"d4fdc288e3cb95fc5ab46177fc0982d2aaa3b028eef6649f8200500da9c2e9a16c7a0462638afef7635bfea3094ec10901de759a48e362b60cb08f7e6585e02f"}]', "");
List<Event> listEvents = [exampleEvent1, exampleEvent2, exampleEvent3];
Set<Event> listEvents = {exampleEvent1, exampleEvent2, exampleEvent3};
Tree node = Tree.fromEvents(listEvents);
node.printTree(0, DateTime.now().subtract(Duration(days:1000)), selectAll);
@ -48,7 +48,7 @@ void main() {
Event exampleEvent2 = Event.fromJson('["EVENT","latest",{"id":"f3a267ecbb631012da618de620bc1fe265f6429f412359bf02330b437cf88e67","pubkey":"e37d948a0eee45e6cd113faaad934fcf17a97de2236c655b70650d4252daa9d3","created_at":1659722463,"kind":1,"tags":[["e","167063f491c41b7b8f79bc74f318e8a8b0a802bf8364b8bb7d19c887d59ec5de"]],"content":"I dont get the technical stuff about relays and things","sig":"9f68031687214a24862226f291e3baadd956dc14ba9c5c552f8c881a40aacd34feda667ef4e4b09711cd43950eec2d272d5b11bd7636de5f457f38f31eaff398"}]', "");
Event exampleEvent3 = Event.fromJson('["EVENT","latest",{"id":"dfc5765da281c0ad99cb8693fc98c87f0f86ad56042a414f06f19d41c1315fc3","pubkey":"e37d948a0eee45e6cd113faaad934fcf17a97de2236c655b70650d4252daa9d3","created_at":1659722537,"kind":1,"tags":[["e","167063f491c41b7b8f79bc74f318e8a8b0a802bf8364b8bb7d19c887d59ec5de"],["e","f3a267ecbb631012da618de620bc1fe265f6429f412359bf02330b437cf88e67"]],"content":"different clients make sense to me. I can use different clients to access nostr but is just one giant soup like twitter","sig":"d4fdc288e3cb95fc5ab46177fc0982d2aaa3b028eef6649f8200500da9c2e9a16c7a0462638afef7635bfea3094ec10901de759a48e362b60cb08f7e6585e02f"}]', "");
List<Event> listEvents = [ exampleEvent3, exampleEvent2, exampleEvent1];
Set<Event> listEvents = { exampleEvent3, exampleEvent2, exampleEvent1};
Tree node = Tree.fromEvents(listEvents);
node.printTree(0, DateTime.now().subtract(Duration(days:1000)), selectAll); // will test for ~1000 days