mirror of
https://github.com/vishalxl/nostr_console.git
synced 2025-05-12 13:20:11 +02:00
used kind 104 to send shared secret for kind 14x
also internally improved logic that encrypted channels internal structures are created by first looking at kind 104 (secret kind id) mentioned version 0.1.9-beta
This commit is contained in:
parent
03317a1dfa
commit
32bc7d2bc0
@ -304,8 +304,13 @@ Future<void> main(List<String> arguments) async {
|
||||
getMultiUserEvents(gListRelayUrls1, gDefaultFollows, limitPerSubscription, getSecondsDaysAgo(limitFollowPosts));
|
||||
|
||||
// get group and meta info events
|
||||
getKindEvents([0, 3, 40, 41, 140, 141], gListRelayUrls1, 3 * limitPerSubscription, getSecondsDaysAgo(limitMetaInfoEvents)); // get all type 3 etc
|
||||
getKindEvents([42, 142], gListRelayUrls1, limitPerSubscription * 3, getSecondsDaysAgo( limitFollowPosts)); // get all type 3 etc
|
||||
getKindEvents([0, 3, 40, 41], gListRelayUrls1, 3 * limitPerSubscription, getSecondsDaysAgo(limitMetaInfoEvents)); // get all type 3 etc
|
||||
getKindEvents([140, 141], gListRelayUrls1, 3 * limitPerSubscription, getSecondsDaysAgo(1)); // get all type 3 etc
|
||||
|
||||
getKindEvents([42], gListRelayUrls1, limitPerSubscription * 3, getSecondsDaysAgo( limitFollowPosts)); // get all type 3 etc
|
||||
getKindEvents([142], gListRelayUrls1, limitPerSubscription * 3, getSecondsDaysAgo( 1)); // get all type 3 etc
|
||||
|
||||
getKindEvents([gSecretMessageKind], gListRelayUrls1, limitPerSubscription * 3, getSecondsDaysAgo( 1)); // get all type 3 etc
|
||||
|
||||
// TODO get all 40 events, and then get all #e for them ( responses to them)
|
||||
|
||||
|
@ -132,12 +132,14 @@ Future<void> sendChannelReply(Store node, Channel channel, String replyTo, Strin
|
||||
}
|
||||
|
||||
// send DM
|
||||
Future<void> sendDirectMessage(Store node, String otherPubkey, String messageToSend) async {
|
||||
Future<void> 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 encryptedMessageToSend = myEncrypt(userPrivateKey, otherPubkey02, messageToSend);
|
||||
|
||||
String replyKind = "4";
|
||||
//print("in sendDirectMessage: replyKind = $replyKind");
|
||||
|
||||
//String replyKind = "4";
|
||||
String strTags = '["p","$otherPubkey"]';
|
||||
strTags += gWhetherToSendClientTag?',["client","nostr_console"]':'';
|
||||
int createdAt = DateTime.now().millisecondsSinceEpoch ~/1000;
|
||||
@ -609,7 +611,7 @@ Future<void> channelMenuUI(Store node) async {
|
||||
//await processNotifications(node); // this takes 300 ms
|
||||
if( !justShowedChannels) {
|
||||
printInColor(" Public Channels ", gCommentColor);
|
||||
node.printChannelsOverview(node.channels, 20, selectorShowAllRooms);
|
||||
node.printChannelsOverview(node.channels, 20, selectorShowAllRooms, node.allChildEventsMap, null);
|
||||
justShowedChannels = true;
|
||||
}
|
||||
|
||||
@ -645,7 +647,7 @@ Future<void> channelMenuUI(Store node) async {
|
||||
firstIteration = false;
|
||||
}
|
||||
|
||||
String fullChannelId = node.showChannel(node.channels, channelId, pageNum);
|
||||
String fullChannelId = node.showChannel(node.channels, channelId, null, null, null, pageNum); // direct channel does not need this, only encrypted channels needs them
|
||||
if( fullChannelId == "") {
|
||||
//print("Could not find the given channel.");
|
||||
showChannelOption = false;
|
||||
@ -719,7 +721,7 @@ Future<void> channelMenuUI(Store node) async {
|
||||
case 2:
|
||||
clearScreen();
|
||||
printInColor(" All Public Channels ", gCommentColor);
|
||||
node.printChannelsOverview(node.channels, node.channels.length, selectorShowAllRooms);
|
||||
node.printChannelsOverview(node.channels, node.channels.length, selectorShowAllRooms, node.allChildEventsMap, null);
|
||||
justShowedChannels = true;
|
||||
break;
|
||||
|
||||
@ -777,7 +779,7 @@ Future<void> createEncryptedChannel(Store node) async {
|
||||
String messageToSend = "App Encrypted Channels: inviting you to encrypted channel $newEncryptedChannelId encrypted using private public keys $channelPriKey $channelPubKey";
|
||||
for( int i = 0; i < participants.length; i++) {
|
||||
// send message to all ( including self which is in that list)
|
||||
await sendDirectMessage(node, participants[i], messageToSend);
|
||||
await sendDirectMessage(node, participants[i], messageToSend, replyKind: gSecretMessageKind.toString());
|
||||
}
|
||||
|
||||
await processAnyIncomingEvents(node, false); // get latest event, this takes 300 ms
|
||||
@ -788,7 +790,7 @@ Future<void> updateEncryptedChannel(Store node, String channelId,
|
||||
String channelName, String channelAbout, String channelPic, String content, String tags,
|
||||
Set<String> participants, Set<String> newParticipants) async {
|
||||
|
||||
List<String> keys = getEncryptedChannelKeys(node.directRooms, node.allChildEventsMap, channelId);
|
||||
List<String> keys = getEncryptedChannelKeys(node.encryptedGroupSecretIds, node.allChildEventsMap, channelId);
|
||||
if( keys.length == 2) {
|
||||
String channelPriKey = keys[0], channelPubKey = keys[1];
|
||||
|
||||
@ -797,7 +799,7 @@ Future<void> updateEncryptedChannel(Store node, String channelId,
|
||||
|
||||
// send message to all new participants
|
||||
newParticipants.forEach((participant) async {
|
||||
await sendDirectMessage(node, participant, messageToSend);
|
||||
await sendDirectMessage(node, participant, messageToSend, replyKind: gSecretMessageKind.toString());
|
||||
});
|
||||
|
||||
int createdAt = DateTime.now().millisecondsSinceEpoch ~/1000;
|
||||
@ -815,7 +817,7 @@ Future<void> updateEncryptedChannel(Store node, String channelId,
|
||||
String encryptChannelMessage(Store node, String channelId, String messageToSend) {
|
||||
String encryptedMessage = '';
|
||||
|
||||
List<String> keys = getEncryptedChannelKeys(node.directRooms, node.allChildEventsMap, channelId);
|
||||
List<String> keys = getEncryptedChannelKeys(node.encryptedGroupSecretIds, node.allChildEventsMap, channelId);
|
||||
if( keys.length != 2) {
|
||||
return '';
|
||||
}
|
||||
@ -888,7 +890,7 @@ Future<void> encryptedChannelMenuUI(Store node) async {
|
||||
|
||||
if( !justShowedChannels) {
|
||||
printInColor(" Encrypted Channels ", gCommentColor);
|
||||
node.printChannelsOverview(node.encryptedChannels, 20, selectorShowAllRooms);
|
||||
node.printChannelsOverview(node.encryptedChannels, 20, selectorShowAllRooms, node.allChildEventsMap, node.encryptedGroupSecretIds);
|
||||
justShowedChannels = true;
|
||||
}
|
||||
|
||||
@ -926,7 +928,7 @@ Future<void> encryptedChannelMenuUI(Store node) async {
|
||||
firstIteration = false;
|
||||
}
|
||||
|
||||
String fullChannelId = node.showChannel(node.encryptedChannels, channelId, pageNum);
|
||||
String fullChannelId = node.showChannel(node.encryptedChannels, channelId, node.allChildEventsMap, node.encryptedGroupSecretIds, node.encryptedChannels, pageNum);
|
||||
if( fullChannelId == "") {
|
||||
//print("Could not find the given channel.");
|
||||
showChannelOption = false;
|
||||
@ -1007,7 +1009,7 @@ Future<void> encryptedChannelMenuUI(Store node) async {
|
||||
case 2:
|
||||
clearScreen();
|
||||
printInColor(" All Encrypted Channels ", gCommentColor);
|
||||
node.printChannelsOverview(node.encryptedChannels, node.encryptedChannels.length, selectorShowAllRooms);
|
||||
node.printChannelsOverview(node.encryptedChannels, node.encryptedChannels.length, selectorShowAllRooms, node.allChildEventsMap, node.encryptedGroupSecretIds);
|
||||
justShowedChannels = true;
|
||||
break;
|
||||
|
||||
@ -1036,7 +1038,7 @@ Future<void> PrivateMenuUI(Store node) async {
|
||||
await processAnyIncomingEvents(node, true); // this takes 300 ms
|
||||
|
||||
printInColor(" Direct Messages", gCommentColor);
|
||||
node.printDirectRoomInfo(showAllRooms);
|
||||
node.printDirectRoomInfo(showAllRooms, node.allChildEventsMap);
|
||||
|
||||
String menuInfo = """Direct Message howto: To send a Direct Message to someone for the first time, enter their 64 byte hex pubkey.
|
||||
To enter or continue a conversation seen in overview, enter the first few letters of the other person's name or of their pubkey""";
|
||||
@ -1368,7 +1370,7 @@ void showInitialNotifications(Store node) {
|
||||
print("\n");
|
||||
|
||||
bool showNotifications (ScrollableMessages room) => room.selectorNotifications();
|
||||
int numDirectRoomsPrinted = node.printDirectRoomInfo(showNotifications);
|
||||
int numDirectRoomsPrinted = node.printDirectRoomInfo( showNotifications, node.allChildEventsMap);
|
||||
|
||||
if( numDirectRoomsPrinted > 0)
|
||||
print("\n");
|
||||
|
@ -281,7 +281,7 @@ class EventData {
|
||||
}
|
||||
} else {
|
||||
int eKind = json['kind'];
|
||||
if ( eKind == 1 || eKind == 7 || eKind == 42 || eKind == 5 || eKind == 4 || eKind == 140 || eKind == 141 || eKind == 142) {
|
||||
if ( eKind == 1 || eKind == 7 || eKind == 42 || eKind == 5 || eKind == 4 || eKind == 140 || eKind == 141 || eKind == 142 || eKind == gSecretMessageKind) {
|
||||
for( int i = 0; i < numTags; i++) {
|
||||
var tag = jsonTags[i];
|
||||
|
||||
@ -338,7 +338,7 @@ class EventData {
|
||||
}
|
||||
|
||||
// replace the patterns
|
||||
|
||||
//print("in main body of expandmentions");
|
||||
for(int i = 0; i < nip08PlaceHolders.length && i < tags.length; i++) {
|
||||
int index = -1;
|
||||
Pattern p = nip08PlaceHolders[i];
|
||||
@ -364,19 +364,18 @@ class EventData {
|
||||
}
|
||||
|
||||
// is called only once for each event received ( or read from file)
|
||||
void translateAndExpandMentions(List<DirectMessageRoom> directRooms, Map<String, Tree> tempChildEventsMap) {
|
||||
void translateAndExpandMentions(Map<String, Tree> tempChildEventsMap) {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndExpandMensitons: decoding $gCheckEventId\n", redColor);
|
||||
//printInColor("in translateAndExpandMentions: decoding $gCheckEventId\n", redColor);
|
||||
}
|
||||
|
||||
if (content == "" || evaluatedContent != "") {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndExpandMensitons: returning \n", redColor);
|
||||
//printInColor("in translateAndExpandMentions: returning \n", redColor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch(kind) {
|
||||
case 1:
|
||||
case 42:
|
||||
@ -404,6 +403,64 @@ class EventData {
|
||||
}
|
||||
break;
|
||||
|
||||
} // end switch
|
||||
return;
|
||||
} // end translateAndExpandMentions
|
||||
|
||||
// is called only once for each event received ( or read from file)
|
||||
String? TranslateAndDecryptSecretMessage(Map<String, Tree> tempChildEventsMap) {
|
||||
if( id == gCheckEventId) {
|
||||
//printInColor("in TranslateAndDecryptSecretMessage: decoding $gCheckEventId\n", redColor);
|
||||
}
|
||||
|
||||
if (content == "" || evaluatedContent != "") {
|
||||
if( id == gCheckEventId) {
|
||||
//printInColor("in TranslateAndDecryptSecretMessage: returning \n", redColor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
switch(kind) {
|
||||
case gSecretMessageKind:
|
||||
if( userPrivateKey == ""){ // cant process if private key not given
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!isValidDirectMessage(this)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if( id == gCheckEventId) {
|
||||
//printInColor("in translateAndExpandMensitons: gonna decrypt \n", redColor);
|
||||
}
|
||||
|
||||
//log.info("decrypting a secret message");
|
||||
|
||||
String? decrypted = decryptDirectMessage();
|
||||
if( decrypted != null) {
|
||||
evaluatedContent = decrypted;
|
||||
}
|
||||
return id;
|
||||
} // end switch
|
||||
|
||||
return null;
|
||||
} // end TranslateAndDecryptSecretMessage
|
||||
|
||||
|
||||
// is called only once for each event received ( or read from file)
|
||||
void translateAndDecryptKind4(Map<String, Tree> tempChildEventsMap) {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndDecryptKind4: decoding $gCheckEventId\n", redColor);
|
||||
}
|
||||
|
||||
if (content == "" || evaluatedContent != "") {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndDecryptKind4: returning \n", redColor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch(kind) {
|
||||
case 4:
|
||||
if( userPrivateKey == ""){ // cant process if private key not given
|
||||
break;
|
||||
@ -417,37 +474,63 @@ class EventData {
|
||||
printInColor("in translateAndExpandMensitons: gonna decrypt \n", redColor);
|
||||
}
|
||||
|
||||
//log.info("decrypting a message of kind 4");
|
||||
|
||||
String? decrypted = decryptDirectMessage();
|
||||
if( decrypted != null) {
|
||||
evaluatedContent = decrypted;
|
||||
evaluatedContent = expandMentions(evaluatedContent, tempChildEventsMap);
|
||||
}
|
||||
//print("evaluatedContent: $evaluatedContent");
|
||||
break;
|
||||
|
||||
|
||||
} // end switch
|
||||
} // end translateAndExpandMentions
|
||||
|
||||
|
||||
// is called only once for each event received ( or read from file)
|
||||
void translateAndExpand14x(List<DirectMessageRoom> directRooms, List<Channel> encryptedChannels, Map<String, Tree> tempChildEventsMap) {
|
||||
void translateAndDecrypt14x(List<String> secretMessageIds, List<Channel> encryptedChannels, Map<String, Tree> tempChildEventsMap) {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndExpandMensitons: decoding ee810ea73072af056cceaa6d051b4fcce60739247f7bcc752e72fa5defb64f09\n", redColor);
|
||||
//printInColor("in translateAndExpand14x: decoding ee810ea73072af056cceaa6d051b4fcce60739247f7bcc752e72fa5defb64f09\n", redColor);
|
||||
}
|
||||
|
||||
if (content == "" || evaluatedContent != "") {
|
||||
if( id == gCheckEventId) {
|
||||
printInColor("in translateAndExpandMensitons: returning \n", redColor);
|
||||
//printInColor("in translateAndExpand14x: returning \n", redColor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( createdAt < getSecondsDaysAgo(1)) {
|
||||
//print("old 142. not decrypting");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch(kind) {
|
||||
case 142:
|
||||
String? decrypted = decryptEncryptedChannelMessage(directRooms, encryptedChannels, tempChildEventsMap);
|
||||
//print("in translateAndDecrypt14x");
|
||||
Channel? channel = getChannelForMessage( encryptedChannels, id);
|
||||
if( channel == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!channel.participants.contains(userPublicKey)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!channel.participants.contains(pubkey)) {
|
||||
break;
|
||||
}
|
||||
|
||||
String? decrypted = decryptEncryptedChannelMessage(secretMessageIds, tempChildEventsMap);
|
||||
if( decrypted != null) {
|
||||
//printWarning("Successfully decrypted kind 142: $id");
|
||||
evaluatedContent = decrypted;
|
||||
//print("in translateAndDecrypt14x: calling expandMentions");
|
||||
evaluatedContent = expandMentions(evaluatedContent, tempChildEventsMap);
|
||||
//print("content = $content");
|
||||
//print(evaluatedContent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -502,34 +585,21 @@ class EventData {
|
||||
}
|
||||
|
||||
|
||||
String? decryptEncryptedChannelMessage(List<DirectMessageRoom> directRooms, List<Channel> encryptedChannels,Map<String, Tree> tempChildEventsMap) {
|
||||
Channel? channel = getChannelForMessage( encryptedChannels, id);
|
||||
if( channel == null) {
|
||||
print("could not find channel");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!channel.participants.contains(userPublicKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!channel.participants.contains(pubkey)) {
|
||||
return null;
|
||||
}
|
||||
String? decryptEncryptedChannelMessage(List<String> secretMessageIds, Map<String, Tree> tempChildEventsMap) {
|
||||
|
||||
if( id == "865c9352de11a3959c06fce5350c5a1b9fa0475d3234078a1bb45d152b370f0b") { // known issue
|
||||
|
||||
//print("\n\ngoing to decrypt b1ab66ac50f00f3c3bbc91e5b9e03fc8e79e3fdb9f6d5c9ae9777aa6ca3020a2");
|
||||
//print(channel.participants);
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
//print("Going to decrypt event id: $id");
|
||||
//printWarning("Going to decrypt 14x event id: $id");
|
||||
|
||||
//print("In decryptEncryptedChannelMessage: for event of kind 142 with event id = $id");
|
||||
int ivIndex = content.indexOf("?iv=");
|
||||
if( ivIndex == -1) {
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
var iv = content.substring( ivIndex + 4, content.length);
|
||||
var enc_str = content.substring(0, ivIndex);
|
||||
@ -537,11 +607,11 @@ class EventData {
|
||||
String channelId = getChannelIdForMessage();
|
||||
//print("In decryptEncryptedChannelMessage: got channel id $channelId");
|
||||
List<String> keys = [];
|
||||
keys = getEncryptedChannelKeys(directRooms, tempChildEventsMap, channelId);
|
||||
keys = getEncryptedChannelKeys(secretMessageIds, tempChildEventsMap, channelId);
|
||||
|
||||
if( keys.length != 2) {
|
||||
//print("Could not get keys for event id: $id and channelId: $channelId");
|
||||
return "";
|
||||
print("Could not get keys for event id: $id and channelId: $channelId");
|
||||
return null;
|
||||
}
|
||||
|
||||
//print("\nevent id: $id");
|
||||
@ -551,6 +621,7 @@ class EventData {
|
||||
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
|
||||
//print("decrypted = |$decrypted|");
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
@ -571,11 +642,24 @@ class EventData {
|
||||
}
|
||||
|
||||
// prints event data in the format that allows it to be shown in tree form by the Tree class
|
||||
void printEventData(int depth, bool topPost) {
|
||||
void printEventData(int depth, bool topPost, Map<String, Tree>? tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels) {
|
||||
if( !(kind == 1 || kind == 4 || kind == 42)) {
|
||||
return; // only print kind 1 and 42 and 4
|
||||
}
|
||||
|
||||
// will only do decryption if its not been decrypted yet by looking at 'evaluatedContent'
|
||||
if( tempChildEventsMap != null )
|
||||
if(kind == 4)
|
||||
translateAndDecryptKind4( tempChildEventsMap);
|
||||
else if ([1, 42].contains(kind)) {
|
||||
translateAndExpandMentions(tempChildEventsMap);
|
||||
} else if ([142].contains(kind)) {
|
||||
if( secretMessageIds != null && encryptedChannels != null) {
|
||||
translateAndDecrypt14x( secretMessageIds, encryptedChannels, tempChildEventsMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int n = 4;
|
||||
String maxN(String v) => v.length > n? v.substring(0,n) : v.substring(0, v.length);
|
||||
|
||||
@ -672,12 +756,25 @@ class EventData {
|
||||
stdout.write(strToPrint);
|
||||
}
|
||||
|
||||
String getAsLine({int len = 20}) {
|
||||
String getAsLine(var tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels, {int len = 20}) {
|
||||
|
||||
// will only do decryption if its not been decrypted yet by looking at 'evaluatedContent'
|
||||
if(kind == 4)
|
||||
translateAndDecryptKind4( tempChildEventsMap);
|
||||
else if ([1, 42].contains(kind)) {
|
||||
translateAndExpandMentions(tempChildEventsMap);
|
||||
} else if ([142].contains(kind)) {
|
||||
if( tempChildEventsMap != null && secretMessageIds != null && encryptedChannels != null) {
|
||||
translateAndDecrypt14x(secretMessageIds, encryptedChannels, tempChildEventsMap);
|
||||
}
|
||||
}
|
||||
|
||||
String contentToPrint = evaluatedContent.isEmpty? content: evaluatedContent;
|
||||
if( len == 0 || len > contentToPrint.length) {
|
||||
//len = contentToPrint.length;
|
||||
}
|
||||
|
||||
|
||||
contentToPrint = contentToPrint.replaceAll("\n", " ");
|
||||
contentToPrint = contentToPrint.replaceAll("\r", " ");
|
||||
contentToPrint = contentToPrint.replaceAll("\t", " ");
|
||||
@ -691,10 +788,26 @@ class EventData {
|
||||
paddedStrToPrint = "$gNotificationColor$paddedStrToPrint$gColorEndMarker";
|
||||
isNotification = false;
|
||||
}
|
||||
//print("returning $paddedStrToPrint");
|
||||
return paddedStrToPrint;
|
||||
}
|
||||
|
||||
String getStrForChannel(int depth) {
|
||||
|
||||
String getStrForChannel(int depth, var tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels) {
|
||||
|
||||
// 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)
|
||||
translateAndDecryptKind4( tempChildEventsMap);
|
||||
else if ([1, 42].contains(kind)) {
|
||||
translateAndExpandMentions(tempChildEventsMap);
|
||||
} else if ([142].contains(kind)) {
|
||||
if( tempChildEventsMap != null && secretMessageIds != null && encryptedChannels != null) {
|
||||
//print('decrypting 14x in getStrForChannel');
|
||||
translateAndDecrypt14x(secretMessageIds, encryptedChannels, tempChildEventsMap);
|
||||
}
|
||||
}
|
||||
|
||||
String strToPrint = "";
|
||||
String name = getAuthorName(pubkey);
|
||||
String strDate = getPrintableDate(createdAt);
|
||||
@ -859,7 +972,7 @@ class Event {
|
||||
}
|
||||
|
||||
void printEvent(int depth, bool topPost) {
|
||||
eventData.printEventData(depth, topPost);
|
||||
eventData.printEventData(depth, topPost, null, null, null);
|
||||
//stdout.write("\n$originalJson --------------------------------\n\n");
|
||||
}
|
||||
|
||||
@ -1463,21 +1576,32 @@ bool isValidDirectMessage(EventData directMessageData) {
|
||||
bool validUserMessage = false;
|
||||
|
||||
List<String> allPtags = [];
|
||||
|
||||
if( [4, gSecretMessageKind].contains( directMessageData.kind) ) {
|
||||
if(gDebug > 0 && gCheckEventId == directMessageData.id) print("in isValidDirectMessage for kind $gSecretMessageKind. its id = ${directMessageData.id}");
|
||||
}
|
||||
|
||||
directMessageData.tags.forEach((tag) {
|
||||
if( tag.length < 2 )
|
||||
if( tag.length < 2 ) {
|
||||
return;
|
||||
}
|
||||
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( 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
|
||||
validUserMessage = true; // case where this user is sender
|
||||
}
|
||||
} else {
|
||||
if(gCheckEventId == directMessageData.id) print("in else case 3");
|
||||
if ( directMessageData.pubkey != userPublicKey) {
|
||||
if(gDebug > 0 && gCheckEventId == directMessageData.id) print("in if 5 allpags 1st = ${allPtags[0]} userPUblic key = $userPublicKey");
|
||||
if( allPtags.length == 1 && allPtags[0] == userPublicKey) {
|
||||
|
||||
validUserMessage = true; // case where this user is recipeint
|
||||
}
|
||||
}
|
||||
@ -1656,17 +1780,15 @@ bool isValidPubkey(String pubkey) {
|
||||
}
|
||||
|
||||
|
||||
List<String> getEncryptedChannelKeys(List<DirectMessageRoom> directRooms, Map<String, Tree> tempChildEventsMap, String channelId) {
|
||||
List<String> getEncryptedChannelKeys(List<String> secretMessageIds, Map<String, Tree> tempChildEventsMap, String channelId) {
|
||||
Event? e = tempChildEventsMap[channelId]?.event;
|
||||
if( e != null) {
|
||||
//print("\n----------------\nIn getEncryptedChannelKeys for encrypted channel $channelId");
|
||||
String creatorPubKey = e.eventData.pubkey;
|
||||
for( int i = 0; i < directRooms.length; i++) {
|
||||
DirectMessageRoom room = directRooms[i];
|
||||
if( room.otherPubkey == creatorPubKey) {
|
||||
//print("got other pubkey $creatorPubKey");
|
||||
for( int j = 0; j < room.messageIds.length; j++) {
|
||||
String messageId = room.messageIds[j];
|
||||
|
||||
|
||||
for( int j = 0; j < secretMessageIds.length; j++) {
|
||||
String messageId = secretMessageIds[j];
|
||||
|
||||
Event? messageEvent = tempChildEventsMap[messageId]?.event;
|
||||
if( messageEvent != null) {
|
||||
@ -1691,8 +1813,6 @@ List<String> getEncryptedChannelKeys(List<DirectMessageRoom> directRooms, Map<St
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ class Relays {
|
||||
if( rEvents.add(e) ) {
|
||||
uniqueIdsRecieved.add(id);
|
||||
String receivedSubscription = json[1];
|
||||
if( gDebug > 3) e.eventData.printEventData(0, true);
|
||||
if( gDebug > 3) e.eventData.printEventData(0, true, null, null, null);
|
||||
if( gDebug > 2) print("");
|
||||
|
||||
if( gDebug > 1) log.info("In relay listener for relay url $relay: after adding element, rEvents Size = ${rEvents.length} numReceived = ${newRelay.numReceived} for subscription $receivedSubscription");
|
||||
|
@ -3,7 +3,7 @@ import 'package:logging/logging.dart';
|
||||
|
||||
// name of executable
|
||||
const String exename = "nostr_console";
|
||||
const String version = "0.1.8-beta";
|
||||
const String version = "0.1.9-beta";
|
||||
|
||||
int gDebug = 0;
|
||||
int gSpecificDebug = 0;
|
||||
@ -11,10 +11,13 @@ int gSpecificDebug = 0;
|
||||
final log = Logger('ExampleLogger');
|
||||
|
||||
// for debugging
|
||||
String gCheckEventId = " e74e93fbc77af5275f29db688931f725813ab1385f16233bfa609078a8779dfa";
|
||||
String gCheckEventId = "0082a613c9f4d43f796a427d15db74f10ac64212e28f465870ceaf099f488087";
|
||||
|
||||
int gDefaultNumWaitSeconds = 8000; // is used in main()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// encrypted Group settings
|
||||
const int gSecretMessageKind = 104;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// file related settings
|
||||
const String gDefaultEventsFilename = "all_nostr_events.txt";
|
||||
String gEventsFilename = ""; // is set in arguments, and if set, then file is read from and written to
|
||||
|
360
lib/tree_ds.dart
360
lib/tree_ds.dart
@ -240,7 +240,7 @@ class ScrollableMessages {
|
||||
|
||||
|
||||
|
||||
void printOnePage(Map<String, Tree> tempChildEventsMap, [int page = 1]) {
|
||||
void printOnePage(Map<String, Tree> tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels, [int page = 1] ) {
|
||||
if( page < 1) {
|
||||
if( gDebug > 0) log.info("In ScrollableMessages::printOnepage got page = $page");
|
||||
page = 1;
|
||||
@ -266,8 +266,8 @@ class ScrollableMessages {
|
||||
String eId = messageIds[i];
|
||||
Event? e = tempChildEventsMap[eId]?.event;
|
||||
if( e!= null) {
|
||||
if( !(e.eventData.kind == 142 && (e.eventData.content == e.eventData.evaluatedContent))) // condition so that in encrypted channels non-encrypted messages aren't printed
|
||||
print(e.eventData.getStrForChannel(0));
|
||||
//printWarning("in print one page");
|
||||
print(e.eventData.getStrForChannel(0, tempChildEventsMap, secretMessageIds, encryptedChannels));
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +373,7 @@ class DirectMessageRoom extends ScrollableMessages{
|
||||
if( gDebug > 0) log.info("In printChannel got page = $page");
|
||||
page = 1;
|
||||
}
|
||||
printOnePage(store.allChildEventsMap, page);
|
||||
printOnePage(store.allChildEventsMap, null, null, page);
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,40 +454,31 @@ class Tree {
|
||||
}
|
||||
|
||||
// returns true if the treee or its children has a reply or like for the user with public key pk; and notification flags are set for such events
|
||||
bool treeSelectorRepliesAndLikes(String pk) {
|
||||
bool treeSelectorRepliesAndLikes(String pubkey) {
|
||||
bool hasReaction = false;
|
||||
bool childMatches = false;
|
||||
|
||||
if( event.eventData.pubkey == pk && gReactions.containsKey(event.eventData.id)) {
|
||||
if( event.eventData.pubkey == pubkey && gReactions.containsKey(event.eventData.id)) {
|
||||
List<List<String>>? reactions = gReactions[event.eventData.id];
|
||||
if( reactions != null) {
|
||||
if( reactions.length > 0) {
|
||||
// has reactions
|
||||
reactions.forEach((reaction) {
|
||||
// dont add notificatoin for self reaction
|
||||
Event? reactorEvent = store?.allChildEventsMap[reaction[0]]?.event;
|
||||
if( reactorEvent != null) {
|
||||
if( reactorEvent.eventData.pubkey != pk){ // ignore self likes
|
||||
event.eventData.newLikes.add(reaction[0]);
|
||||
hasReaction = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
event.eventData.isNotification = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( event.eventData.pubkey == pk && children.length > 0) {
|
||||
if( event.eventData.pubkey == pubkey && children.length > 0) {
|
||||
for( int i = 0; i < children.length; i++ ) {
|
||||
children.forEach((child) {
|
||||
// if child is someone else then set notifications and flag, means there are replies to this event
|
||||
childMatches = child.event.eventData.isNotification = ((child.event.eventData.pubkey != pk)? true: false) ;
|
||||
childMatches = child.event.eventData.isNotification = ((child.event.eventData.pubkey != pubkey)? true: false) ;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = 0; i < children.length; i++ ) {
|
||||
if( children[i].treeSelectorRepliesAndLikes(pk)) {
|
||||
if( children[i].treeSelectorRepliesAndLikes(pubkey)) {
|
||||
childMatches = true;
|
||||
}
|
||||
}
|
||||
@ -549,6 +540,7 @@ class Tree {
|
||||
childMatches = true;
|
||||
}
|
||||
}
|
||||
if( event.eventData.id == gCheckEventId) printWarning("found the event $gCheckEventId");
|
||||
|
||||
if( event.eventData.content.toLowerCase().contains(word) || event.eventData.id == word ) {
|
||||
event.eventData.isNotification = true;
|
||||
@ -644,12 +636,14 @@ class Store {
|
||||
List<Channel> encryptedChannels = [];
|
||||
List<DirectMessageRoom> directRooms = [];
|
||||
|
||||
List<String> encryptedGroupSecretIds; // event id's of gSecretMessageKind messages, which contain encrypted room secrets
|
||||
|
||||
static String startMarkerStr = "" ;
|
||||
static String endMarkerStr = "";
|
||||
|
||||
static const Set<int> typesInEventMap = {0, 1, 3, 4, 5, 7, 40, 42, 140, 141, 142}; // 0 meta, 1 post, 3 follows list, 7 reactions
|
||||
static const Set<int> typesInEventMap = {0, 1, 3, 4, 5, 7, 40, 42, 140, 141, 142, gSecretMessageKind}; // 0 meta, 1 post, 3 follows list, 7 reactions
|
||||
|
||||
Store(this.topPosts, this.allChildEventsMap, this.eventsWithoutParent, this.channels, this.encryptedChannels, this.directRooms) {
|
||||
Store(this.topPosts, this.allChildEventsMap, this.eventsWithoutParent, this.channels, this.encryptedChannels, this.directRooms, this.encryptedGroupSecretIds) {
|
||||
allChildEventsMap.forEach((eventId, tree) {
|
||||
if( tree.store == null) {
|
||||
tree.setStore(this);
|
||||
@ -729,10 +723,84 @@ class Store {
|
||||
} // end switch
|
||||
}
|
||||
|
||||
static void handleEncryptedChannelEvents( List<DirectMessageRoom> directRooms, List<Channel> encryptedChannels, Map<String, Tree> tempChildEventsMap, Event ce) {
|
||||
static String? getEncryptedChannelIdFromSecretMessage( List<String> secretMessageIds, Map<String, Tree> tempChildEventsMap, Event eventSecretMessage) {
|
||||
String evaluatedContent = eventSecretMessage.eventData.evaluatedContent;
|
||||
|
||||
//print("In getEncryptedChannelIdFromSecretMessage: evaluatedContent length = ${evaluatedContent.length}\nevaluatedContent = ${evaluatedContent} ");
|
||||
if( evaluatedContent.startsWith("App Encrypted Channels:")) {
|
||||
//print("got App");
|
||||
if(evaluatedContent.length == 288) {
|
||||
String channelId = evaluatedContent.substring(58, 58 + 64);
|
||||
|
||||
if( channelId.length == 64) {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static void createEncryptedRoomFromInvite( List<String> secretMessageIds, List<Channel> encryptedChannels, Map<String, Tree> tempChildEventsMap, Event eventSecretMessage) {
|
||||
|
||||
String? event140Id = getEncryptedChannelIdFromSecretMessage(secretMessageIds, tempChildEventsMap, eventSecretMessage);
|
||||
Event? event140 = tempChildEventsMap[event140Id]?.event;
|
||||
if( event140 != null) {
|
||||
String eId = event140.eventData.id;
|
||||
|
||||
Set<String> participants = {};
|
||||
event140.eventData.pTags.forEach((element) { participants.add(element);});
|
||||
//print("In createEncryptedRoomFromInvite: processing new enc channel with participants = $participants");
|
||||
|
||||
String chatRoomId = eId;
|
||||
try {
|
||||
dynamic json = jsonDecode(event140.eventData.content);
|
||||
Channel? channel = getChannel(encryptedChannels, chatRoomId);
|
||||
if( channel != null) {
|
||||
// if channel entry already exists, then update its participants info, and name info
|
||||
if( channel.chatRoomName == "" && json.containsKey('name')) {
|
||||
channel.chatRoomName = json['name'];
|
||||
//print("renamed channel to ${channel.chatRoomName}");
|
||||
}
|
||||
if( channel.lastUpdated == 0) { // == 0 only when it was created using a 142 msg. otherwise, don't update it if it was created using 141
|
||||
channel.participants = participants;
|
||||
channel.lastUpdated = event140.eventData.createdAt;
|
||||
}
|
||||
channel.creatorPubkey = event140.eventData.pubkey;
|
||||
|
||||
} else {
|
||||
String roomName = "", roomAbout = "";
|
||||
if( json.containsKey('name') ) {
|
||||
roomName = json['name']??"";
|
||||
}
|
||||
|
||||
if( json.containsKey('about')) {
|
||||
roomAbout = json['about'];
|
||||
}
|
||||
List<String> emptyMessageList = [];
|
||||
Channel room = Channel(chatRoomId, roomName, roomAbout, "", emptyMessageList, participants, event140.eventData.createdAt, event140.eventData.pubkey);
|
||||
//print("created encrypted room with id $chatRoomId and name $roomName");
|
||||
encryptedChannels.add( room);
|
||||
}
|
||||
} on Exception catch(e) {
|
||||
if( gDebug > 0) print("In From Event. Event type 140. Json Decode error for event id ${event140.eventData.id}. error = $e");
|
||||
}
|
||||
} // end if 140
|
||||
else {
|
||||
printWarning("could not find event 140 from event $gSecretMessageKind ${eventSecretMessage.eventData.id}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void handleEncryptedChannelEvent( List<String> secretMessageIds, List<Channel> encryptedChannels, Map<String, Tree> tempChildEventsMap, Event ce) {
|
||||
String eId = ce.eventData.id;
|
||||
int eKind = ce.eventData.kind;
|
||||
|
||||
if( ce.eventData.createdAt < getSecondsDaysAgo(1)) {
|
||||
return; // dont process old 142/141 messages cause they're different format
|
||||
}
|
||||
|
||||
switch(eKind) {
|
||||
case 142:
|
||||
{
|
||||
@ -748,8 +816,8 @@ class Store {
|
||||
channel.addMessageToRoom(eId, tempChildEventsMap);
|
||||
|
||||
} else {
|
||||
Channel newChannel = Channel(channelId, "", "", "", [eId], {}, 0);
|
||||
encryptedChannels.add( newChannel);
|
||||
//Channel newChannel = Channel(channelId, "", "", "", [eId], {}, 0);
|
||||
//encryptedChannels.add( newChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -791,7 +859,7 @@ class Store {
|
||||
Event ?e = tempChildEventsMap[channel.messageIds[i]]?.event;
|
||||
if( e != null) {
|
||||
//print("num directRooms = ${directRooms.length}");
|
||||
e.eventData.translateAndExpand14x(directRooms, encryptedChannels, tempChildEventsMap);
|
||||
e.eventData.translateAndDecrypt14x(secretMessageIds, encryptedChannels, tempChildEventsMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -807,9 +875,9 @@ class Store {
|
||||
roomAbout = json['about'];
|
||||
}
|
||||
List<String> emptyMessageList = [];
|
||||
Channel room = Channel(chatRoomId, roomName, roomAbout, "", emptyMessageList, participants, ce.eventData.createdAt);
|
||||
//Channel room = Channel(chatRoomId, roomName, roomAbout, "", emptyMessageList, participants, ce.eventData.createdAt);
|
||||
//print("created encrypted room with id $chatRoomId and name $roomName");
|
||||
encryptedChannels.add( room);
|
||||
//encryptedChannels.add( room);
|
||||
}
|
||||
} on Exception catch(e) {
|
||||
if( gDebug > 0) print("In From Event. Event type 140. Json Decode error for event id ${ce.eventData.id}. error = $e");
|
||||
@ -817,57 +885,39 @@ class Store {
|
||||
}
|
||||
break;
|
||||
|
||||
case 140:
|
||||
{
|
||||
Set<String> participants = {};
|
||||
ce.eventData.pTags.forEach((element) { participants.add(element);});
|
||||
//print("In handleEncryptedChannelEvents: processing new en channel with participants = $participants");
|
||||
|
||||
String chatRoomId = eId;
|
||||
try {
|
||||
dynamic json = jsonDecode(ce.eventData.content);
|
||||
Channel? channel = getChannel(encryptedChannels, chatRoomId);
|
||||
if( channel != null) {
|
||||
// if channel entry already exists, then update its participants info, and name info
|
||||
if( channel.chatRoomName == "" && json.containsKey('name')) {
|
||||
channel.chatRoomName = json['name'];
|
||||
//print("renamed channel to ${channel.chatRoomName}");
|
||||
}
|
||||
if( channel.lastUpdated == 0) { // == 0 only when it was created using a 142 msg. otherwise, don't update it if it was created using 141
|
||||
channel.participants = participants;
|
||||
channel.lastUpdated = ce.eventData.createdAt;
|
||||
}
|
||||
channel.creatorPubkey = ce.eventData.pubkey;
|
||||
|
||||
} else {
|
||||
String roomName = "", roomAbout = "";
|
||||
if( json.containsKey('name') ) {
|
||||
roomName = json['name']??"";
|
||||
}
|
||||
|
||||
if( json.containsKey('about')) {
|
||||
roomAbout = json['about'];
|
||||
}
|
||||
List<String> emptyMessageList = [];
|
||||
Channel room = Channel(chatRoomId, roomName, roomAbout, "", emptyMessageList, participants, ce.eventData.createdAt, ce.eventData.pubkey);
|
||||
//print("created encrypted room with id $chatRoomId and name $roomName");
|
||||
encryptedChannels.add( room);
|
||||
}
|
||||
} on Exception catch(e) {
|
||||
if( gDebug > 0) print("In From Event. Event type 140. Json Decode error for event id ${ce.eventData.id}. error = $e");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // end switch
|
||||
}
|
||||
|
||||
|
||||
static void handleDirectMessages( List<DirectMessageRoom> directRooms, Map<String, Tree> tempChildEventsMap, Event ce) {
|
||||
// returns 1 if message was to the user; adds the secret message id to tempEncyrp... variable
|
||||
static int handleSecretMessageKind(List<String> tempEncryptedSecretMessageIds, Map<String, Tree> tempChildEventsMap, Event ce) {
|
||||
int eKind = ce.eventData.kind;
|
||||
|
||||
if( gSecretMessageKind != eKind || !isValidDirectMessage(ce.eventData)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for(i = 0; i < tempEncryptedSecretMessageIds.length; i++) {
|
||||
if ( ce.eventData.id == tempEncryptedSecretMessageIds[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tempEncryptedSecretMessageIds.add( ce.eventData.id);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
static int handleDirectMessage( List<DirectMessageRoom> directRooms, Map<String, Tree> tempChildEventsMap, Event ce) {
|
||||
String eId = ce.eventData.id;
|
||||
int eKind = ce.eventData.kind;
|
||||
|
||||
int numMessagesDecrypted = 0;
|
||||
|
||||
if( ce.eventData.id == gCheckEventId) {
|
||||
printInColor("in handleDirectmessge: $gCheckEventId", redColor);
|
||||
}
|
||||
@ -876,7 +926,7 @@ class Store {
|
||||
if( ce.eventData.id == gCheckEventId) {
|
||||
printInColor("in handleDirectmessge: returning", redColor);
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(eKind) {
|
||||
@ -905,7 +955,8 @@ 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}.");
|
||||
}
|
||||
ce.eventData.translateAndExpandMentions(directRooms, tempChildEventsMap);
|
||||
//ce.eventData.translateAndExpandMentions(directRooms, tempChildEventsMap);
|
||||
if( ce.eventData.evaluatedContent.length > 0) numMessagesDecrypted++;
|
||||
} else {
|
||||
if( gDebug > 0) print("Could not get chat room id for event ${ce.eventData.id} sender pubkey = ${ce.eventData.pubkey}.");
|
||||
}
|
||||
@ -914,6 +965,8 @@ class Store {
|
||||
default:
|
||||
break;
|
||||
} // end switch
|
||||
|
||||
return numMessagesDecrypted;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************/
|
||||
@ -923,7 +976,7 @@ class Store {
|
||||
if( events.isEmpty) {
|
||||
List<DirectMessageRoom> temp = [];
|
||||
|
||||
return Store( [], {}, [], [], [], temp);
|
||||
return Store( [], {}, [], [], [], temp, []);
|
||||
}
|
||||
|
||||
// create a map tempChildEventsMap from list of events, key is eventId and value is event itself
|
||||
@ -945,12 +998,16 @@ class Store {
|
||||
List<Channel> encryptedChannels = [];
|
||||
List<DirectMessageRoom> tempDirectRooms = [];
|
||||
Set<String> dummyEventIds = {};
|
||||
List<String> tempEncryptedSecretMessageIds = [];
|
||||
|
||||
int numEventsNotPosts = 0; // just for debugging info
|
||||
int numKind40Events = 0;
|
||||
int numKind42Events = 0;
|
||||
if( gDebug > 0) print("In Tree from Events: after adding all required events of type ${typesInEventMap} to tempChildEventsMap map, its size = ${tempChildEventsMap.length} ");
|
||||
|
||||
|
||||
log.info('in middle of fromEvents');
|
||||
int totoalDirectMessages = 0;
|
||||
tempChildEventsMap.forEach((newEventId, tree) {
|
||||
int eKind = tree.event.eventData.kind;
|
||||
|
||||
@ -961,10 +1018,18 @@ class Store {
|
||||
|
||||
if( eKind == 42 || eKind == 40) {
|
||||
handleChannelEvents(channels, tempChildEventsMap, tree.event);
|
||||
return;
|
||||
}
|
||||
|
||||
if( eKind == 4) {
|
||||
handleDirectMessages(tempDirectRooms, tempChildEventsMap, tree.event);
|
||||
totoalDirectMessages += handleDirectMessage(tempDirectRooms, tempChildEventsMap, tree.event);
|
||||
return;
|
||||
}
|
||||
|
||||
if( eKind == gSecretMessageKind) {
|
||||
// add the event id to given structure if its a valid message
|
||||
handleSecretMessageKind(tempEncryptedSecretMessageIds, tempChildEventsMap, tree.event);
|
||||
return;
|
||||
}
|
||||
|
||||
// if reacted to event is not in store, then add it to dummy list so it can be fetched
|
||||
@ -984,17 +1049,10 @@ class Store {
|
||||
print(e);
|
||||
}
|
||||
|
||||
// only posts, of kind 1, are added to the main tree structure
|
||||
if( eKind != 1) {
|
||||
numEventsNotPosts++;
|
||||
return;
|
||||
}
|
||||
|
||||
if( tree.event.eventData.id == gCheckEventId) {
|
||||
print("In fromEvent: got evnet id $gCheckEventId");
|
||||
}
|
||||
|
||||
|
||||
// find its parent and then add this element to that parent Tree
|
||||
String parentId = tree.event.eventData.getParent(tempChildEventsMap);
|
||||
|
||||
@ -1011,13 +1069,28 @@ class Store {
|
||||
}
|
||||
|
||||
if( tempChildEventsMap[parentId]?.event.eventData.kind != 1) {
|
||||
// first check there isn't already a dummy in top trees
|
||||
bool dummyParentAlreadyExists = false;
|
||||
for( int i = 0; i < topLevelTrees.length; i++) {
|
||||
if( topLevelTrees[i].event.eventData.id == parentId) {
|
||||
dummyParentAlreadyExists = true;
|
||||
topLevelTrees[i].children.add(tree);
|
||||
if( parentId == gCheckEventId) print("7f261931531d1e5c500236725c6cfaea89b7afbe424816d3bfd5d8dfb3ddcec7 already exists as top, as non-kind 1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dummyParentAlreadyExists) {
|
||||
Event dummy = Event("","", EventData(parentId,gDummyAccountPubkey, tree.event.eventData.createdAt, 1, "<Parent is not of Kind 1>", [], [], [], [[]], {}), [""], "[json]");
|
||||
|
||||
Tree dummyTopNode = Tree.withoutStore(dummy, []);
|
||||
dummyTopNode.children.add(tree);
|
||||
tempWithoutParent.add(tree.event.eventData.id);
|
||||
topLevelTrees.add(dummyTopNode);
|
||||
if( parentId == gCheckEventId) print("7f261931531d1e5c500236725c6cfaea89b7afbe424816d3bfd5d8dfb3ddcec7 added as top, and it is not kind 1");
|
||||
} // else is handled in above for loop itself
|
||||
|
||||
tempWithoutParent.add(tree.event.eventData.id);
|
||||
//printWarning("added ${tree.event.eventData.id} as a non kind 1 top tree");
|
||||
// dont add this dummy in dummyEventIds list ( cause that's used to fetch events not in store)
|
||||
} else {
|
||||
tempChildEventsMap[parentId]?.children.add(tree);
|
||||
@ -1028,6 +1101,20 @@ class Store {
|
||||
|
||||
if( parentId.length == 64) {
|
||||
// add the dummy evnets to top level trees, so that their real children get printed too with them so no post is missed by reader
|
||||
|
||||
// first check there isn't already a dummy in top trees
|
||||
bool dummyParentAlreadyExists = false;
|
||||
for( int i = 0; i < topLevelTrees.length; i++) {
|
||||
if( topLevelTrees[i].event.eventData.id == parentId) {
|
||||
dummyParentAlreadyExists = true;
|
||||
topLevelTrees[i].children.add(tree);
|
||||
if( parentId == gCheckEventId) print("7f261931531d1e5c500236725c6cfaea89b7afbe424816d3bfd5d8dfb3ddcec7 already exists as top, as unknown event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dummyParentAlreadyExists) {
|
||||
// kind 1 is needed to enable search etc . the dummy pubkey distinguishes it as a dummy node
|
||||
Event dummy = Event("","", EventData(parentId,gDummyAccountPubkey, tree.event.eventData.createdAt, 1, "Event not loaded", [], [], [], [[]], {}), [""], "[json]");
|
||||
|
||||
Tree dummyTopNode = Tree.withoutStore(dummy, []);
|
||||
@ -1035,6 +1122,9 @@ class Store {
|
||||
tempWithoutParent.add(tree.event.eventData.id);
|
||||
dummyEventIds.add(parentId);
|
||||
topLevelTrees.add(dummyTopNode);
|
||||
if( parentId == gCheckEventId) print("7f261931531d1e5c500236725c6cfaea89b7afbe424816d3bfd5d8dfb3ddcec7 added as top, as unknown event");
|
||||
}
|
||||
//printWarning("Added unknown event as top : ${parentId}");
|
||||
}
|
||||
else {
|
||||
if( gDebug > 0) {
|
||||
@ -1057,10 +1147,24 @@ class Store {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// tempEncrytedSecretMessageIds has been created above
|
||||
// now create encrypted rooms
|
||||
tempEncryptedSecretMessageIds.forEach((secretEventId) {
|
||||
Event? secretEvent = tempChildEventsMap[secretEventId]?.event;
|
||||
|
||||
if( secretEvent != null) {
|
||||
secretEvent.eventData.TranslateAndDecryptSecretMessage(tempChildEventsMap);
|
||||
//printWarning("created enc room");
|
||||
createEncryptedRoomFromInvite(tempEncryptedSecretMessageIds, encryptedChannels, tempChildEventsMap, secretEvent);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
tempChildEventsMap.forEach((newEventId, tree) {
|
||||
int eKind = tree.event.eventData.kind;
|
||||
if( eKind == 142 || eKind == 140 || eKind == 141) {
|
||||
handleEncryptedChannelEvents(tempDirectRooms, encryptedChannels, tempChildEventsMap, tree.event);
|
||||
if( eKind == 142 || eKind == 141) {
|
||||
handleEncryptedChannelEvent(tempEncryptedSecretMessageIds, encryptedChannels, tempChildEventsMap, tree.event);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1073,18 +1177,17 @@ class Store {
|
||||
|
||||
//log.info("In fromEvents bfore calling SendEventsRequest for ${dummyEventIds.length} dummy evnets");
|
||||
|
||||
if(gDebug != 0) print("In Tree FromEvents: number of events in map which are not kind 1 = ${numEventsNotPosts}");
|
||||
if(gDebug != 0) print("In Tree FromEvents: number of events in map of kind 40 = ${numKind40Events}");
|
||||
if(gDebug != 0) print("In Tree FromEvents: number of events in map of kind 42 = ${numKind42Events}");
|
||||
if(gDebug != 0) print("In Tree FromEvents: number of events without parent in fromEvents = ${tempWithoutParent.length}");
|
||||
|
||||
log.info("total direct messages: $totoalDirectMessages");
|
||||
|
||||
// get dummy events
|
||||
sendEventsRequest(gListRelayUrls1, dummyEventIds);
|
||||
|
||||
//log.info("In fromEvents After calling SendEventsRequest for ${dummyEventIds.length} dummy evnets ids: $dummyEventIds");
|
||||
|
||||
// create a dummy top level tree and then create the main Tree object
|
||||
return Store( topLevelTrees, tempChildEventsMap, tempWithoutParent, channels, encryptedChannels, tempDirectRooms);
|
||||
return Store( topLevelTrees, tempChildEventsMap, tempWithoutParent, channels, encryptedChannels, tempDirectRooms, tempEncryptedSecretMessageIds);
|
||||
} // end fromEvents()
|
||||
|
||||
/***********************************************************************************************************************************/
|
||||
@ -1142,7 +1245,7 @@ class Store {
|
||||
|
||||
// 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)
|
||||
newEvent.eventData.translateAndExpandMentions(directRooms, allChildEventsMap); // this also handles dm decryption for kind 4 messages, for kind 1 will do translation/expansion;
|
||||
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
|
||||
@ -1151,9 +1254,9 @@ class Store {
|
||||
Tree tree = topPosts[i];
|
||||
if( tree.event.eventData.id == newEvent.eventData.id) {
|
||||
// its a replacement.
|
||||
if( gDebug > 0) log.info("In processIncoming: Replaced old dummy event of id: ${newEvent.eventData.id}");
|
||||
if( gDebug >= 0 && newEvent.eventData.id == gCheckEventId) log.info("In processIncoming: Replaced old dummy event of id: ${newEvent.eventData.id}");
|
||||
tree.event = newEvent;
|
||||
tree = topPosts.removeAt(i);
|
||||
//tree = topPosts.removeAt(i);
|
||||
allChildEventsMap[tree.event.eventData.id] = tree;
|
||||
if( newEvent.eventData.createdAt > getSecondsDaysAgo(gDontHighlightEventsOlderThan)) {
|
||||
newEventIdsSet.add(newEvent.eventData.id);
|
||||
@ -1243,37 +1346,27 @@ class Store {
|
||||
}
|
||||
break;
|
||||
|
||||
case 140:
|
||||
case 141:
|
||||
//print("calling handleEncryptedChannelEvents for kind ${newTree.event.eventData.kind} from processIncoming");
|
||||
handleEncryptedChannelEvents(directRooms, encryptedChannels, allChildEventsMap, newTree.event);
|
||||
break;
|
||||
|
||||
case 142:
|
||||
|
||||
newTree.event.eventData.isNotification = true; // highlight it too in next printing
|
||||
// add 142 chat message event id to its chat room
|
||||
String channelId = newTree.event.eventData.getChannelIdForMessage();
|
||||
if( channelId != "") {
|
||||
Channel? channel = getChannel(encryptedChannels, channelId);
|
||||
if( channel != null) {
|
||||
if( gDebug > 0) print("added event to encrypted chat room in insert event");
|
||||
channel.addMessageToRoom(newTree.event.eventData.id, allChildEventsMap); // adds in order
|
||||
newTree.event.eventData.translateAndExpand14x(directRooms, encryptedChannels, allChildEventsMap);
|
||||
//print("calling handleEncryptedChannelEvents for kind ${newTree.event.eventData.kind} from processIncoming");
|
||||
handleEncryptedChannelEvent(encryptedGroupSecretIds, encryptedChannels, allChildEventsMap, newTree.event);
|
||||
break;
|
||||
|
||||
case gSecretMessageKind:
|
||||
if( isValidDirectMessage(newTree.event.eventData)) {
|
||||
//printWarning("1. decrypting secret message with id: ${newTree.event.eventData.id}");
|
||||
String ? temp = newTree.event.eventData.TranslateAndDecryptSecretMessage( allChildEventsMap);
|
||||
if( temp != null) {
|
||||
//printWarning("added to the secretMesssageIds");
|
||||
createEncryptedRoomFromInvite(encryptedGroupSecretIds, encryptedChannels, allChildEventsMap, newTree.event);
|
||||
}
|
||||
} else {
|
||||
Set<String> participants = {};
|
||||
newTree.event.eventData.pTags.forEach((element) {participants.add(element);});
|
||||
Channel newChannel = Channel(channelId, "", "", "", [], participants, 0);
|
||||
newChannel.addMessageToRoom(newTree.event.eventData.id, allChildEventsMap);
|
||||
encryptedChannels.add(newChannel);
|
||||
newTree.event.eventData.translateAndExpand14x(directRooms, encryptedChannels, allChildEventsMap);
|
||||
}
|
||||
//print("1. kind $gSecretMessageKind with id ${newTree.event.eventData.id} is not a valid direct message to user. ");
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1479,10 +1572,9 @@ class Store {
|
||||
}
|
||||
|
||||
/**
|
||||
* @printAllChennelsInfo Print one line information about all channels, which are type 40 events ( class ChatRoom)
|
||||
* @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
|
||||
*/
|
||||
int printChannelsOverview(List<Channel> channelstoPrint, int numRoomsOverview, fRoomSelector selector) {
|
||||
|
||||
int printChannelsOverview(List<Channel> channelstoPrint, int numRoomsOverview, fRoomSelector selector, var tempChildEventsMap , List<String>? secretMessageIds) {
|
||||
|
||||
channelstoPrint.sort(scrollableCompareTo);
|
||||
int numChannelsActuallyPrinted = 0;
|
||||
@ -1521,7 +1613,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()}");
|
||||
stdout.write("${e.eventData.getAsLine(tempChildEventsMap, secretMessageIds, channelstoPrint)}");
|
||||
break; // print only one event, the latest one
|
||||
}
|
||||
}
|
||||
@ -1536,13 +1628,13 @@ class Store {
|
||||
return numChannelsActuallyPrinted;
|
||||
}
|
||||
|
||||
void printChannel(Channel room, [int page = 1]) {
|
||||
void printChannel(Channel room, Map<String, Tree>? tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels, [int page = 1]) {
|
||||
if( page < 1) {
|
||||
if( gDebug > 0) log.info("In printChannel got page = $page");
|
||||
page = 1;
|
||||
}
|
||||
|
||||
room.printOnePage(allChildEventsMap, page);
|
||||
room.printOnePage(allChildEventsMap, secretMessageIds, encryptedChannels, page);
|
||||
}
|
||||
|
||||
// prints some info about the encrypted channel
|
||||
@ -1569,9 +1661,10 @@ class Store {
|
||||
|
||||
}
|
||||
|
||||
// works for both 4x and 14x channels
|
||||
// shows the given channelId, where channelId is prefix-id or channel name as mentioned in room.name. returns full id of channel.
|
||||
// looks for channelId in id first, then in names.
|
||||
String showChannel(List<Channel> listChannels, String channelId, [int page = 1]) {
|
||||
String showChannel(List<Channel> listChannels, String channelId, Map<String, Tree>? tempChildEventsMap, List<String>? secretMessageIds, List<Channel>? encryptedChannels, [int page = 1]) {
|
||||
if( channelId.length > 64 ) {
|
||||
return "";
|
||||
}
|
||||
@ -1610,13 +1703,10 @@ class Store {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
printEncryptedChannelInfo(room);
|
||||
|
||||
|
||||
stdout.write("\n\n");
|
||||
}
|
||||
printChannel(room, page);
|
||||
printChannel(room, tempChildEventsMap, secretMessageIds, encryptedChannels, page);
|
||||
}
|
||||
return fullChannelId.first;
|
||||
} else {
|
||||
@ -1638,7 +1728,7 @@ class Store {
|
||||
/**
|
||||
* @printDirectRoomInfo Print one line information about chat rooms
|
||||
*/
|
||||
int printDirectRoomInfo(fRoomSelector roomSelector) {
|
||||
int printDirectRoomInfo(fRoomSelector roomSelector, var tempChildEventsMap) {
|
||||
directRooms.sort(scrollableCompareTo);
|
||||
|
||||
int numNotificationRooms = 0;
|
||||
@ -1674,7 +1764,7 @@ class Store {
|
||||
numRoomsActuallyPrinted++;
|
||||
Event? e = allChildEventsMap[messageIds[i]]?.event;
|
||||
if( e!= null) {
|
||||
String line = e.eventData.getAsLine();
|
||||
String line = e.eventData.getAsLine(tempChildEventsMap, null, null);
|
||||
stdout.write(line);
|
||||
break; // print only one event, the latest one
|
||||
}
|
||||
@ -2280,7 +2370,7 @@ Store getTree(Set<Event> events) {
|
||||
if(gDebug > 0) log.info("Warning: In printEventsAsTree: events length = 0");
|
||||
|
||||
List<DirectMessageRoom> temp =[];
|
||||
return Store([], {}, [], [], [], temp);
|
||||
return Store([], {}, [], [], [], temp, []);
|
||||
}
|
||||
|
||||
// remove posts older than 20 days or so
|
||||
@ -2310,13 +2400,19 @@ Store getTree(Set<Event> events) {
|
||||
if( gDebug > 0) log.info("Calling fromEvents for ${events.length} events.");
|
||||
|
||||
// create tree from events
|
||||
//log.info("Before calling fromEvents for ${events.length} events");
|
||||
log.info("Before calling fromEvents for ${events.length} events");
|
||||
Store node = Store.fromEvents(events);
|
||||
//log.info("After calling fromEvents with ${node.allChildEventsMap.length} events in its internal store");
|
||||
log.info("After calling fromEvents with ${node.allChildEventsMap.length} events in its internal store");
|
||||
|
||||
// translate and expand mentions for all ( both take 0.5 sec for 20k events)
|
||||
events.where((element) => element.eventData.kind != 142).forEach( (event) => event.eventData.translateAndExpandMentions(node.directRooms, node.allChildEventsMap));;
|
||||
events.where((element) => element.eventData.kind == 142).forEach( (event) => event.eventData.translateAndExpand14x(node.directRooms, node.encryptedChannels, node.allChildEventsMap));;
|
||||
log.info('before calling expandmentions');
|
||||
events.where((element) => [1, 42, gSecretMessageKind].contains(element.eventData.kind)).forEach( (event) => event.eventData.translateAndExpandMentions( node.allChildEventsMap));;
|
||||
|
||||
|
||||
|
||||
log.info('between calling expandmentions');
|
||||
events.where((element) => element.eventData.kind == 142).forEach( (event) => event.eventData.translateAndDecrypt14x(node.encryptedGroupSecretIds, node.encryptedChannels, node.allChildEventsMap));;
|
||||
log.info('after calling expandmentions');
|
||||
if( gDebug > 0) log.info("expand mentions finished.");
|
||||
|
||||
if(gDebug > 0) print("total number of posts/replies in main tree = ${node.count()}");
|
||||
|
@ -1,11 +1,10 @@
|
||||
name: nostr_console
|
||||
description: A multi-platform nostr client built for terminal/console.
|
||||
version: 0.1.8-beta
|
||||
version: 0.1.9-beta
|
||||
homepage: https://github.com/vishalxl/nostr_console
|
||||
|
||||
# Release 0.1.8-beta
|
||||
# menu changed
|
||||
# build
|
||||
# Release 0.1.9-beta
|
||||
# used kind 104
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.3 <3.0.0'
|
||||
|
@ -10,7 +10,7 @@ EventData exampleEdataChild = EventData("id2", "pubkey", 1111111, 1, "content ch
|
||||
Event exampleEvent = Event('event', 'id3', exampleEdata, ['relay name'], "[json]");
|
||||
Event exampleEventChild = Event('event', 'id4', exampleEdataChild, ['relay name'], "[json]");
|
||||
|
||||
Store exampleStore = Store([], {}, [], [], [], []);
|
||||
Store exampleStore = Store([], {}, [], [], [], [], []);
|
||||
Tree exampleTree = Tree.withoutStore(exampleEvent, []);
|
||||
|
||||
//bool skipTest = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user