From b9ac5538b3abd1a070db76a6da7bffc6743456cd Mon Sep 17 00:00:00 2001 From: Vishal <64505169+vishalxl@users.noreply.github.com> Date: Sun, 31 Mar 2024 17:14:01 +0530 Subject: [PATCH] added analysis_options.yaml and ran lint and applied changes --- analysis_options.yaml | 26 ++++ bin/nostr_console.dart | 29 ++-- lib/console_ui.dart | 90 +++++++----- lib/event_ds.dart | 197 ++++++++++++------------- lib/relays.dart | 56 ++++---- lib/settings.dart | 10 +- lib/tree_ds.dart | 271 +++++++++++++++++++---------------- lib/user.dart | 14 +- lib/utils.dart | 66 +++++---- pubspec.yaml | 5 +- test/nostr_console_test.dart | 24 ++-- 11 files changed, 436 insertions(+), 352 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..437f4a3 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,26 @@ +# reference : https://dart.dev/tools/analysis + + +include: package:lints/recommended.yaml + +analyzer: + exclude: [build/**] + language: + strict-raw-types: true + strict-inference: true + +# not including +# strict-casts: true + + +linter: + rules: + - use_super_parameters + - cancel_subscriptions + - close_sinks + - combinators_ordering + - comment_references + - invalid_case_patterns + - library_annotations + - one_member_abstracts + - only_throw_errors \ No newline at end of file diff --git a/bin/nostr_console.dart b/bin/nostr_console.dart index f0562bd..bbc3d02 100644 --- a/bin/nostr_console.dart +++ b/bin/nostr_console.dart @@ -92,7 +92,7 @@ Future main(List arguments) async { userPrivateKey = ""; } - if( gUserLocation.length > 0){ + if( gUserLocation.isNotEmpty){ print("Going to add $gUserLocation as the location tag with each post."); } @@ -118,7 +118,7 @@ Future main(List arguments) async { // write informative message in case user is not using proper keys if( userPublicKey == gDefaultPublicKey) { - print("You should ideally create your own private key and use it with ${gWarningColor}--prikey$gColorEndMarker program argument. "); + print("You should ideally create your own private key and use it with $gWarningColor--prikey$gColorEndMarker program argument. "); print("Create a private key from ${gWarningColor}astral.ninja, @damusapp, or even from command line using `openssl rand -hex 32`.$gColorEndMarker.\n"); print("npub/nsec keys can be converted to hex key format using https://damus.io/key"); } @@ -127,16 +127,16 @@ Future main(List arguments) async { if( argResults[relayArg] != null) { Set userRelayList = Set.from(argResults[relayArg].split(",")); Set parsedRelays = {}; - userRelayList.forEach((relay) { + for (var relay in userRelayList) { if(relay.startsWith(RegExp(r'^ws[s]?:\/\/'))) { parsedRelays.add(relay); } else { printWarning("The provided relay entry: \"$relay\" does not start with ws:// or wss://, omitting"); } - }); + } // verify that there is at least one valid relay they provided, otherwise keep defaults - if (parsedRelays.length > 0) { + if (parsedRelays.isNotEmpty) { gListRelayUrls = parsedRelays; defaultServerUrl = gListRelayUrls.first; } else { @@ -174,8 +174,9 @@ Future main(List arguments) async { try { var terminalColumns = gDefaultTextWidth; - if( stdout.hasTerminal ) + if( stdout.hasTerminal ) { terminalColumns = stdout.terminalColumns; + } // can be computed only after textWidth has been found if( gTextWidth > terminalColumns) { @@ -184,7 +185,7 @@ Future main(List arguments) async { gNumLeftMarginSpaces = (terminalColumns - gTextWidth )~/2; } on StdoutException catch (e) { print("Cannot find terminal size. Left aligning by default."); - if( gDebug > 0) log.info("${e.message}"); + if( gDebug > 0) log.info(e.message); gNumLeftMarginSpaces = 0; } // undo above if left option is given @@ -262,10 +263,10 @@ Future main(List arguments) async { stdout.write('Reading events from ${whetherDefault}file.......'); // read file events and give the events to relays from where they're picked up later - initialEvents = await readEventsFromFile(gEventsFilename); + initialEvents = readEventsFromFile(gEventsFilename); // count events - initialEvents.forEach((element) { numFileEvents++;}); + for (var element in initialEvents) { numFileEvents++;} print("read $numFileEvents events from file $gEventsFilename"); } @@ -338,7 +339,7 @@ Future main(List arguments) async { clearEvents(); - initialEvents.forEach((element) { element.eventData.kind == 1? numUserPosts++: numUserPosts;}); + for (var element in initialEvents) { element.eventData.kind == 1? numUserPosts++: numUserPosts;} numUserPosts -= numFilePosts; stdout.write("...done\n");//received $numUserPosts new posts made by the user\n"); @@ -353,7 +354,9 @@ Future main(List arguments) async { getKindEvents([40, 41], gListRelayUrls, limitPerSubscription, getSecondsDaysAgo(limitSelfEvents)); getKindEvents([42], gListRelayUrls, 3 * limitPerSubscription, getSecondsDaysAgo(limitOthersEvents)); - initialEvents.forEach((e) => processKind3Event(e)); // first process the kind 3 event ; basically populate the global structure that holds this info + for (var e in initialEvents) { + processKind3Event(e); + } // first process the kind 3 event ; basically populate the global structure that holds this info Set contacts = {}; Set pTags = {}; @@ -365,9 +368,9 @@ Future main(List arguments) async { // if contact list was found, get user's feed; also get some default contacts if (contactEvent != null ) { if(gDebug > 0) print("In main: found contact list: \n ${contactEvent.originalJson}"); - contactEvent.eventData.contactList.forEach((contact) { + for (var contact in contactEvent.eventData.contactList) { contacts.add(contact.contactPubkey); - }); + } } else { print("Could not find your contact list."); } diff --git a/lib/console_ui.dart b/lib/console_ui.dart index 6c36a20..ccda8c8 100644 --- a/lib/console_ui.dart +++ b/lib/console_ui.dart @@ -93,7 +93,7 @@ Future sendReplyPostLike(Store node, String replyToId, String replyKind, S int numShaDone = 0; for( numShaDone = 0; numShaDone < 100000000; numShaDone++) { - vanityTag = strTags + ',["nonce","$numShaDone","$gDifficulty"]'; + vanityTag = '$strTags,["nonce","$numShaDone","$gDifficulty"]'; id = getShaId(userPublicKey, createdAt.toString(), replyKind, vanityTag, content); if( id.substring(0, numBytes) == zeroString) { break; @@ -160,7 +160,7 @@ Future sendChannelReply(Store node, Channel channel, String replyTo, Strin // send DM Future sendDirectMessage(Store node, String otherPubkey, String messageToSend, {String replyKind = "4"}) async { //messageToSend = addEscapeChars(messageToSend); since this get encrypted , it does not need escaping - String otherPubkey02 = "02" + otherPubkey; + String otherPubkey02 = "02$otherPubkey"; String encryptedMessageToSend = myEncrypt(userPrivateKey, otherPubkey02, messageToSend); //print("in sendDirectMessage: replyKind = $replyKind"); @@ -271,7 +271,7 @@ bool sendDeleteEvent(Store node, String eventIdToDelete) { } else { print("Event not found. Kindly ensure you have entered a valid event id."); } - }; + } return false; } @@ -282,8 +282,9 @@ void reAdjustAlignment() { try { var terminalColumns = gDefaultTextWidth; - if( stdout.hasTerminal ) + if( stdout.hasTerminal ) { terminalColumns = stdout.terminalColumns; + } if( gTextWidth > terminalColumns) { gTextWidth = terminalColumns - 5; @@ -291,7 +292,7 @@ void reAdjustAlignment() { gNumLeftMarginSpaces = (terminalColumns - gTextWidth )~/2; } on StdoutException catch (e) { print("Terminal information not available"); - if( gDebug>0) print("${e.message}"); + if( gDebug>0) print(e.message); gNumLeftMarginSpaces = 0; } } @@ -317,7 +318,7 @@ void printProfile(Store node, String profilePubkey) { String picture = gKindONames[profilePubkey]?.picture??""; String lud06 = gKindONames[profilePubkey]?.lud06??""; String lud16 = gKindONames[profilePubkey]?.lud16??""; - String display_name= gKindONames[profilePubkey]?.display_name??""; + String displayName= gKindONames[profilePubkey]?.display_name??""; String website = gKindONames[profilePubkey]?.website??""; int dateLastUpdated = gKindONames[profilePubkey]?.createdAt??0; bool verified = gKindONames[profilePubkey]?.nip05Verified??false; @@ -334,7 +335,7 @@ void printProfile(Store node, String profilePubkey) { // print LNRUL lud06 if it exists if( lud06.length > gMinLud06AddressLength) { try { - String lud06LNString = "lightning:" + lud06; + String lud06LNString = "lightning:$lud06"; List? typesAndModule = getTypeAndModule(lud06LNString); if( typesAndModule != null) { @@ -349,7 +350,7 @@ void printProfile(Store node, String profilePubkey) { // print LNRUL lud16 if it exists if( lud16.length > gMinLud16AddressLength) { try { - String lud16LNString = "" + lud16; + String lud16LNString = lud16; List? typesAndModule = getTypeAndModule(lud16LNString); if( typesAndModule != null) { print("Printing lud16 address as QR:\n\n"); @@ -360,14 +361,14 @@ void printProfile(Store node, String profilePubkey) { } } - print("\nName : $authorName ( ${profilePubkey} )."); + print("\nName : $authorName ( $profilePubkey )."); print("About : $about"); print("Picture : $picture"); - print("display_name: $display_name"); + print("display_name: $displayName"); print("Website : $website"); print("Lud06 : $lud06"); print("Lud16 : $lud16"); - print("Nip 05 : ${verified?"yes. ${nip05Id}":"no"}"); + print("Nip 05 : ${verified?"yes. $nip05Id":"no"}"); print("\nLast Updated: ${getPrintableDate(dateLastUpdated)}\n"); // get the latest kind 3 event for the user, which lists his 'follows' list @@ -389,7 +390,9 @@ void printProfile(Store node, String profilePubkey) { // print follow list stdout.write("$pronoun follow ${profileContactEvent.eventData.contactList.length} accounts: "); profileContactEvent.eventData.contactList.sort(); - profileContactEvent.eventData.contactList.forEach((x) => stdout.write("${getAuthorName(x.contactPubkey)}, ")); + for (var x in profileContactEvent.eventData.contactList) { + stdout.write("${getAuthorName(x.contactPubkey)}, "); + } print("\n"); } @@ -397,7 +400,9 @@ void printProfile(Store node, String profilePubkey) { List followers = node.getFollowers(profilePubkey); stdout.write("$pronoun have ${followers.length} followers: "); followers.sort((a, b) => getAuthorName(a).compareTo(getAuthorName(b))); - followers.forEach((x) => stdout.write("${getAuthorName(x)}, ")); + for (var x in followers) { + stdout.write("${getAuthorName(x)}, "); + } print(""); print(""); } @@ -436,11 +441,13 @@ void printMenu(List menuOptions) { var terminalColumns = gDefaultTextWidth; - if( stdout.hasTerminal ) + if( stdout.hasTerminal ) { terminalColumns = stdout.terminalColumns; + } - if( longestMenuOption + 5> gMenuWidth ) + if( longestMenuOption + 5> gMenuWidth ) { gMenuWidth = longestMenuOption + 8; + } if( terminalColumns~/gMenuWidth > 4) { terminalColumns = gMenuWidth * 4; @@ -463,7 +470,7 @@ void printMenu(List menuOptions) { int showMenu(List menuOptions, String menuName, [String menuInfo = ""]) { - if(menuInfo.length > 0) { + if(menuInfo.isNotEmpty) { print("\n$menuInfo\n"); } @@ -531,13 +538,15 @@ Do you want to proceed. Press y/Y or n/N: """, "n"); void printPubkeys(Set pubkey) { print("${myPadRight("pubkey",64)} ${myPadRight("name", 20)} ${myPadRight("about", 40)} ${myPadRight("Nip05", 30)}"); - pubkey.forEach( (x) => print("$x ${myPadRight(getAuthorName(x), 20)} ${myPadRight(gKindONames[x]?.about??"", 40)} ${myPadRight(gKindONames[x]?.nip05Id??"No", 30)}")); + for (var x in pubkey) { + print("$x ${myPadRight(getAuthorName(x), 20)} ${myPadRight(gKindONames[x]?.about??"", 40)} ${myPadRight(gKindONames[x]?.nip05Id??"No", 30)}"); + } print(""); } void printPubkeyResult(Set pubkey) { - if( pubkey.length == 0) { + if( pubkey.isEmpty) { print("There is no pubkey for that given name."); return; } else { @@ -599,18 +608,18 @@ Future otherOptionsMenuUi(Store node) async { String userName = getStringFromUser("Enter your new name : ", getAuthorName(userPublicKey)); String userAbout = getStringFromUser("Enter new 'about me' for yourself : ", gKindONames[userPublicKey]?.about??""); String userPic = getStringFromUser("Enter url to your new display picture: ", gKindONames[userPublicKey]?.picture??"https://placekitten.com/200/200"); - String display_name = getStringFromUser("Enter your new display name : ", gKindONames[userPublicKey]?.display_name??""); + String displayName = getStringFromUser("Enter your new display name : ", gKindONames[userPublicKey]?.display_name??""); String website = getStringFromUser("Enter your new website : ", gKindONames[userPublicKey]?.website??""); String nip05id = getStringFromUser("Enter your nip 05 id. Leave blank if unknown/none: ", gKindONames[userPublicKey]?.nip05Id??""); String lud06 = getStringFromUser("Enter your lud06 or lnurl. Leave blank if unknown/none: ", gKindONames[userPublicKey]?.lud06??""); String lud16 = getStringFromUser("Enter your lud16 address. Leave blank if unknown/none: ", gKindONames[userPublicKey]?.lud16??""); - String strLud06 = lud06.length > 0? '"lud06":"$lud06",': ''; - String strLud16 = lud16.length > 0? '"lud16":"$lud16",': ''; - String strDispName = display_name.length > 0? '"display_name":"$display_name",': ''; - String strWebsite = website.length > 0? '"website":"$website",': ''; + String strLud06 = lud06.isNotEmpty? '"lud06":"$lud06",': ''; + String strLud16 = lud16.isNotEmpty? '"lud16":"$lud16",': ''; + String strDispName = displayName.isNotEmpty? '"display_name":"$displayName",': ''; + String strWebsite = website.isNotEmpty? '"website":"$website",': ''; - String content = "{\"name\": \"$userName\", \"about\": \"$userAbout\", \"picture\": \"$userPic\"${ nip05id.length >0 ? ", $strDispName $strWebsite $strLud06 $strLud16 \"nip05\": \"$nip05id\"":""}}"; + String content = "{\"name\": \"$userName\", \"about\": \"$userAbout\", \"picture\": \"$userPic\"${ nip05id.isNotEmpty ? ", $strDispName $strWebsite $strLud06 $strLud16 \"nip05\": \"$nip05id\"":""}}"; int createdAt = DateTime.now().millisecondsSinceEpoch ~/1000; EventData eventData = EventData('id', userPublicKey, createdAt, 0, content, [], [], [], [], {}, ); @@ -633,11 +642,11 @@ Future otherOptionsMenuUi(Store node) async { if( eventIdToDelete.length == 1) { String toDeleteId = eventIdToDelete.first; - print("Going to send a delete event for the following event with id ${toDeleteId}"); + print("Going to send a delete event for the following event with id $toDeleteId"); sendDeleteEvent(node, eventIdToDelete.first); await processAnyIncomingEvents(node, false); // get latest event, this takes 300 ms } else { - if( eventIdToDelete.length == 0) { + if( eventIdToDelete.isEmpty) { printWarning("Could not find the given event id. Kindly try again, by entering a 64 byte long hex event id, or by entering a unique prefix for the given event id."); } else { printWarning("Invalid Event Id(s). Kindly enter a more unique id."); @@ -802,8 +811,9 @@ Future channelMenuUI(Store node) async { Channel? channel = node.getChannelFromId(node.channels, fullChannelId); String actualMessage = messageToSend.substring(7); - if( messageToSend.indexOf(tokens[1]) + tokens[1].length < messageToSend.length) + if( messageToSend.indexOf(tokens[1]) + tokens[1].length < messageToSend.length) { actualMessage = messageToSend.substring( messageToSend.indexOf(tokens[1]) + tokens[1].length + 1); + } if( channel != null) { await sendChannelReply(node, channel, replyTo, actualMessage, getPostKindFrom( channel.roomType)); @@ -945,7 +955,7 @@ String encryptChannelMessage(Store node, String channelId, String messageToSend) } String priKey = keys[0], pubKey = keys[1]; - encryptedMessage = myEncrypt(priKey, "02" + pubKey, messageToSend); + encryptedMessage = myEncrypt(priKey, "02$pubKey", messageToSend); return encryptedMessage; } @@ -983,9 +993,9 @@ Future addUsersToEncryptedChannel(Store node, String fullChannelId, Set encryptedChannelMenuUI(Store node) async { String replyTo = tokens[1]; String actualMessage = messageToSend.substring(7); - if( messageToSend.indexOf(tokens[1]) + tokens[1].length < messageToSend.length) + if( messageToSend.indexOf(tokens[1]) + tokens[1].length < messageToSend.length) { actualMessage = messageToSend.substring( messageToSend.indexOf(tokens[1]) + tokens[1].length + 1); + } String encryptedMessageToSend = encryptChannelMessage(node, fullChannelId, actualMessage); if( encryptedMessageToSend != "") { @@ -1439,7 +1450,9 @@ Future socialMenuUi(Store node) async { if( numPrinted[0] == 0) { print("\nNot found in the last $gHoursDefaultPrint hours. Try increasing the number of days printed, from social network options to search further back into history.\n"); } - } else printWarning("Blank word entered. Try again."); + } else { + printWarning("Blank word entered. Try again."); + } break; @@ -1542,7 +1555,7 @@ Future socialMenuUi(Store node) async { stdout.write("Printing profile of a user; type username or first few letters of user's public key( or full public key): "); String? $tempUserName = stdin.readLineSync(); String userName = $tempUserName??""; - stdout.write( "user name: " + userName); + stdout.write( "user name: $userName"); if( userName != "") { Set pubkey = getPublicKeyFromName(userName); @@ -1578,7 +1591,7 @@ Future socialMenuUi(Store node) async { continue; } on Exception catch (e) { printWarning("Invalid input. Kindly try again."); - if( gDebug > 0) print(" ${e}"); + if( gDebug > 0) print(" $e"); continue; } break; @@ -1600,8 +1613,9 @@ void directRoomNotifications(Store node, [int x = 0, int y = 0]) { bool showNotifications (ScrollableMessages room) => room.selectorNotifications(); int numDirectRoomsPrinted = node.printDirectRoomsOverview( showNotifications, 100, node.allChildEventsMap); - if( numDirectRoomsPrinted > 0) - print("\n"); + if( numDirectRoomsPrinted > 0) { + print("\n"); + } int totalNotifications = numPrinted[2] + numDirectRoomsPrinted; if( totalNotifications > 0) { @@ -1691,7 +1705,7 @@ Future mainMenuUi(Store node) async { mainMenuContinue = false; String authorName = getAuthorName(userPublicKey); clearScreen(); - print("\nFinished Nostr session for user: ${authorName} ($userPublicKey)"); + print("\nFinished Nostr session for user: $authorName ($userPublicKey)"); if( gEventsFilename != "") { await node.writeEventsToFile(gEventsFilename); } diff --git a/lib/event_ds.dart b/lib/event_ds.dart index c579a28..74c9484 100644 --- a/lib/event_ds.dart +++ b/lib/event_ds.dart @@ -38,7 +38,7 @@ class UserNameInfo { Event ?latestContactEvent; bool nip05Verified; String? nip05Id; - UserNameInfo(this.createdAt, this.name, this.about, this.picture, this.lud06, this.lud16, this.display_name, this.website, this.nip05Id , this.latestContactEvent, [this.createdAtKind3 = null, this.nip05Verified = false]); + UserNameInfo(this.createdAt, this.name, this.about, this.picture, this.lud06, this.lud16, this.display_name, this.website, this.nip05Id , this.latestContactEvent, [this.createdAtKind3, this.nip05Verified = false]); } /* @@ -59,7 +59,7 @@ Set getReactorPubkeys(String eventId) { List>? reactions = gReactions[eventId]; if( reactions != null) { - reactions.forEach((reaction) { reactorIds.add(reaction[0]);}); + for (var reaction in reactions) { reactorIds.add(reaction[0]);} } return reactorIds; @@ -157,22 +157,22 @@ class EventData { } // then depending on the numbers and values ( of root and replyto) return the parent - if( replyId.length > 0) { + if( replyId.isNotEmpty) { if( numReply == 1) { return replyId; } else { // if there are multiply reply's we can't tell which is which, so we return the one at top - if( replyId.length > 0) { + if( replyId.isNotEmpty) { return replyId; } else { // this is case when there is no reply id . should not actually happen given if conditions - if( rootId.length > 0) { + if( rootId.isNotEmpty) { return rootId; } } } } else { - if( rootId.length > 0) { + if( rootId.isNotEmpty) { //printWarning("returning root id. no reply id found."); return rootId; } @@ -209,7 +209,7 @@ class EventData { } List? getTTags() { - List? tTags = null; + List? tTags; for( int i = 0; i < tags.length; i++) { List tag = tags[i]; @@ -217,9 +217,7 @@ class EventData { continue; } if( tag[0] == 't') { - if( tTags == null ) { - tTags = []; - } + tTags ??= []; tTags.add(tag[1]); } @@ -268,7 +266,7 @@ class EventData { try { verifyEvent(json); - } on Exception catch(e) { + } on Exception { //printWarning("verify gave exception $e"); throw Exception("in Event constructor: sig verify gave exception"); } @@ -364,7 +362,7 @@ class EventData { String author = getAuthorName(mentionedId); return "@$author"; } else { - EventData? eventData = tempChildEventsMap[mentionedId]?.event.eventData??null; + EventData? eventData = tempChildEventsMap[mentionedId]?.event.eventData; if( eventData != null) { String quotedAuthor = getAuthorName(eventData.pubkey); String prefixId = mentionedId.substring(0, 3); @@ -401,7 +399,7 @@ class EventData { } // replace the mentions, if any are found - String mentionStr = "(\#\[[0-9]+\])"; + String mentionStr = "(#[[0-9]+])"; RegExp mentionRegExp = RegExp(mentionStr); content = content.replaceAllMapped(mentionRegExp, replaceMentions); return content; @@ -468,7 +466,7 @@ class EventData { return null; } - if(!isValidDirectMessage(this, acceptableKind: this.kind)) { + if(!isValidDirectMessage(this, acceptableKind: kind)) { return null; } @@ -577,19 +575,19 @@ class EventData { int ivIndex = content.indexOf("?iv="); if( ivIndex > 0) { var iv = content.substring( ivIndex + 4, content.length); - var enc_str = content.substring(0, ivIndex); + var encStr = content.substring(0, ivIndex); String userKey = userPrivateKey ; - String otherUserPubKey = "02" + pubkey; + String otherUserPubKey = "02$pubkey"; if( pubkey == userPublicKey) { // if user themselve is the sender change public key used to decrypt userKey = userPrivateKey; int numPtags = 0; - tags.forEach((tag) { + for (var tag in tags) { if(tag[0] == "p" ) { - otherUserPubKey = "02" + tag[1]; + otherUserPubKey = "02${tag[1]}"; numPtags++; } - }); + } // if there are more than one p tags, we don't know who its for if( numPtags != 1) { if( gDebug >= 0) printInColor(" in translateAndExpand: got event $id with number of p tags != one : $numPtags . not decrypting", redColor); @@ -597,7 +595,7 @@ class EventData { } } - var decrypted = myPrivateDecrypt( userKey, otherUserPubKey, enc_str, iv); // use bob's privatekey and alic's publickey means bob can read message from alic + var decrypted = myPrivateDecrypt( userKey, otherUserPubKey, encStr, iv); // use bob's privatekey and alic's publickey means bob can read message from alic return decrypted; } else { if(gDebug > 0) print("Invalid content for dm, could not get ivIndex: $content"); @@ -629,7 +627,7 @@ class EventData { return null; } var iv = content.substring( ivIndex + 4, content.length); - var enc_str = content.substring(0, ivIndex); + var encStr = content.substring(0, ivIndex); String channelId = getChannelIdForKind4x(); List keys = []; @@ -642,9 +640,9 @@ class EventData { } String priKey = keys[0]; - String pubKey = "02" + keys[1]; + String pubKey = "02${keys[1]}"; - var decrypted = myPrivateDecrypt( priKey, pubKey, enc_str, iv); // use bob's privatekey and alic's publickey means bob can read message from alic + var decrypted = myPrivateDecrypt( priKey, pubKey, encStr, iv); // use bob's privatekey and alic's publickey means bob can read message from alic return decrypted; } @@ -657,7 +655,7 @@ class EventData { // get first e tag, which should be the channel of which this is part of for( int i = 0; i < eTags.length; i++) { List tag = eTags[i]; - if( tag.length >= 1) { + if( tag.isNotEmpty) { return tag[0]; } } @@ -665,7 +663,7 @@ class EventData { } String getChannelIdForTTagRoom(String tagValue) { - return tagValue + " #t"; + return "$tagValue #t"; } // only applicable for kind 42/142 event; returns the channel 40/140 id of which the event is part of @@ -687,9 +685,9 @@ class EventData { // will only do decryption if its not been decrypted yet by looking at 'evaluatedContent' if( tempChildEventsMap != null ) - if(kind == 4) + if(kind == 4) { translateAndDecryptKind4( tempChildEventsMap); - else if ([1, 42].contains(kind)) { + } else if ([1, 42].contains(kind)) { translateAndExpandMentions(tempChildEventsMap); } else if ([142].contains(kind)) { if( secretMessageIds != null && encryptedChannels != null) { @@ -752,7 +750,7 @@ class EventData { strToPrint += "█"; } - strToPrint += "${name}: "; + strToPrint += "$name: "; const int typicalxLen = "|id: 82b5 , 12:04 AM Sep 19".length + 5; // not sure where 5 comes from List reactionString = getReactionStr(depth); //print("\n|${reactionString[0]}|\n ${ reactionString[1]}\n }"); @@ -781,8 +779,9 @@ class EventData { // effective len of last line is used to calcluate where the idDateLikes str is affixed at the end int effectiveLastLineLen = lastLineLen - gSpacesPerDepth * depth - effectiveNameFieldLen - gNumLeftMarginSpaces; - if( contentShifted.length <= maxLineLen ) + if( contentShifted.length <= maxLineLen ) { effectiveLastLineLen = contentShifted.length; + } // needed to use this because the color padding in notifications reactions will mess up the length calculation in the actual reaction string int colorStrLen = reactionString[0].length - reactionString[1]; @@ -791,20 +790,20 @@ class EventData { if( (gSpacesPerDepth * depth + effectiveNameFieldLen + effectiveLastLineLen + idDateLikes.length ) <= gTextWidth) { idDateLikes = idDateLikes.padLeft((gTextWidth ) + colorStrLen - (gSpacesPerDepth * depth + effectiveNameFieldLen + effectiveLastLineLen)); } else { - idDateLikes = "\n" + idDateLikes.padLeft(gNumLeftMarginSpaces + gTextWidth + colorStrLen); + idDateLikes = "\n${idDateLikes.padLeft(gNumLeftMarginSpaces + gTextWidth + colorStrLen)}"; } // print content and the dateslikes string - strToPrint += getStrInColor(contentShifted + idDateLikes + "\n", commentColor); + strToPrint += getStrInColor("$contentShifted$idDateLikes\n", commentColor); stdout.write(strToPrint); } String getAsLine(var tempChildEventsMap, Set? secretMessageIds, List? encryptedChannels, {int len = 20}) { // will only do decryption if its not been decrypted yet by looking at 'evaluatedContent' - if(kind == 4) + if(kind == 4) { translateAndDecryptKind4( tempChildEventsMap); - else if ([1, 42].contains(kind)) { + } else if ([1, 42].contains(kind)) { translateAndExpandMentions(tempChildEventsMap); } else if ([142].contains(kind)) { if( tempChildEventsMap != null && secretMessageIds != null && encryptedChannels != null) { @@ -839,12 +838,12 @@ class EventData { // will only do decryption if its not been decrypted yet by looking at 'evaluatedContent' // will only do decryption if its not been decrypted yet by looking at 'evaluatedContent' - if(kind == 4) + if(kind == 4) { translateAndDecryptKind4( tempChildEventsMap); - else if ([1, 42].contains(kind)) { + } else if ([1, 42].contains(kind)) { translateAndExpandMentions(tempChildEventsMap); } else if ([142].contains(kind)) { - if( tempChildEventsMap != null && secretMessageIds != null && encryptedChannels != null) { + if( secretMessageIds != null && encryptedChannels != null) { //print('decrypting 14x in getStrForChannel'); translateAndDecrypt14x(secretMessageIds, encryptedChannels, tempChildEventsMap); } @@ -867,8 +866,9 @@ class EventData { tempEvaluatedContent = tempContent = content; // content would be changed so show that } - if( tempEvaluatedContent=="") + if( tempEvaluatedContent=="") { tempEvaluatedContent = tempContent; + } const int nameWidthDepth = 16~/gSpacesPerDepth; // how wide name will be in depth spaces const int timeWidthDepth = 18~/gSpacesPerDepth; @@ -900,13 +900,13 @@ class EventData { if( replyToEvent.eventData.evaluatedContent.length <= gReplyLengthPrinted){ replyToPrint = replyToEvent.eventData.evaluatedContent; } else { - replyToPrint = replyToEvent.eventData.evaluatedContent.substring(0, gReplyLengthPrinted) + "..."; + replyToPrint = "${replyToEvent.eventData.evaluatedContent.substring(0, gReplyLengthPrinted)}..."; } strReplyTo = 'In reply to:"${getAuthorName(replyToEvent.eventData.pubkey)}: $replyToPrint"'; strReplyTo = makeParagraphAtDepth(strReplyTo, finalContentDepthInSpaces + 6); // one extra for content // add reply to string to end of the content. How it will show: - contentShifted += ( "\n" + getNumSpaces( contentPlacementColumn + gSpacesPerDepth) + strReplyTo); + contentShifted += ( "\n${getNumSpaces( contentPlacementColumn + gSpacesPerDepth)}$strReplyTo"); } } } else { @@ -916,10 +916,10 @@ class EventData { String msgId = id.substring(0, 3).padLeft(gSpacesPerDepth~/2).padRight(gSpacesPerDepth) ; if( isNotification) { - strToPrint = "$gNotificationColor${getDepthSpaces(depth-1)}$msgId $dateToPrint $nameToPrint: $gNotificationColor" + contentShifted + gColorEndMarker; + strToPrint = "$gNotificationColor${getDepthSpaces(depth-1)}$msgId $dateToPrint $nameToPrint: $gNotificationColor$contentShifted$gColorEndMarker"; isNotification = false; } else { - strToPrint = "${getDepthSpaces(depth-1)}$msgId $dateToPrint $nameToPrint: " + contentShifted; + strToPrint = "${getDepthSpaces(depth-1)}$msgId $dateToPrint $nameToPrint: $contentShifted"; } return strToPrint; } @@ -956,8 +956,9 @@ class EventData { firstEntry = false; } else { // this is normal printing of the reaction. only print for + for now - if( reactors[i][1] == "+") + if( reactors[i][1] == "+") { authorName = getAuthorName(reactorId); + } reactorNames += comma + authorName; len += (2 + authorName.length); firstEntry = false; @@ -979,14 +980,14 @@ class EventData { // returns the last e tag as reply to event for kind 42 and 142 events Event? getReplyToChannelEvent(Map tempChildEventsMap) { - switch (this.kind) { + switch (kind) { case 42: case 142: for(int i = tags.length - 1; i >= 0; i--) { List tag = tags[i]; if( tag[0] == 'e') { String replyToEventId = tag[1]; - Event? eventInReplyTo = (gStore?.allChildEventsMap[replyToEventId]?.event)??null; + Event? eventInReplyTo = (gStore?.allChildEventsMap[replyToEventId]?.event); if( eventInReplyTo != null) { // add 1 cause 42 can reply to or tag kind 1, and we'll show that kind 1 if ( [1,42,142].contains( eventInReplyTo.eventData.kind)) { @@ -1039,14 +1040,15 @@ class Event { } EventData newEventData = EventData.fromJson(json[2]); - if( !fromFile) + if( !fromFile) { newEventData.isNotification = true; + } return Event(json[0] as String, json[1] as String, newEventData, [relay], d, fromFile ); } on Exception catch(e) { if( gDebug > 0) { print("Could not create event. $e\nproblem str: $d\n"); } - throw e; + rethrow; } } @@ -1115,7 +1117,7 @@ bool processKind0Event(Event e) { String picture = ""; String lud06 = ""; String lud16 = ""; - String display_name = ""; + String displayName = ""; String website = ""; String nip05 = ""; @@ -1126,7 +1128,7 @@ bool processKind0Event(Event e) { picture = json["picture"]??""; lud06 = json["lud06"]??""; lud16 = json["lud16"]??""; - display_name = json["display_name"]??""; + displayName = json["display_name"]??""; website = json["website"]??""; nip05 = json['nip05']??""; //String twitterId = json['twitter']??""; @@ -1137,12 +1139,12 @@ bool processKind0Event(Event e) { bool newEntry = false, entryModified = false; if( !gKindONames.containsKey(e.eventData.pubkey)) { - gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture, lud06, lud16, display_name, website, nip05, null); - newEntry = true;; + gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture, lud06, lud16, displayName, website, nip05, null); + newEntry = true; } 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, lud06, lud16, display_name, website, nip05, null); + gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture, lud06, lud16, displayName, website, nip05, null); entryModified = true; } } @@ -1154,13 +1156,13 @@ bool processKind0Event(Event e) { bool localDebug = false; //e.eventData.pubkey == "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"? true: false; if( newEntry || entryModified) { - if(nip05.length > 0) { + if(nip05.isNotEmpty) { List urlSplit = nip05.split("@"); if( urlSplit.length == 2) { - String urlNip05 = urlSplit[1] + "/.well-known/nostr.json?name=" + urlSplit[0]; + String urlNip05 = "${urlSplit[1]}/.well-known/nostr.json?name=${urlSplit[0]}"; if( !urlNip05.startsWith("http")) { - urlNip05 = "http://"+ urlNip05; + urlNip05 = "http://$urlNip05"; } fetchNip05Info(urlNip05) @@ -1178,7 +1180,7 @@ bool processKind0Event(Event e) { int oldTime = 0; if( !gKindONames.containsKey(e.eventData.pubkey)) { //printWarning("in response handing. creating user info"); - gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture, lud06, lud16, display_name, website,null, null); + gKindONames[e.eventData.pubkey] = UserNameInfo(e.eventData.createdAt, name, about, picture, lud06, lud16, displayName, website,null, null); } else { oldTime = gKindONames[e.eventData.pubkey]?.createdAt??0; //print("in response handing. user info exists with old time = $oldTime and this event time = ${e.eventData.createdAt}"); @@ -1215,23 +1217,23 @@ bool processKind3Event(Event newContactEvent) { bool newEntry = false, entryModified = false; if( !gKindONames.containsKey(newContactEvent.eventData.pubkey)) { gKindONames[newContactEvent.eventData.pubkey] = UserNameInfo(null, null, null, null, null, null, null, null, null, newContactEvent, newContactEvent.eventData.createdAt); - newEntry = true;; + newEntry = true; } else { // if entry already exists, then check its old time and update only if we have a newer entry now int oldTime = gKindONames[newContactEvent.eventData.pubkey]?.createdAtKind3??0; if( oldTime < newContactEvent.eventData.createdAt) { - int? createdAt = gKindONames[newContactEvent.eventData.pubkey]?.createdAt??null; + int? createdAt = gKindONames[newContactEvent.eventData.pubkey]?.createdAt; String? name = gKindONames[newContactEvent.eventData.pubkey]?.name, about = gKindONames[newContactEvent.eventData.pubkey]?.about, picture = gKindONames[newContactEvent.eventData.pubkey]?.picture, lud06 = gKindONames[newContactEvent.eventData.pubkey]?.lud06, lud16 = gKindONames[newContactEvent.eventData.pubkey]?.lud16, - display_name = gKindONames[newContactEvent.eventData.pubkey]?.display_name, + displayName = gKindONames[newContactEvent.eventData.pubkey]?.display_name, website = gKindONames[newContactEvent.eventData.pubkey]?.website, nip05id = gKindONames[newContactEvent.eventData.pubkey]?.nip05Id??""; - gKindONames[newContactEvent.eventData.pubkey] = UserNameInfo(createdAt, name, about, picture, lud06, lud16, display_name, website, nip05id, newContactEvent, newContactEvent.eventData.createdAt ); - entryModified = true;; + gKindONames[newContactEvent.eventData.pubkey] = UserNameInfo(createdAt, name, about, picture, lud06, lud16, displayName, website, nip05id, newContactEvent, newContactEvent.eventData.createdAt ); + entryModified = true; } } @@ -1260,16 +1262,16 @@ String getNip05Name( String pubkey) { // returns name by looking up global list gKindONames, which is populated by kind 0 events String getAuthorName(String pubkey, {int maxDisplayLen = gMaxInteger, int pubkeyLenShown = 5}) { - if( gFollowList.length == 0) { + if( gFollowList.isEmpty) { gFollowList = getFollows(userPublicKey); } bool isFollow = gFollowList.contains(pubkey) && (pubkey != userPublicKey); String maxLen(String pubkey) => pubkey.length > pubkeyLenShown? pubkey.substring(0,pubkeyLenShown) : pubkey.substring(0, pubkey.length); String name = ""; - if( gKindONames[pubkey]?.name == null || gKindONames[pubkey]?.name?.length == 0) - name = maxLen(pubkey); - else { + if( gKindONames[pubkey]?.name == null || gKindONames[pubkey]?.name?.length == 0) { + name = maxLen(pubkey); + } else { name = (gKindONames[pubkey]?.name)??maxLen(pubkey); } @@ -1291,7 +1293,7 @@ String getAuthorName(String pubkey, {int maxDisplayLen = gMaxInteger, int pubkey // returns full public key(s) for the given username( which can be first few letters of pubkey, or the user name) Set getPublicKeyFromName(String inquiredName) { - if( inquiredName.length < 1) { + if( inquiredName.isEmpty) { return {}; } Set pubkeys = {}; @@ -1337,8 +1339,9 @@ void printDepth(int d) { void printCenteredHeadline(displayName) { int numDashes = 10; // num of dashes on each side int startText = gNumLeftMarginSpaces + ( gTextWidth - (displayName.length + 2 * numDashes)) ~/ 2; - if( startText < 0) + if( startText < 0) { startText = 0; + } String str = getNumSpaces(startText) + getNumDashes(numDashes) + displayName + getNumDashes(numDashes); print(str); @@ -1369,7 +1372,7 @@ String makeParagraphAtDepth(String s, int depthInSpaces) { String line = listCulledLine[0]; int lenReturned = listCulledLine[1] as int; - if( line.length == 0 || lenReturned == 0) break; + if( line.isEmpty || lenReturned == 0) break; newString += line; startIndex += lenReturned; @@ -1381,8 +1384,9 @@ String makeParagraphAtDepth(String s, int depthInSpaces) { // returns from string[startIndex:] the first len number of chars. no newline is added. List getLineWithMaxLen(String s, int startIndex, int lenPerLine, String spacesString, List> urlRanges) { - if( startIndex >= s.length) + if( startIndex >= s.length) { return ["", 0]; + } String line = ""; // is returned @@ -1420,7 +1424,9 @@ List getLineWithMaxLen(String s, int startIndex, int lenPerLine, String spacesSt int i = line.length - 1; // find a whitespace character - for( ; i > 0 && !isWordSeparater(line[i]); i--); + for( ; i > 0 && !isWordSeparater(line[i]); i--) { + {} + } // for ended if( line.length - i < gMaxLenUnbrokenWord) { @@ -1428,9 +1434,10 @@ List getLineWithMaxLen(String s, int startIndex, int lenPerLine, String spacesSt // break the line here if its a word separator if( isWordSeparater(line[i])) { int newLineStart = i + 1; - if( line[i] != ' ') + if( line[i] != ' ') { newLineStart = i; - line = line.substring(0, i) + "\n" + spacesString + line.substring(newLineStart, line.length); + } + line = "${line.substring(0, i)}\n$spacesString${line.substring(newLineStart, line.length)}"; lineBroken = true; } } @@ -1494,16 +1501,16 @@ bool isValidDirectMessage(EventData directMessageData, {int acceptableKind = 4}) bool validUserMessage = false; List allPtags = []; - directMessageData.tags.forEach((tag) { + for (var tag in directMessageData.tags) { if( tag.length < 2 ) { - return; + continue; } if( tag[0] == "p" && tag[1].length == 64) { // basic length sanity test allPtags.add(tag[1]); } - }); + } - if(gDebug >= 0 && gCheckEventId == directMessageData.id) print("In isvalid direct message: ptags len: ${allPtags.length}, ptags = ${allPtags}"); + if(gDebug >= 0 && gCheckEventId == directMessageData.id) print("In isvalid direct message: ptags len: ${allPtags.length}, ptags = $allPtags"); if( directMessageData.pubkey == userPublicKey && allPtags.length == 1) { if( allPtags[0].substring(0, 32) != "0".padLeft(32, '0')) { // check that the message hasn't been sent to an invalid pubkey @@ -1524,16 +1531,16 @@ bool isValidDirectMessage(EventData directMessageData, {int acceptableKind = 4}) String getRandomPrivKey() { FortunaRandom fr = FortunaRandom(); - final _sGen = Random.secure();; + final sGen = Random.secure(); fr.seed(KeyParameter( - Uint8List.fromList(List.generate(32, (_) => _sGen.nextInt(255))))); + Uint8List.fromList(List.generate(32, (_) => sGen.nextInt(255))))); BigInt randomNumber = fr.nextBigInteger(256); String strKey = randomNumber.toRadixString(16); if( strKey.length < 64) { int numZeros = 64 - strKey.length; for(int i = 0; i < numZeros; i++) { - strKey = "0" + strKey; + strKey = "0$strKey"; } } return strKey; @@ -1568,7 +1575,7 @@ Uint8List myPrivateDecryptRaw( String privateString, } if( byteSecret.isEmpty) { - byteSecret = Kepler.byteSecret(privateString, publicString);; + byteSecret = Kepler.byteSecret(privateString, publicString); gMapByteSecret[publicString] = byteSecret; } @@ -1578,11 +1585,11 @@ Uint8List myPrivateDecryptRaw( String privateString, ? convert.base64.decode(b64IV) : Uint8List.fromList(secretIV[1]); - CipherParameters params = new PaddedBlockCipherParameters( - new ParametersWithIV(new KeyParameter(key), iv), null); + CipherParameters params = PaddedBlockCipherParameters( + ParametersWithIV(KeyParameter(key), iv), null); - PaddedBlockCipherImpl cipherImpl = new PaddedBlockCipherImpl( - new PKCS7Padding(), new CBCBlockCipher(new AESEngine())); + PaddedBlockCipherImpl cipherImpl = PaddedBlockCipherImpl( + PKCS7Padding(), CBCBlockCipher(AESEngine())); cipherImpl.init(false, params as PaddedBlockCipherParameters _sGen.nextInt(255))))); + Uint8List.fromList(List.generate(32, (_) => sGen.nextInt(255))))); final iv = fr.nextBytes(16); - CipherParameters params = new PaddedBlockCipherParameters( - new ParametersWithIV(new KeyParameter(key), iv), null); + CipherParameters params = PaddedBlockCipherParameters( + ParametersWithIV(KeyParameter(key), iv), null); - PaddedBlockCipherImpl cipherImpl = new PaddedBlockCipherImpl( - new PKCS7Padding(), new CBCBlockCipher(new AESEngine())); + PaddedBlockCipherImpl cipherImpl = PaddedBlockCipherImpl( + PKCS7Padding(), CBCBlockCipher(AESEngine())); cipherImpl.init(true, // means to encrypt params as PaddedBlockCipherParameters readEventsFromFile(String filename) { Set events = {}; final File file = File(filename); @@ -1709,7 +1714,7 @@ String myGetPublicKey(String prikey) { if( pubkey.length < 64) { int numZeros = 64 - pubkey.length; for(int i = 0; i < numZeros; i++) { - pubkey = "0" + pubkey; + pubkey = "0$pubkey"; } } return pubkey; diff --git a/lib/relays.dart b/lib/relays.dart index 7abb949..f07006a 100644 --- a/lib/relays.dart +++ b/lib/relays.dart @@ -63,7 +63,7 @@ class Relays { void getKindEvents(List kind, String relayUrl, int limit, int sinceWhen) { kind.toString(); - String subscriptionId = "kind_" + kind.toString() + "_" + relayUrl.substring(6); + String subscriptionId = "kind_${kind}_${relayUrl.substring(6)}"; String request = getKindRequest(subscriptionId, kind, limit, sinceWhen); sendRequest(relayUrl, request); @@ -79,20 +79,21 @@ class Relays { } } - String subscriptionId = "single_user" + (relays[relayUrl]?.numRequestsSent??"").toString() + "_" + relayUrl.substring(6); + String subscriptionId = "single_user${relays[relayUrl]?.numRequestsSent??""}_${relayUrl.substring(6)}"; if( relays.containsKey(relayUrl)) { Set? users = relays[relayUrl]?.users; if( users != null) { // get a user only if it has not already been requested // following is too restrictive casuse changed sinceWhen is not considered. TODO improve it bool alreadyRecevied = false; - users.forEach((user) { + for (var user in users) { if( user == publicKey) { alreadyRecevied = true; } - }); + } - if( alreadyRecevied) + if( alreadyRecevied) { return; + } users.add(publicKey); } @@ -110,7 +111,7 @@ class Relays { } } - String subscriptionId = "mention" + (relays[relayUrl]?.numRequestsSent??"").toString() + "_" + relayUrl.substring(6); + String subscriptionId = "mention${relays[relayUrl]?.numRequestsSent??""}_${relayUrl.substring(6)}"; String request = getMentionRequest(subscriptionId, ids, limit, sinceWhen, tagToGet); sendRequest(relayUrl, request); @@ -118,7 +119,7 @@ class Relays { void getIdAndMentionEvents(String relayUrl, Set ids, int limit, int idSinceWhen, int mentionSinceWhen, String tagToGet, String idType) { - String subscriptionId = "id_mention_tag" + (relays[relayUrl]?.numRequestsSent??"").toString() + "_" + relayUrl.substring(6); + String subscriptionId = "id_mention_tag${relays[relayUrl]?.numRequestsSent??""}_${relayUrl.substring(6)}"; String request = getIdAndMentionRequest(subscriptionId, ids, limit, idSinceWhen, mentionSinceWhen, tagToGet, idType); sendRequest(relayUrl, request); } @@ -128,7 +129,7 @@ class Relays { * @connect Connect to given relay and get all events for multiple users/publicKey and insert the * received events in the given List */ - void getMultiUserEvents(String relayUrl, List publicKeys, int limit, int sinceWhen, [Set? kind = null]) { + void getMultiUserEvents(String relayUrl, List publicKeys, int limit, int sinceWhen, [Set? kind]) { Set setPublicKeys = publicKeys.toSet(); if( relays.containsKey(relayUrl)) { @@ -139,7 +140,7 @@ class Relays { } } - String subscriptionId = "multiple_user" + (relays[relayUrl]?.numRequestsSent??"").toString() + "_" + relayUrl.substring(6); + String subscriptionId = "multiple_user${relays[relayUrl]?.numRequestsSent??""}_${relayUrl.substring(6)}"; String request = getMultiUserRequest( subscriptionId, setPublicKeys, limit, sinceWhen, kind); sendRequest(relayUrl, request); } @@ -215,7 +216,7 @@ class Relays { print('WebSocketChannelException exception for relay $relayUrl'); return; // is presently not used/called } - on Exception catch(ex) { + on Exception { printWarning("Invalid event\n"); } @@ -261,9 +262,9 @@ void getContactFeed(Set relayUrls, Set setContacts, int numEvent groupContacts.add(contacts[i + j]); } - relayUrls.forEach((relayUrl) { + for (var relayUrl in relayUrls) { relays.getMultiUserEvents(relayUrl, groupContacts, numEventsToGet, sinceWhen); - }); + } } @@ -272,28 +273,28 @@ void getContactFeed(Set relayUrls, Set setContacts, int numEvent } void getUserEvents(Set serverUrls, String publicKey, int numUserEvents, int sinceWhen) { - serverUrls.forEach((serverUrl) { + for (var serverUrl in serverUrls) { relays.getUserEvents(serverUrl, publicKey, numUserEvents, sinceWhen); - }); + } } void getMentionEvents(Set serverUrls, Set ids, int numUserEvents, int sinceWhen, String tagToGet) { - serverUrls.forEach((serverUrl) { + for (var serverUrl in serverUrls) { relays.getMentionEvents(serverUrl, ids, numUserEvents, sinceWhen, tagToGet); - }); + } } void getIdAndMentionEvents(Set serverUrls, Set ids, int numUserEvents, int idSinceWhen, int mentionSinceWhen, String tagToGet, String idType) { - serverUrls.forEach((serverUrl) { + for (var serverUrl in serverUrls) { relays.getIdAndMentionEvents(serverUrl, ids, numUserEvents, idSinceWhen, mentionSinceWhen, tagToGet, idType); - }); + } } getKindEvents(List kind, Set serverUrls, int limit, int sinceWhen) { - serverUrls.forEach((serverUrl) { + for (var serverUrl in serverUrls) { relays.getKindEvents(kind, serverUrl, limit, sinceWhen); - }); + } } void getMultiUserEvents(Set serverUrls, Set setPublicKeys, int numUserEvents, int sinceWhen, [Set? kind]) { @@ -314,23 +315,24 @@ void getMultiUserEvents(Set serverUrls, Set setPublicKeys, int n // send request for specific events whose id's are passed as list eventIds void sendEventsRequest(Set serverUrls, Set eventIds) { - if( eventIds.length == 0) + if( eventIds.isEmpty) { return; + } - String eventIdsStr = getCommaSeparatedQuotedStrs(eventIds);; + String eventIdsStr = getCommaSeparatedQuotedStrs(eventIds); String getEventRequest = '["REQ","event_${eventIds.length}",{"ids":[$eventIdsStr]}]'; if( gDebug > 0) log.info("sending $getEventRequest"); - serverUrls.forEach((url) { + for (var url in serverUrls) { relays.sendRequest(url, getEventRequest); - }); + } } void sendRequest(Set serverUrls, request) { - serverUrls.forEach((url) { + for (var url in serverUrls) { relays.sendRequest(url, request); - }); + } } Set getRecievedEvents() { @@ -343,7 +345,7 @@ void clearEvents() { } void setRelaysIntialEvents(Set eventsFromFile) { - eventsFromFile.forEach((element) {relays.uniqueIdsRecieved.add(element.eventData.id);}); + for (var element in eventsFromFile) {relays.uniqueIdsRecieved.add(element.eventData.id);} relays.rEvents = eventsFromFile; } diff --git a/lib/settings.dart b/lib/settings.dart index ae738b1..289545f 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -241,8 +241,9 @@ Map pubkeyColor = { '0': magentaColor, '1': brightMagentaColor, }; String getNameColor( String pubkey) { - if( pubkey.length == 0) + if( pubkey.isEmpty) { return brightMagentaColor; + } String firstChar = pubkey.substring(0, 1).toLowerCase(); return pubkeyColor[firstChar]??brightMagentaColor; @@ -408,10 +409,11 @@ List lines = intro.split("\n"); var terminalColumns = gDefaultTextWidth; -if( stdout.hasTerminal ) +if( stdout.hasTerminal ) { terminalColumns = stdout.terminalColumns; +} -lines.forEach((line) {print(line.length > terminalColumns ? line.substring(0, terminalColumns) : line );}); +for (var line in lines) {print(line.length > terminalColumns ? line.substring(0, terminalColumns) : line );} } @@ -427,6 +429,6 @@ void printUsage() { print(gUsage); } void printVersion() { - print("$version"); + print(version); } diff --git a/lib/tree_ds.dart b/lib/tree_ds.dart index d9c1b14..97b0c8e 100644 --- a/lib/tree_ds.dart +++ b/lib/tree_ds.dart @@ -1,4 +1,3 @@ -import 'dart:ffi'; import 'dart:io'; import 'dart:convert'; import 'package:nostr_console/event_ds.dart'; @@ -6,7 +5,7 @@ import 'package:nostr_console/relays.dart'; import 'package:nostr_console/utils.dart'; import 'package:nostr_console/settings.dart'; import 'package:nostr_console/user.dart'; -import 'dart:math'; // for Point +// for Point typedef fTreeSelector = bool Function(Tree a); @@ -16,7 +15,7 @@ typedef fRoomSelector = bool Function(ScrollableMessages room); typedef fvisitorMarkNotifications = void Function(Event e); -Store? gStore = null; +Store? gStore; // only show in which user is involved bool selectorTrees_selfPosts(Tree t) { @@ -37,7 +36,7 @@ bool userInvolved(String pubkey, Event e) { } if( gReactions.containsKey(e.eventData.id)) { - List>? reactors = gReactions[e.eventData.id]??null; + List>? reactors = gReactions[e.eventData.id]; if( reactors != null) { for( var reactor in reactors) { String reactorPubkey = reactor[0]; @@ -91,7 +90,7 @@ bool followsInvolved(Event e, Event? contactEvent) { // check if any of the contact liked it if( gReactions.containsKey(e.eventData.id)) { - List>? reactors = gReactions[e.eventData.id]??null; + List>? reactors = gReactions[e.eventData.id]; if( reactors != null) { for( var reactor in reactors) { String reactorPubkey = reactor[0]; @@ -147,20 +146,20 @@ bool showAllRooms (ScrollableMessages room) => selectorShowAllRooms(room); int getLatestMessageTime(ScrollableMessages channel) { - List _messageIds = channel.messageIds; + List messageIds = channel.messageIds; if(gStore == null) { return 0; } - if(_messageIds.length == 0) { + if(messageIds.isEmpty) { int createdAt = channel.createdAt; return createdAt; } int latest = 0; - for(int i = 0; i < _messageIds.length; i++) { + for(int i = 0; i < messageIds.length; i++) { if( gStore != null) { - Tree? tree = (gStore?.allChildEventsMap[_messageIds[i]] ); + Tree? tree = (gStore?.allChildEventsMap[messageIds[i]] ); if( tree != null) { EventData ed = tree.event.eventData; if( ed.createdAt > latest) { @@ -193,8 +192,9 @@ DirectMessageRoom? getDirectRoom(List rooms, String otherPubk int scrollableCompareTo(ScrollableMessages a, ScrollableMessages b) { - if( gStore == null) + if( gStore == null) { return 0; + } int otherLatest = getLatestMessageTime(b); int thisLatest = getLatestMessageTime(a); @@ -228,12 +228,12 @@ class ScrollableMessages { int eventTime = (tempChildEventsMap[messageIds[i]]?.event.eventData.createdAt??0); if( newEventTime < eventTime) { // shift current i and rest one to the right, and put event Time here - if(gSpecificDebug > 0 && roomType == enumRoomType.kind140) print("In addMessageToRoom: inserted enc message in middle to room with name ${topHeader}"); + if(gSpecificDebug > 0 && roomType == enumRoomType.kind140) print("In addMessageToRoom: inserted enc message in middle to room with name $topHeader"); messageIds.insert(i, messageId); return; } } - if(gSpecificDebug > 0 && roomType == enumRoomType.kind140) print("In addMessageToRoom: inserted enc message in end of room with name ${topHeader}"); + if(gSpecificDebug > 0 && roomType == enumRoomType.kind140) print("In addMessageToRoom: inserted enc message in end of room with name $topHeader"); // insert at end messageIds.add(messageId); @@ -273,15 +273,16 @@ class ScrollableMessages { if( messageIds.length > gNumChannelMessagesToShow) { print("\n"); printDepth(0); - stdout.write("${gNotificationColor}Displayed page number ${page} (out of total $numPages pages, where 1st is the latest 'page').\n"); + stdout.write("${gNotificationColor}Displayed page number $page (out of total $numPages pages, where 1st is the latest 'page').\n"); printDepth(0); - stdout.write("To see older pages, enter numbers from 1-${numPages}, in format '/N', a slash followed by the required page number.${gColorEndMarker}\n\n"); + stdout.write("To see older pages, enter numbers from 1-$numPages, in format '/N', a slash followed by the required page number.$gColorEndMarker\n\n"); } } bool selectorNotifications() { - if( gStore == null) + if( gStore == null) { return false; + } for(int i = 0; i < messageIds.length; i++) { Event? e = gStore?.allChildEventsMap[messageIds[i]]?.event; @@ -317,6 +318,7 @@ class Channel extends ScrollableMessages { Set participants; // pubkey of all participants - only for encrypted channels String creatorPubkey; // creator of the channel, if event is known + @override enumRoomType roomType; Channel(this.channelId, this.internalChatRoomName, this.about, this.picture, List messageIds, this.participants, this.lastUpdated, this.roomType, [this.creatorPubkey=""] ) : @@ -333,7 +335,7 @@ class Channel extends ScrollableMessages { return internalChatRoomName; } - void set chatRoomName(String newName){ + set chatRoomName(String newName){ internalChatRoomName = newName; super.topHeader = "Channel Name: $newName (Id: $channelId)"; } @@ -366,11 +368,11 @@ class Channel extends ScrollableMessages { // represents direct chat of kind 4 class DirectMessageRoom extends ScrollableMessages{ String otherPubkey; // id of user this DM is happening + @override int createdAt; DirectMessageRoom(this.otherPubkey, List messageIds, this.createdAt): - super ( "${(otherPubkey)} ($otherPubkey)", messageIds, createdAt, enumRoomType.kind4) { - } + super ( "${(otherPubkey)} ($otherPubkey)", messageIds, createdAt, enumRoomType.kind4); String getChannelId() { return otherPubkey; @@ -404,7 +406,7 @@ class Tree { store = s; } - /***********************************************************************************************************************************/ + /// ******************************************************************************************************************************** /* The main print tree function. Calls the reeSelector() for every node and prints it( and its children), only if it returns true. * returns Point , where first int is total Threads ( or top trees) printed, and second is notifications printed * returns list< total top threads printed, total events printed, total notifications printed> @@ -412,7 +414,7 @@ class Tree { List printTree(int depth, DateTime newerThan, bool topPost, [int countPrinted = 0, int maxToPrint = gMaxEventsInThreadPrinted]) { List ret = [0,0,0]; - if(event.eventData.isNotification || event.eventData.newLikes.length > 0) { + if(event.eventData.isNotification || event.eventData.newLikes.isNotEmpty) { ret[2] = 1; } @@ -503,7 +505,7 @@ class Tree { if( pubkeys.contains(event.eventData.pubkey) && gReactions.containsKey(event.eventData.id)) { List>? reactions = gReactions[event.eventData.id]; if( reactions != null) { - if( reactions.length > 0) { + if( reactions.isNotEmpty) { // set every reaction as a new like so they all get highlighted; these are all later reset after first printing Set reactorPubkeys = getReactorPubkeys(event.eventData.id); event.eventData.newLikes = reactorPubkeys; @@ -517,18 +519,20 @@ class Tree { Set pplTagged = pTags.toSet().intersection(pubkeys); // 2nd condition: person making the event should not be on this list; they would already be considered in other test - if( pplTagged.length > 0 && !pubkeys.contains(event.eventData.pubkey)) { + if( pplTagged.isNotEmpty && !pubkeys.contains(event.eventData.pubkey)) { event.eventData.isNotification = isMentioned = true; } // check if there are any replies from other people to an event made by someone in list - if( pubkeys.contains(event.eventData.pubkey) && children.length > 0) { + if( pubkeys.contains(event.eventData.pubkey) && children.isNotEmpty) { for( int i = 0; i < children.length; i++ ) { - children.forEach((child) { + for (var child in children) { // if child is someone else then set notifications and flag, means there are replies to this event - if(child.event.eventData.pubkey != event.eventData.pubkey ) // tests reply is not from same user + if(child.event.eventData.pubkey != event.eventData.pubkey ) { + // tests reply is not from same user childMatches = child.event.eventData.isNotification = true; - }); + } + } } } @@ -597,8 +601,9 @@ class Tree { if( reactions != null) { for( int i = 0; i < reactions.length; i++) { if( pubkeys.contains(reactions[i][0]) ) { - if( enableNotifications) + if( enableNotifications) { event.eventData.newLikes.add(reactions[i][0]); + } hasReacted = true; } } @@ -617,8 +622,9 @@ class Tree { // if event is by user(s) if( pubkeys.contains(event.eventData.pubkey)) { - if( enableNotifications) + if( enableNotifications) { event.eventData.isNotification = true; + } return true; } if( hasReacted || childMatches) { @@ -636,8 +642,9 @@ class Tree { // if event is by user(s) if( pubkeys.contains(event.eventData.pubkey)) { - if( enableNotifications) + if( enableNotifications) { event.eventData.isNotification = true; + } return true; } @@ -721,7 +728,7 @@ class Tree { bool treeSelector_hasNotifications() { bool hasNotifications = false; - if( event.eventData.isNotification || event.eventData.newLikes.length > 0) { + if( event.eventData.isNotification || event.eventData.newLikes.isNotEmpty) { hasNotifications = true; } @@ -749,7 +756,7 @@ class Tree { count = 1; } - if( event.eventData.newLikes.length > 0) { + if( event.eventData.newLikes.isNotEmpty) { event.eventData.newLikes = {}; count = 1; } @@ -777,7 +784,7 @@ class Tree { } // end count() } // end Tree -/***********************************************************************************************************************************/ +/// ******************************************************************************************************************************** /* * The actual tree struture holds only kind 1 events, or only posts. Tree itself can hold any event type( to be fixed, needs renaming etc TODO) * This Store class holds events too in its map, and in its chatRooms structure @@ -902,7 +909,7 @@ class Store { static void addTTagEventInChannel(EventData eventData, List rooms, Map tempChildEventsMap) { List? tTags = eventData.getTTags(); - if( tTags != null && tTags.length > 0) { + if( tTags != null && tTags.isNotEmpty) { for( int i = 0; i < tTags.length; i++) { String chatRoomId = eventData.getChannelIdForTTagRoom(tTags[i]); Channel? channel = getChannel(rooms, chatRoomId); @@ -949,11 +956,9 @@ class Store { return null; } - /** - * Will create a entry in encryptedChannels ( if one does not already exist) - * Returns id of channel if one is created, null otherwise. - * - */ + /// Will create a entry in encryptedChannels ( if one does not already exist) + /// Returns id of channel if one is created, null otherwise. + /// static String? createEncryptedRoomFromInvite( List encryptedChannels, Map tempChildEventsMap, Event eventSecretMessage) { String? temp140Id = getEncryptedChannelIdFromSecretMessage( eventSecretMessage); @@ -969,7 +974,7 @@ class Store { if( event140 != null) { Set participants = {}; - event140.eventData.pTags.forEach((element) { participants.add(element);}); + for (var element in event140.eventData.pTags) { participants.add(element);} String chatRoomId = event140Id; try { @@ -1018,7 +1023,7 @@ class Store { // update the participant list if the event already exists ( the room was likely creted with 104 invite, which did not have participant list) Set participants = {}; - event14x.eventData.pTags.forEach((element) { participants.add(element);}); + for (var element in event14x.eventData.pTags) { participants.add(element);} Channel? channel = getChannel(encryptedChannels, event14x.eventData.id); dynamic json = jsonDecode(event14x.eventData.content); @@ -1042,7 +1047,7 @@ class Store { case 141: Set participants = {}; - event14x.eventData.pTags.forEach((element) { participants.add(element);}); + for (var element in event14x.eventData.pTags) { participants.add(element);} String chatRoomId = event14x.eventData.getChannelIdForKind4x(); if( chatRoomId.length != 64) { @@ -1093,7 +1098,7 @@ class Store { } } else { // could not get channel id of message. - printWarning("---Could not get encryptd channel for message id ${event14x.eventData.id} got channelId : ${channelId} its len ${channelId.length}"); + printWarning("---Could not get encryptd channel for message id ${event14x.eventData.id} got channelId : $channelId its len ${channelId.length}"); } break; @@ -1158,7 +1163,7 @@ class Store { directRooms.add( newDirectRoom); if( ce.eventData.id == gCheckEventId && gDebug >= 0) print("Adding new message ${ce.eventData.id} to NEW direct room $directRoomId. sender pubkey = ${ce.eventData.pubkey}."); } - if( ce.eventData.evaluatedContent.length > 0) numMessagesDecrypted++; + if( ce.eventData.evaluatedContent.isNotEmpty) numMessagesDecrypted++; } else { if( gDebug > 0) print("Could not get chat room id for event ${ce.eventData.id} sender pubkey = ${ce.eventData.pubkey}."); } @@ -1252,7 +1257,7 @@ class Store { } } - /***********************************************************************************************************************************/ + /// ******************************************************************************************************************************** // @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 Store.fromEvents(Set events) { @@ -1264,12 +1269,12 @@ class Store { // create a map tempChildEventsMap from list of events, key is eventId and value is event itself Map tempChildEventsMap = {}; - events.forEach((event) { + for (var event in events) { // only add in map those kinds that are supported or supposed to be added ( 0 1 3 7 40) if( typesInEventMap.contains(event.eventData.kind)) { tempChildEventsMap[event.eventData.id] = Tree.withoutStore( event, []); } - }); + } processDeleteEvents(tempChildEventsMap); // handle returned values perhaps later processReactions(events, tempChildEventsMap); @@ -1322,7 +1327,7 @@ class Store { } // if reacted to event is not in store, then add it to dummy list so it can be fetched - if( tree.event.eventData.eTags.length > 0 && tree.event.eventData.eTags.last.length > 0) { + if( tree.event.eventData.eTags.isNotEmpty && tree.event.eventData.eTags.last.isNotEmpty) { String reactedToId = tree.event.eventData.eTags.last[0]; if( !tempChildEventsMap.containsKey(reactedToId) && tree.event.eventData.createdAt > getSecondsDaysAgo(3)) { //print("liked event not found in store."); @@ -1353,7 +1358,7 @@ class Store { // allEncryptedGroupInviteIds has been created above // now create encrypted rooms from that list which are just for the current user Set usersEncryptedChannelIds = {}; - allEncryptedGroupInviteIds.forEach((secretEventId) { + for (var secretEventId in allEncryptedGroupInviteIds) { Event? secretEvent = tempChildEventsMap[secretEventId]?.event; if( secretEvent != null) { @@ -1363,7 +1368,7 @@ class Store { usersEncryptedChannelIds.add(newEncryptedChannelId); // is later used so a request can be sent to fetch events related to this room } } - }); + } tempChildEventsMap.forEach((newEventId, tree) { int eKind = tree.event.eventData.kind; @@ -1391,7 +1396,7 @@ class Store { return Store( topLevelTrees, tempChildEventsMap, tempWithoutParent, channels, encryptedChannels, tempDirectRooms, allEncryptedGroupInviteIds); } // end fromEvents() - /***********************************************************************************************************************************/ + /// ******************************************************************************************************************************** /* @processIncomingEvent inserts the relevant events into the tree and otherwise processes likes, delete events etc. * returns the id of the ones actually new so that they can be printed as notifications. */ @@ -1403,26 +1408,26 @@ class Store { Set dummyEventIds = {}; // add the event to the main event store thats allChildEventsMap - newEventsToProcess.forEach((newEvent) { + for (var newEvent in newEventsToProcess) { if( newEvent.eventData.kind == 1 && newEvent.eventData.content.compareTo("Hello Nostr! :)") == 0 && newEvent.eventData.id.substring(0,3).compareTo("000") == 0) { - return; // spam prevention + continue; // spam prevention } if( allChildEventsMap.containsKey(newEvent.eventData.id)) {// don't process if the event is already present in the map - return; + continue; } //ignore bots if( [4, 42, 142].contains( newEvent.eventData.kind ) && gBots.contains(newEvent.eventData.pubkey)) { - return; + continue; } // handle reaction events and return if we could not find the reacted to. Continue otherwise to add this to notification set newEventIdsSet if( newEvent.eventData.kind == 7) { if( processReaction(newEvent, allChildEventsMap) == "") { if(gDebug > 0) print("In insertEvents: For new reaction ${newEvent.eventData.id} could not find reactedTo or reaction was already present by this reactor"); - return; + continue; } } @@ -1430,12 +1435,12 @@ class Store { if( newEvent.eventData.kind == 5) { processDeleteEvent(allChildEventsMap, newEvent); if(gDebug > 0) print("In insertEvents: For new deleteion event ${newEvent.eventData.id} could not process it."); - return; + continue; } if( newEvent.eventData.kind == 4) { if( !isValidDirectMessage(newEvent.eventData)) { // direct message not relevant to user are ignored; also otherwise validates the message that it has one p tag - return; + continue; } } @@ -1445,12 +1450,13 @@ class Store { // only kind 0, 1, 3, 4, 5( delete), 7, 40, 42, 140, 142 events are added to map-store, return otherwise if( !typesInEventMap.contains(newEvent.eventData.kind) ) { - return; + continue; } // expand mentions ( and translate if flag is set) and then add event to main event map; 142 events are expanded later - if( newEvent.eventData.kind != 142) + if( newEvent.eventData.kind != 142) { newEvent.eventData.translateAndExpandMentions( allChildEventsMap); // this also handles dm decryption for kind 4 messages, for kind 1 will do translation/expansion; + } // add them to the main store of the Tree object, but after checking that its not one of the dummy/missing events. // In that case, replace the older dummy event, and only then add it to store-map @@ -1462,7 +1468,7 @@ class Store { if( gDebug >= 0 && newEvent.eventData.id == gCheckEventId) log.info("In processIncoming: Replaced old dummy event of id: ${newEvent.eventData.id}"); tree.event = newEvent; allChildEventsMap[tree.event.eventData.id] = tree; - return; + continue; } } @@ -1471,10 +1477,10 @@ class Store { // add to new-notification list only if this is a recent event ( because relays may send old events, and we dont want to highlight stale messages) newEventIdsSet.add(newEvent.eventData.id); - }); + } // now go over the newly inserted event, and add it to the tree for kind 1 events, add 42 events to channels. rest ( such as kind 0, kind 3, kind 7) are ignored. - newEventIdsSet.forEach((newId) { + for (var newId in newEventIdsSet) { Tree? newTree = allChildEventsMap[newId]; if( newTree != null) { // this should return true because we just inserted this event in the allEvents in block above @@ -1497,21 +1503,22 @@ class Store { topPosts.add(dummyTopNode); // add it to list to fetch it from relays - if( parentId.length == 64) - dummyEventIds.add(parentId); + if( parentId.length == 64) { + dummyEventIds.add(parentId); + } } } // now process case where there is a tag which should put this kind 1 message in a channel String? location = newTree.event.eventData.getSpecificTag("location"); if( location != null && location != "") { - addLocationTagEventInChannel(newTree.event.eventData, this.channels, allChildEventsMap); + addLocationTagEventInChannel(newTree.event.eventData, channels, allChildEventsMap); } // now process case where there is a tag which should put this kind 1 message in a channel List? tTags = newTree.event.eventData.getTTags(); if( tTags != null && tTags != "") { - addTTagEventInChannel(newTree.event.eventData, this.channels, allChildEventsMap); + addTTagEventInChannel(newTree.event.eventData, channels, allChildEventsMap); } break; @@ -1562,19 +1569,19 @@ class Store { break; } } - }); + } // get dummy events sendEventsRequest(gListRelayUrls, dummyEventIds); int totalTreeSize = 0; - topPosts.forEach((element) {totalTreeSize += element.count();}); + for (var element in topPosts) {totalTreeSize += element.count();} if(gDebug > 0) print("In end of insertEvents: allChildEventsMap size = ${allChildEventsMap.length}; mainTree count = $totalTreeSize"); if(gDebug > 0) print("Returning ${newEventIdsSet.length} new notification-type events, which are ${newEventIdsSet.length < 10 ? newEventIdsSet: " "} "); return newEventIdsSet; } // end insertEvents() - /***********************************************************************************************************************************/ + /// ******************************************************************************************************************************** /* * @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 @@ -1600,13 +1607,13 @@ class Store { } List topNotificationTree = []; // collect all top tress to display in this list. only unique tress will be displayed - newEventIdsSet.forEach((eventID) { + for (var eventID in newEventIdsSet) { Tree ?t = allChildEventsMap[eventID]; if( t == null) { // ignore if not in Tree. Should ideally not happen. TODO write warning otherwise if( gDebug > 0) print("In printNotifications: Could not find event $eventID in tree"); - return; + continue; } else { if( isRelevantForNotification(t)) { switch(t.event.eventData.kind) { @@ -1643,7 +1650,7 @@ class Store { } } } - }); + } // remove duplicate top trees Set ids = {}; @@ -1655,7 +1662,7 @@ class Store { gFollowList = getFollows(userPublicKey); List ret = [0,0,0]; - topNotificationTree.forEach( (t) { + for (var t in topNotificationTree) { bool selectorTrees_followActionsWithNotifications (Tree t) => t.treeSelectorUserPostAndLike(getFollows( userPublicKey), enableNotifications: true); if( selectorTrees_followActionsWithNotifications(t)) { List temp = Store.printTopPost(t, 0, DateTime(0)); @@ -1664,7 +1671,7 @@ class Store { ret[2] += temp[2]; //print("\n"); } - }); + } return ret; } @@ -1691,7 +1698,7 @@ class Store { return count; } - /***********************************************************************************************************************************/ + /// ******************************************************************************************************************************** /* The main print tree function. Calls the treeSelector() for every node and prints it( and its children), only if it returns true. */ List printStoreTrees(int depth, DateTime newerThan, fTreeSelector treeSelector, [int maxToPrint = gMaxEventsInThreadPrinted]) { @@ -1787,11 +1794,9 @@ class Store { return 0; } - /** - * @printAllChennelsInfo Print one line information about all channels, which are type 40 events ( class ChatRoom) and for 14x channels both; channelsToPrint is different for both - * - * @param numRoomsOverview This many number of rooms should be printed. - */ + /// @printAllChennelsInfo Print one line information about all channels, which are type 40 events ( class ChatRoom) and for 14x channels both; channelsToPrint is different for both + /// + /// @param numRoomsOverview This many number of rooms should be printed. int printChannelsOverview(List channelsToPrint, int numRoomsOverview, fRoomSelector selector, var tempChildEventsMap , Set? secretMessageIds) { channelsToPrint.sort(scrollableCompareTo); @@ -1818,7 +1823,7 @@ class Store { //stdout.write("channel type: " + channelsToPrint[j].roomType.toString()); - if( channelsToPrint[j].participants.length > 0 && !channelsToPrint[j].participants.contains(userPublicKey)) { + if( channelsToPrint[j].participants.isNotEmpty && !channelsToPrint[j].participants.contains(userPublicKey)) { continue; } @@ -1838,11 +1843,11 @@ class Store { } if( channelsToPrint[j].chatRoomName != "") { - name = "${channelsToPrint[j].chatRoomName}"; + name = channelsToPrint[j].chatRoomName; } int numMessages = channelsToPrint[j].getNumValidMessages(); - stdout.write("${name} ${getNumSpaces(32-name.length)} $id $numMessages${getNumSpaces(20- numMessages.toString().length)}"); + stdout.write("$name ${getNumSpaces(32-name.length)} $id $numMessages${getNumSpaces(20- numMessages.toString().length)}"); numChannelsActuallyPrinted++; List messageIds = channelsToPrint[j].messageIds; for( int i = messageIds.length - 1; i >= 0; i--) { @@ -1850,7 +1855,7 @@ class Store { Event? e = allChildEventsMap[messageIds[i]]?.event; if( e!= null) { if( !(e.eventData.kind == 142 && e.eventData.content == e.eventData.evaluatedContent)) { - stdout.write("${e.eventData.getAsLine(tempChildEventsMap, secretMessageIds, channelsToPrint)}"); + stdout.write(e.eventData.getAsLine(tempChildEventsMap, secretMessageIds, channelsToPrint)); break; // print only one event, the latest one } } @@ -1886,15 +1891,15 @@ class Store { stdout.write("\nChannel participants : "); int i = 0; - room.participants.forEach((participant) { + for (var participant in room.participants) { if( i != 0) { stdout.write(', '); } String pName = getAuthorName(participant); - printInColor("$pName", gCommentColor); + printInColor(pName, gCommentColor); i++; - }); + } } @@ -1968,7 +1973,7 @@ class Store { } return fullChannelId.first; } else { - if( fullChannelId.length == 0) { + if( fullChannelId.isEmpty) { printWarning("Could not find the channel."); } else { @@ -1982,16 +1987,15 @@ class Store { return directRooms.length; } - /** - * @printDirectRoomInfo Print one line information about chat rooms - */ + /// @printDirectRoomInfo Print one line information about chat rooms int printDirectRoomsOverview(fRoomSelector roomSelector, int numRoomsOverview, var tempChildEventsMap) { directRooms.sort(scrollableCompareTo); int numNotificationRooms = 0; for( int j = 0; j < directRooms.length; j++) { - if( roomSelector(directRooms[j])) + if( roomSelector(directRooms[j])) { numNotificationRooms++; + } } // even if num rooms is zero, we will show the heading when its show all rooms @@ -2026,8 +2030,9 @@ class Store { int iNotification = 0; // notification counter for( int j = startRoomIndex; j < endRoomIndex; j++) { - if( !roomSelector(directRooms[j])) + if( !roomSelector(directRooms[j])) { continue; + } // print only that we have been asked for if( iNotification++ > numNotificationRooms) { @@ -2042,7 +2047,7 @@ class Store { room.visitAllMessages(this, markAllRead); int numMessages = room.messageIds.length; - stdout.write("${name} ${getNumSpaces(32-name.length)} $id $numMessages${getNumSpaces(18- numMessages.toString().length)}"); + stdout.write("$name ${getNumSpaces(32-name.length)} $id $numMessages${getNumSpaces(18- numMessages.toString().length)}"); // print latest event in one line List messageIds = room.messageIds; @@ -2106,10 +2111,10 @@ class Store { } } } else { - if( lookedUpName.length > 0) { + if( lookedUpName.isNotEmpty) { printWarning("Got more than one public id for the name given, which are: "); for(String pubkey in lookedUpName) { - print("${getAuthorName(pubkey)} - ${pubkey}, "); + print("${getAuthorName(pubkey)} - $pubkey, "); } } else { // in case the given id is not present in our global list of usernames, create new room for them @@ -2169,7 +2174,7 @@ class Store { Future writeEventsToFile(String filename) async { // this variable will be used later; update it if needed - if( gFollowList.length == 0) { + if( gFollowList.isEmpty) { gFollowList = getFollows(userPublicKey); } @@ -2207,7 +2212,7 @@ class Store { } String temp = tree.event.originalJson.trim(); - String line = "${temp}\n"; + String line = "$temp\n"; nLinesStr += line; eventCounter++; if( tree.event.eventData.kind == 1) { @@ -2227,8 +2232,8 @@ class Store { nLinesStr = ""; } - if(gDebug > 0) log.info("finished writing eventCounter = ${eventCounter}."); - print("Appended $eventCounter new events to file \"$gEventsFilename\" of which ${countPosts} are posts."); + if(gDebug > 0) log.info("finished writing eventCounter = $eventCounter."); + print("Appended $eventCounter new events to file \"$gEventsFilename\" of which $countPosts are posts."); } on Exception catch (e) { print("Could not open file $filename."); if( gDebug > 0) print("Could not open file: $e"); @@ -2241,7 +2246,7 @@ class Store { * Also adds 'client' tag with application name. * @parameter replyToId First few letters of an event id for which reply is being made */ - String getTagStr(String replyToId, String clientName, [bool addAllP = false, Set? extraTags = null]) { + String getTagStr(String replyToId, String clientName, [bool addAllP = false, Set? extraTags]) { clientName = (clientName == "")? "nostr_console": clientName; // in case its empty //print("extraTags = $extraTags"); @@ -2249,26 +2254,29 @@ class Store { if( extraTags != null) for( String extraTag in extraTags) { - if( otherTags.length > 0) + if( otherTags.isNotEmpty) { otherTags += ","; + } otherTags += '["t","$extraTag"]'; } if( gWhetherToSendClientTag) { - if( otherTags.length > 0) + if( otherTags.isNotEmpty) { otherTags += ","; + } otherTags += '["client","$clientName"]'; } if( gUserLocation != "") { - if( otherTags.length > 0) + if( otherTags.isNotEmpty) { otherTags += ","; + } otherTags += '["location","$gUserLocation"]'; } //print("otherTags = $otherTags"); if( replyToId.isEmpty) { - return otherTags.length >0 ? otherTags: '[]'; + return otherTags.isNotEmpty ? otherTags: '[]'; } String strTags = otherTags ; @@ -2293,7 +2301,7 @@ class Store { if( latestEventId.isEmpty && replyToId.length == 64) { latestEventId = replyToId; } - if( latestEventId.isEmpty && replyToId.length != 64 && replyToId.length != 0) { + if( latestEventId.isEmpty && replyToId.length != 64 && replyToId.isNotEmpty) { return ""; } @@ -2301,8 +2309,9 @@ class Store { if( latestEventId.isNotEmpty) { String? pTagPubkey = allChildEventsMap[latestEventId]?.event.eventData.pubkey; if( pTagPubkey != null) { - if( strTags.length > 0) + if( strTags.isNotEmpty) { strTags += ","; + } strTags += '["p","$pTagPubkey"]'; } String relay = getRelayOfUser(userPublicKey, pTagPubkey??""); @@ -2315,14 +2324,16 @@ class Store { Tree topTree = getTopTree(t); rootEventId = topTree.event.eventData.id; if( rootEventId != latestEventId) { // if the reply is to a top/parent event, then only one e tag is sufficient - if( strTags.length > 0) + if( strTags.isNotEmpty) { strTags += ","; + } strTags += '["e","$rootEventId","","root"]'; } } - if( strTags.length > 0) + if( strTags.isNotEmpty) { strTags += ","; + } strTags += '["e","$latestEventId","$relay","reply"]'; } @@ -2405,7 +2416,7 @@ class Store { latestEventId = replyToId; } - if( latestEventId.isEmpty && replyToId.length != 64 && replyToId.length != 0) { + if( latestEventId.isEmpty && replyToId.length != 64 && replyToId.isNotEmpty) { printWarning('Could not find the given id: $replyToId. Sending a regular message.'); } @@ -2422,7 +2433,7 @@ class Store { // add root for kind 1 in rooms if( [enumRoomType.RoomLocationTag, enumRoomType.RoomTTag].contains( channel.roomType) ) { - Tree? replyTree = allChildEventsMap[latestEventId]??null; + Tree? replyTree = allChildEventsMap[latestEventId]; if( replyTree != null) { Tree rootTree = getTopTree(replyTree); String rootEventId = rootTree.event.eventData.id; @@ -2517,7 +2528,7 @@ class Store { } else { stdout.write("* Of the $selfNumContacts people you follow, $numSecond follow you back. Their names are: "); mutualFollows.sort(); - mutualFollows.forEach((name) { stdout.write("$name, ");}); + for (var name in mutualFollows) { stdout.write("$name, ");} } print(""); } else { // end if contact event was found @@ -2536,9 +2547,9 @@ class Store { static List processDeleteEvent(Map tempChildEventsMap, Event deleterEvent) { List deletedEventIds = []; if( deleterEvent.eventData.kind == 5) { - deleterEvent.eventData.tags.forEach((tag) { + for (var tag in deleterEvent.eventData.tags) { if( tag.length < 2) { - return; + continue; } if( tag[0] == "e") { String deletedEventId = tag[1]; @@ -2555,7 +2566,7 @@ class Store { } } } - }); + } } // end if return deletedEventIds; } // end processDeleteEvent @@ -2566,7 +2577,7 @@ class Store { Event deleterEvent = tree.event; if( deleterEvent.eventData.kind == 5) { List tempIds = processDeleteEvent(tempChildEventsMap, deleterEvent); - tempIds.forEach((tempId) { deletedEventIds.add(tempId); }); + for (var tempId in tempIds) { deletedEventIds.add(tempId); } } }); return deletedEventIds; @@ -2590,8 +2601,9 @@ class Store { // the reactedTo event's id, blank if invalid reaction etc static String processReaction(Event event, Map tempChildEventsMap) { - if( gDebug > 0 && event.eventData.id == gCheckEventId) - print("in processReaction: 0 got reaction $gCheckEventId"); + if( gDebug > 0 && event.eventData.id == gCheckEventId) { + print("in processReaction: 0 got reaction $gCheckEventId"); + } List validReactionList = ["+", "!"]; // TODO support opposite reactions List opppositeReactions = ['-', "~"]; @@ -2785,20 +2797,24 @@ Store getTree(Set events) { events.retainWhere((event) => ids.add(event.eventData.id)); // process kind 0 events about metadata - events.forEach( (event) => processKind0Event(event)); + for (var event in events) { + processKind0Event(event); + } // process kind 3 events which is contact list. Update global info about the user (with meta data) - events.forEach( (event) => processKind3Event(event)); + for (var event in events) { + processKind3Event(event); + } // create tree from events Store node = Store.fromEvents(events); // translate and expand mentions - events.where((element) => [1, 42].contains(element.eventData.kind)).forEach( (event) => event.eventData.translateAndExpandMentions( node.allChildEventsMap));; + events.where((element) => [1, 42].contains(element.eventData.kind)).forEach( (event) => event.eventData.translateAndExpandMentions( node.allChildEventsMap)); // has been done in fromEvents //events.where((element) => [gSecretMessageKind].contains(element.eventData.kind)).forEach( (event) => event.eventData.TranslateAndDecryptGroupInvite( ));; - events.where((element) => element.eventData.kind == 142).forEach( (event) => event.eventData.translateAndDecrypt14x(node.encryptedGroupInviteIds, node.encryptedChannels, node.allChildEventsMap));; + events.where((element) => element.eventData.kind == 142).forEach( (event) => event.eventData.translateAndDecrypt14x(node.encryptedGroupInviteIds, node.encryptedChannels, node.allChildEventsMap)); return node; } @@ -2807,18 +2823,19 @@ Store getTree(Set events) { String getDirectRoomId(EventData eventData) { List participantIds = []; - eventData.tags.forEach((tag) { - if( tag.length < 2) - return; + for (var tag in eventData.tags) { + if( tag.length < 2) { + continue; + } if( tag[0] == 'p') { participantIds.add(tag[1]); } - }); + } participantIds.sort(); String uniqueId = ""; - participantIds.forEach((element) {uniqueId += element;}); // TODO ensure its only one thats added s + for (var element in participantIds) {uniqueId += element;} // TODO ensure its only one thats added s // send the other persons pubkey as identifier if( eventData.pubkey == userPublicKey) { diff --git a/lib/user.dart b/lib/user.dart index 3237d51..f89a167 100644 --- a/lib/user.dart +++ b/lib/user.dart @@ -10,7 +10,7 @@ Event? getContactEvent(String pubkey) { // get the latest kind 3 event for the user, which lists his 'follows' list if( gKindONames.containsKey(pubkey)) { - Event? e = (gKindONames[pubkey]?.latestContactEvent)??null; + Event? e = (gKindONames[pubkey]?.latestContactEvent); return e; } @@ -23,7 +23,9 @@ Set getFollows(String pubkey) { Event? profileContactEvent = getContactEvent(pubkey); if( profileContactEvent != null) { - profileContactEvent.eventData.contactList.forEach((x) => followPubkeys.add(x.contactPubkey)); + for (var x in profileContactEvent.eventData.contactList) { + followPubkeys.add(x.contactPubkey); + } //followPubkeys = profileContactEvent.eventData.contactList.toSet(); } @@ -33,7 +35,7 @@ Set getFollows(String pubkey) { Set getUserChannels(Set userEvents, String userPublicKey) { Set userChannels = {}; - userEvents.forEach((event) { + for (var event in userEvents) { if( event.eventData.pubkey == userPublicKey) { if( [42, 142].contains( event.eventData.kind) ) { String channelId = event.eventData.getChannelIdForKind4x(); @@ -44,7 +46,7 @@ Set getUserChannels(Set userEvents, String userPublicKey) { userChannels.add(event.eventData.id); } } - }); + } return userChannels; } @@ -88,11 +90,11 @@ Set getpTags(Set events, int numMostFrequent) { Set getOnlyUserEvents(Set initialEvents, String userPubkey) { Set userEvents = {}; - initialEvents.forEach((event) { + for (var event in initialEvents) { if( event.eventData.pubkey == userPubkey) { userEvents.add(event.eventData.id); } - }); + } return userEvents; } diff --git a/lib/utils.dart b/lib/utils.dart index a97c433..f0481ea 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -22,14 +22,13 @@ String getPostKindFrom(enumRoomType eType) { } Set? getTagsFromContent(String content) { - Set? tags = null; + Set? tags; - String regexp1 = '(#[a-zA-Z0-9_\-]+ )|(#[a-zA-Z0-9_\-]+)\$'; + String regexp1 = '(#[a-zA-Z0-9_-]+ )|(#[a-zA-Z0-9_-]+)\$'; RegExp httpRegExp = RegExp(regexp1); for( var match in httpRegExp.allMatches(content) ) { - if( tags == null) - tags = {}; + tags ??= {}; tags.add( content.substring(match.start + 1, match.end).trim() ); } @@ -84,31 +83,35 @@ extension StringX on String { int isChannelPageNumber(int max) { - if(this.length < 2 || this[0] != '/') { + if(length < 2 || this[0] != '/') { return 0; } - String rest = this.substring(1); + String rest = substring(1); //print("rest = $rest"); int? n = int.tryParse(rest); if( n != null) { - if( n < max) + if( n < max) { return n; + } } return 0; } isEnglish( ) { // since smaller words can be smileys they should not be translated - if( length < 10) + if( length < 10) { return true; + } - if( !isLatinAlphabet()) + if( !isLatinAlphabet()) { return false; + } - if (isRomanceLanguage()) + if (isRomanceLanguage()) { return false; + } return true; } @@ -127,7 +130,7 @@ extension StringX on String { Set romanceWords = frenchWords.union(spanishWords).union(portugeseWords); for( String word in romanceWords) { - if( this.toLowerCase().contains(" $word ")) { + if( toLowerCase().contains(" $word ")) { return true; } } @@ -208,7 +211,7 @@ String unEscapeChars(String str) { return temp; } -void printUnderlined(String x) => { stdout.write("$x\n${getNumDashes(x.length)}\n")}; +void printUnderlined(String x) { stdout.write("$x\n${getNumDashes(x.length)}\n");} String getNumSpaces(int num) { String s = ""; @@ -228,7 +231,7 @@ String getNumDashes(int num, [String dashType = "-"]) { List> getUrlRanges(String s) { List> urlRanges = []; - String regexp1 = "http[s]*:\/\/[a-zA-Z0-9]+([.a-zA-Z0-9/_\\-\\#\\+=\\&\\?]*)"; + String regexp1 = "http[s]*://[a-zA-Z0-9]+([.a-zA-Z0-9/_\\-\\#\\+=\\&\\?]*)"; RegExp httpRegExp = RegExp(regexp1); for( var match in httpRegExp.allMatches(s) ) { @@ -273,11 +276,13 @@ List? getTypeAndModule(String str) { bool sanityChecked(String lnInvoice) { - if( lnInvoice.length < gMinLnInvoiceLength) + if( lnInvoice.length < gMinLnInvoiceLength) { return false; + } - if( lnInvoice.substring(0, 4).toLowerCase() != "lnbc") + if( lnInvoice.substring(0, 4).toLowerCase() != "lnbc") { return false; + } return true; } @@ -306,7 +311,7 @@ String expandLNInvoices(String content) { } qrStr = getPubkeyAsQrString(lnInvoice, typeAndModule[0], typeAndModule[1], ""); - content = content.substring(0, match.start) + ":-\n\n" + qrStr + "\n\n" + content.substring(match.end); + content = "${content.substring(0, match.start)}:-\n\n$qrStr\n\n${content.substring(match.end)}"; } return content; @@ -319,7 +324,7 @@ String getPubkeyAsQrString(String str, [int typeNumber = 4, moduleCount = 33, St String output = ""; final qrCode = QrCode(typeNumber, QrErrorCorrectLevel.L) - ..addData('$str'); + ..addData(str); final qrImage = QrImage(qrCode); assert( qrImage.moduleCount == moduleCount); @@ -372,8 +377,9 @@ String getStringFromUser(String prompt, [String defaultValue=""] ) { stdout.write(prompt); str = (stdin.readLineSync())??""; - if( str.length == 0) + if( str.isEmpty) { str = defaultValue; + } return str; } @@ -399,21 +405,21 @@ String getCommaSeparatedInts(Set? kind) { return ""; } - if( kind.length == 0) { + if( kind.isEmpty) { return ""; } String strKind = ""; int i = 0; - kind.forEach((k) { + for (var k in kind) { String comma = ","; if( i == kind.length-1) { comma = ""; } strKind = strKind + k.toString() + comma; i++; - }); + } return strKind; } @@ -432,16 +438,14 @@ String getKindRequest(String subscriptionId, List kind, int limit, int sinc return strRequest; } -String getUserRequest(String subscriptionId, String publicKey, int numUserEvents, int sinceWhen, [Set? _kind = null]) { +String getUserRequest(String subscriptionId, String publicKey, int numUserEvents, int sinceWhen, [Set? kind]) { Set kind = {}; - if( _kind != null) { - kind = _kind; - } + kind = kind; String strKind = getCommaSeparatedInts(kind); String strKindSection = ""; - if( strKind.length > 0) { + if( strKind.isNotEmpty) { strKindSection = '"kinds":[$strKind],'; } @@ -477,12 +481,12 @@ String getIdAndMentionRequest(String subscriptionId, Set ids, int numUse var strSubscription1 = '["REQ","$subscriptionId",{ "$tagToGet": ['; var strSubscription2 ='], "limit": $numUserEvents $idStrTime } ]'; - String req = '["REQ","$subscriptionId",{ "$tagToGet": [' + getCommaSeparatedQuotedStrs(ids) + '], "limit": $numUserEvents $mentionStrTime},{"$idString":[' + getCommaSeparatedQuotedStrs(ids) + ']$idStrTime}]'; + String req = '["REQ","$subscriptionId",{ "$tagToGet": [${getCommaSeparatedQuotedStrs(ids)}], "limit": $numUserEvents $mentionStrTime},{"$idString":[${getCommaSeparatedQuotedStrs(ids)}]$idStrTime}]'; return req; } -String getMultiUserRequest(String subscriptionId, Set publicKeys, int numUserEvents, int sinceWhen, [Set? kind = null]) { +String getMultiUserRequest(String subscriptionId, Set publicKeys, int numUserEvents, int sinceWhen, [Set? kind]) { String strTime = ""; if( sinceWhen != 0) { strTime = ', "since": ${sinceWhen.toString()}'; @@ -491,7 +495,7 @@ String getMultiUserRequest(String subscriptionId, Set publicKeys, int nu String strKind = getCommaSeparatedInts(kind); String strKindSection = ""; - if( strKind.length > 0) { + if( strKind.isNotEmpty) { strKindSection = '"kinds":[$strKind],'; } @@ -508,14 +512,14 @@ void printSet( Set toPrint, [ String prefix = "", String separator = ""] stdout.write(prefix); int i = 0; - toPrint.forEach((element) { + for (var element in toPrint) { if( i != 0) { stdout.write(separator); } stdout.write(element); i++; - }); + } stdout.write("\n"); } diff --git a/pubspec.yaml b/pubspec.yaml index adb9b7d..29f8cf2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,7 +91,8 @@ homepage: https://github.com/vishalxl/nostr_console environment: - sdk: '>=2.17.3 <3.0.0' + sdk: '>=2.17.3 <4.0.0' + dev_dependencies: @@ -107,3 +108,5 @@ dependencies: logging: ^1.0.2 kepler: ^1.0.3 qr: ^3.0.1 + pointycastle: any + http: any diff --git a/test/nostr_console_test.dart b/test/nostr_console_test.dart index 3f17a39..2df6e68 100644 --- a/test/nostr_console_test.dart +++ b/test/nostr_console_test.dart @@ -221,11 +221,11 @@ String expectedResult = "11 https://github.com/nostr-protocol/nips/blob/master/16.md#ephemeral-events", "https://res.cloudinary.com/eskema/image/upload/v1669030722/306072883_413474904244526_502927779121754777_n.jpg_l6je2d.jpg"]; - urls.forEach((url) { + for (var url in urls) { String res = makeParagraphAtDepth(url, 30); //print(url); print(res);print(""); expect( res, url); - }); + } }); @@ -233,27 +233,33 @@ String expectedResult = Set initialEvents = {}; // collect all events here and then create tree out of them - String input_filename = 'test_event_file.csv'; - initialEvents = await readEventsFromFile(input_filename); + String inputFilename = 'test_event_file.csv'; + initialEvents = readEventsFromFile(inputFilename); int numFilePosts = 0; // count events - initialEvents.forEach((element) { element.eventData.kind == 1? numFilePosts++: numFilePosts;}); + for (var element in initialEvents) { element.eventData.kind == 1? numFilePosts++: numFilePosts;} //print("read $numFilePosts posts from file $gEventsFilename"); expect(numFilePosts, 3486, reason:'Verify right number of kind 1 posts'); - Store node = await getTree(initialEvents); + Store node = getTree(initialEvents); expect(0, node.getNumDirectRooms(), reason:'verify correct number of direct chat rooms created'); int numKind4xChannels = 0; - node.channels.forEach((channel) => channel.roomType == enumRoomType.kind40? numKind4xChannels++:1); + for (var channel in node.channels) { + channel.roomType == enumRoomType.kind40? numKind4xChannels++:1; + } int numTTagChannels = 0; - node.channels.forEach((channel) => channel.roomType == enumRoomType.RoomTTag? numTTagChannels++:1); + for (var channel in node.channels) { + channel.roomType == enumRoomType.RoomTTag? numTTagChannels++:1; + } int numLocationTagChannels = 0; - node.channels.forEach((channel) => channel.roomType == enumRoomType.RoomLocationTag? numLocationTagChannels++:1); + for (var channel in node.channels) { + channel.roomType == enumRoomType.RoomLocationTag? numLocationTagChannels++:1; + } expect(78, numKind4xChannels, reason: 'verify correct number of public channels created of kind 4x'); expect(41, numTTagChannels, reason: 'verify correct number of public channels created of T tag type');