mirror of
https://github.com/vishalxl/nostr_console.git
synced 2025-03-18 22:02:20 +01:00
527 lines
13 KiB
Dart
527 lines
13 KiB
Dart
import 'dart:io';
|
|
import 'package:qr/qr.dart';
|
|
|
|
enum enumRoomType { kind4, kind40, kind140, RoomLocationTag, RoomTTag}
|
|
|
|
int gMinLnInvoiceLength = 20; // TODO put real value
|
|
int gMaxStrLenForQrCode = 600; // in bytes, maximum acceptable length of string that is converted to qr code. for lnbc1 invoices
|
|
|
|
String getPostKindFrom(enumRoomType eType) {
|
|
switch (eType) {
|
|
case enumRoomType.kind4:
|
|
return "4";
|
|
case enumRoomType.kind40:
|
|
return "42";
|
|
case enumRoomType.kind140:
|
|
return "142";
|
|
case enumRoomType.RoomLocationTag:
|
|
return "1";
|
|
case enumRoomType.RoomTTag:
|
|
return "1";
|
|
}
|
|
}
|
|
|
|
Set<String>? getTagsFromContent(String content) {
|
|
Set<String>? tags;
|
|
|
|
String regexp1 = '(#[a-zA-Z0-9_-]+ )|(#[a-zA-Z0-9_-]+)\$';
|
|
RegExp httpRegExp = RegExp(regexp1);
|
|
|
|
for( var match in httpRegExp.allMatches(content) ) {
|
|
tags ??= {};
|
|
|
|
tags.add( content.substring(match.start + 1, match.end).trim() );
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
|
|
class HistogramEntry {
|
|
String str;
|
|
int count;
|
|
HistogramEntry(this.str, this.count);
|
|
static int histogramSorter(HistogramEntry a, HistogramEntry b) {
|
|
if( a.count < b.count ) {
|
|
return 1;
|
|
} if( a.count == b.count ) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> myWait(int ms) async {
|
|
Future<void> foo1() async {
|
|
await Future.delayed(Duration(milliseconds: ms));
|
|
return;
|
|
}
|
|
await foo1();
|
|
}
|
|
|
|
bool isNumeric(String s) {
|
|
return double.tryParse(s) != null;
|
|
}
|
|
|
|
bool isWordSeparater(String s) {
|
|
if( s.length != 1) {
|
|
return false;
|
|
}
|
|
return s[0] == ' ' || s[0] == '\n' || s[0] == '\r' || s[0] == '\t'
|
|
|| s[0] == ',' || s[0] == '.' || s[0] == '-' || s[0] == '('|| s[0] == ')';
|
|
}
|
|
|
|
|
|
bool isWhitespace(String s) {
|
|
if( s.length != 1) {
|
|
return false;
|
|
}
|
|
return s[0] == ' ' || s[0] == '\n' || s[0] == '\r' || s[0] == '\t';
|
|
}
|
|
|
|
extension StringX on String {
|
|
|
|
int isChannelPageNumber(int max) {
|
|
|
|
if(length < 2 || this[0] != '/') {
|
|
return 0;
|
|
}
|
|
|
|
String rest = substring(1);
|
|
|
|
//print("rest = $rest");
|
|
int? n = int.tryParse(rest);
|
|
if( n != null) {
|
|
if( n < max) {
|
|
return n;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
isEnglish( ) {
|
|
// since smaller words can be smileys they should not be translated
|
|
if( length < 10) {
|
|
return true;
|
|
}
|
|
|
|
if( !isLatinAlphabet()) {
|
|
return false;
|
|
}
|
|
|
|
if (isRomanceLanguage()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
isPortugese() {
|
|
false; // https://1000mostcommonwords.com/1000-most-common-portuguese-words/
|
|
}
|
|
|
|
bool isRomanceLanguage() {
|
|
|
|
// https://www.thoughtco.com/most-common-french-words-1372759
|
|
Set<String> frenchWords = {"oui", "je", "le", "un", "de", "merci", "une", "ce", "pas"}; // "et" is in 'et al'
|
|
Set<String> spanishWords = {"y", "se", "el", "uso", "que", "te", "los", "va", "ser", "si", "por", "lo", "es", "era", "un", "o"};
|
|
Set<String> portugeseWords = {"como", "seu", "que", "ele", "foi", "eles", "tem", "este", "por", "quente", "vai",
|
|
"ter", "mas", "ou", "teve", "fora", "é", "te", "mais"};
|
|
|
|
Set<String> romanceWords = frenchWords.union(spanishWords).union(portugeseWords);
|
|
for( String word in romanceWords) {
|
|
if( toLowerCase().contains(" $word ")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
isLatinAlphabet({caseSensitive = false}) {
|
|
int countLatinletters = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
final target = caseSensitive ? this[i] : this[i].toLowerCase();
|
|
if ( (target.codeUnitAt(0) > 96 && target.codeUnitAt(0) < 123) || ( isNumeric(target) ) || isWhitespace(target)) {
|
|
countLatinletters++;
|
|
}
|
|
}
|
|
|
|
if( countLatinletters < ( 40.0/100 ) * length ) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isValidHexPubkey(String pubkey) {
|
|
if( pubkey.length == 64) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
String myPadRight(String str, int width) {
|
|
String newStr = "";
|
|
|
|
if( str.length < width) {
|
|
newStr = str.padRight(width);
|
|
} else {
|
|
newStr = str.substring(0, width);
|
|
}
|
|
return newStr;
|
|
}
|
|
|
|
// returns tags as string that can be used to calculate event has. called from EventData constructor
|
|
String getStrTagsFromJson(dynamic json) {
|
|
String str = "";
|
|
|
|
int i = 0;
|
|
for( dynamic tag in json ) {
|
|
if( i != 0) {
|
|
str += ",";
|
|
}
|
|
|
|
str += "[";
|
|
int j = 0;
|
|
for(dynamic element in tag) {
|
|
if( j != 0) {
|
|
str += ",";
|
|
}
|
|
str += "\"${element.toString()}\"";
|
|
j++;
|
|
}
|
|
str += "]";
|
|
i++;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
String addEscapeChars(String str) {
|
|
String temp = "";
|
|
//temp = temp.replaceAll("\\", "\\\\");
|
|
temp = str.replaceAll("\"", "\\\"");
|
|
return temp.replaceAll("\n", "\\n");
|
|
}
|
|
|
|
String unEscapeChars(String str) {
|
|
String temp = str.replaceAll("\"", "\\\"");
|
|
temp = temp.replaceAll("\n", "\\n");
|
|
return temp;
|
|
}
|
|
|
|
void printUnderlined(String x) { stdout.write("$x\n${getNumDashes(x.length)}\n");}
|
|
|
|
String getNumSpaces(int num) {
|
|
String s = "";
|
|
for( int i = 0; i < num; i++) {
|
|
s += " ";
|
|
}
|
|
return s;
|
|
}
|
|
|
|
String getNumDashes(int num, [String dashType = "-"]) {
|
|
String s = "";
|
|
for( int i = 0; i < num; i++) {
|
|
s += dashType;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
List<List<int>> getUrlRanges(String s) {
|
|
List<List<int>> urlRanges = [];
|
|
String regexp1 = "http[s]*://[a-zA-Z0-9]+([.a-zA-Z0-9/_\\-\\#\\+=\\&\\?]*)";
|
|
|
|
RegExp httpRegExp = RegExp(regexp1);
|
|
for( var match in httpRegExp.allMatches(s) ) {
|
|
List<int> entry = [match.start, match.end];
|
|
urlRanges.add(entry);
|
|
}
|
|
|
|
return urlRanges;
|
|
}
|
|
|
|
// returns true if n is in any of the ranges given in list
|
|
int isInRange( int n, List<List<int>> ranges ) {
|
|
for( int i = 0; i < ranges.length; i++) {
|
|
if( n >= ranges[i][0] && n < ranges[i][1]) {
|
|
return ranges[i][1];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// https://jpgraph.net/download/manuals/chunkhtml/ch27.html
|
|
// both go from 1 to 20 inclusive. index is type.
|
|
List<int> qrMaxDataBits = [152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888];
|
|
List<int> qrModules = [21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97];
|
|
|
|
// return type and module as entries in a list
|
|
List<int>? getTypeAndModule(String str) {
|
|
if( qrMaxDataBits.length != qrModules.length) {
|
|
return null;
|
|
}
|
|
|
|
// 5 for padding which it seems to need, otherwise it gives error like 'QrInputTooLongException: Input too long. 2212 > 2192' for a str which is exactly 2192
|
|
int strLen = str.length + 5;
|
|
for( int i = 0; i < qrModules.length; i++) {
|
|
if( strLen * 8 <= qrMaxDataBits[i]) {
|
|
return [i+1, qrModules[i]];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
bool sanityChecked(String lnInvoice) {
|
|
|
|
if( lnInvoice.length < gMinLnInvoiceLength) {
|
|
return false;
|
|
}
|
|
|
|
if( lnInvoice.substring(0, 4).toLowerCase() != "lnbc") {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
String expandLNInvoices(String content) {
|
|
|
|
String regexp1 = '(lnbc[a-zA-Z0-9]+)';
|
|
RegExp httpRegExp = RegExp(regexp1);
|
|
|
|
for( var match in httpRegExp.allMatches(content.toLowerCase()) ) {
|
|
String lnInvoice = content.substring(match.start, match.end);
|
|
|
|
if( !sanityChecked(lnInvoice)) {
|
|
continue;
|
|
}
|
|
|
|
if( lnInvoice.length > gMaxStrLenForQrCode) {
|
|
continue;
|
|
}
|
|
|
|
String qrStr = "";
|
|
|
|
List<int>? typeAndModule = getTypeAndModule(lnInvoice);
|
|
if( typeAndModule == null) {
|
|
continue;
|
|
}
|
|
|
|
qrStr = getPubkeyAsQrString(lnInvoice, typeAndModule[0], typeAndModule[1], "");
|
|
content = "${content.substring(0, match.start)}:-\n\n$qrStr\n\n${content.substring(match.end)}";
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
// https://www.sproutqr.com/blog/qr-code-types
|
|
// https://jpgraph.net/download/manuals/chunkhtml/ch27.html
|
|
// default 4 and 33 work for pubkey
|
|
String getPubkeyAsQrString(String str, [int typeNumber = 4, moduleCount = 33, String leftPadding = " "]) {
|
|
String output = "";
|
|
|
|
final qrCode = QrCode(typeNumber, QrErrorCorrectLevel.L)
|
|
..addData(str);
|
|
final qrImage = QrImage(qrCode);
|
|
|
|
assert( qrImage.moduleCount == moduleCount);
|
|
var x = 0;
|
|
for (x = 0; x < qrImage.moduleCount -1 ; x += 2) {
|
|
output += leftPadding;
|
|
for (var y = 0; y < qrImage.moduleCount ; y++) {
|
|
|
|
bool topDark = qrImage.isDark(y, x);
|
|
bool bottomDark = qrImage.isDark(y, x + 1);
|
|
if (topDark && bottomDark) {
|
|
output += "█";
|
|
}
|
|
else if (topDark ) {
|
|
output += "▀";
|
|
} else if ( bottomDark) {
|
|
output += "▄";
|
|
} else if( !topDark && !bottomDark) {
|
|
output += " ";
|
|
}
|
|
}
|
|
output += "\n";
|
|
}
|
|
|
|
if( qrImage.moduleCount %2 == 1) {
|
|
output += leftPadding;
|
|
for (var y = 0; y < qrImage.moduleCount ; y++) {
|
|
bool dark = qrImage.isDark(y, x);
|
|
if (dark ) {
|
|
output += "▀";
|
|
} else {
|
|
output += " ";
|
|
}
|
|
|
|
}
|
|
output += "\n";
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
void clearScreen() {
|
|
print("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
|
|
}
|
|
|
|
// returns a string entered by the user
|
|
String getStringFromUser(String prompt, [String defaultValue=""] ) {
|
|
String str = "";
|
|
|
|
stdout.write(prompt);
|
|
str = (stdin.readLineSync())??"";
|
|
|
|
if( str.isEmpty) {
|
|
str = defaultValue;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// relay related functions
|
|
|
|
// returns list in form ( if 3 sized list)
|
|
// "pubkey1","pubkey2","pubkey3"
|
|
String getCommaSeparatedQuotedStrs(Set<String> publicKeys) {
|
|
String s = "";
|
|
int i = 0;
|
|
for(String pubkey in publicKeys) {
|
|
s += "\"${pubkey.toLowerCase()}\"";
|
|
if( i < publicKeys.length - 1) {
|
|
s += ",";
|
|
}
|
|
i++;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
String getCommaSeparatedInts(Set<int>? kind) {
|
|
if( kind == null) {
|
|
return "";
|
|
}
|
|
|
|
if( kind.isEmpty) {
|
|
return "";
|
|
}
|
|
|
|
String strKind = "";
|
|
int i = 0;
|
|
|
|
for (var k in kind) {
|
|
String comma = ",";
|
|
if( i == kind.length-1) {
|
|
comma = "";
|
|
}
|
|
strKind = strKind + k.toString() + comma;
|
|
i++;
|
|
}
|
|
|
|
return strKind;
|
|
}
|
|
|
|
String getKindRequest(String subscriptionId, List<int> kind, int limit, int sinceWhen) {
|
|
String strTime = "";
|
|
if( sinceWhen != 0) {
|
|
strTime = ', "since":${sinceWhen.toString()}';
|
|
}
|
|
var strSubscription1 = '["REQ","$subscriptionId",{"kinds":[';
|
|
var strSubscription2 ='], "limit":$limit$strTime } ]';
|
|
|
|
String strKind = getCommaSeparatedInts(kind.toSet());
|
|
|
|
String strRequest = strSubscription1 + strKind + strSubscription2;
|
|
return strRequest;
|
|
}
|
|
|
|
String getUserRequest(String subscriptionId, String publicKey, int numUserEvents, int sinceWhen, [Set<int>? kind]) {
|
|
Set<int> kind = {};
|
|
kind = kind;
|
|
|
|
String strKind = getCommaSeparatedInts(kind);
|
|
|
|
String strKindSection = "";
|
|
if( strKind.isNotEmpty) {
|
|
strKindSection = '"kinds":[$strKind],';
|
|
}
|
|
|
|
String strTime = "";
|
|
if( sinceWhen != 0) {
|
|
strTime = ', "since": ${sinceWhen.toString()}';
|
|
}
|
|
var strSubscription1 = '["REQ","$subscriptionId",{ "authors": ["';
|
|
var strSubscription2 ='"],$strKindSection"limit": $numUserEvents $strTime } ]';
|
|
String request = strSubscription1 + publicKey.toLowerCase() + strSubscription2;
|
|
return request;
|
|
}
|
|
|
|
String getMentionRequest(String subscriptionId, Set<String> ids, int numUserEvents, int sinceWhen, String tagToGet) {
|
|
String strTime = "";
|
|
if( sinceWhen != 0) {
|
|
strTime = ', "since": ${sinceWhen.toString()}';
|
|
}
|
|
var strSubscription1 = '["REQ","$subscriptionId",{ "$tagToGet": [';
|
|
var strSubscription2 ='], "limit": $numUserEvents $strTime } ]';
|
|
return strSubscription1 + getCommaSeparatedQuotedStrs(ids) + strSubscription2;
|
|
}
|
|
|
|
String getIdAndMentionRequest(String subscriptionId, Set<String> ids, int numUserEvents, int idSinceWhen, int mentionSinceWhen, String tagToGet, String idString) {
|
|
String idStrTime = "", mentionStrTime = "";
|
|
if( idSinceWhen != 0) {
|
|
idStrTime = ', "since": ${idSinceWhen.toString()}';
|
|
}
|
|
|
|
if( mentionSinceWhen != 0) {
|
|
mentionStrTime = ', "since": ${mentionSinceWhen.toString()}';
|
|
}
|
|
|
|
var strSubscription1 = '["REQ","$subscriptionId",{ "$tagToGet": [';
|
|
var strSubscription2 ='], "limit": $numUserEvents $idStrTime } ]';
|
|
String req = '["REQ","$subscriptionId",{ "$tagToGet": [${getCommaSeparatedQuotedStrs(ids)}], "limit": $numUserEvents $mentionStrTime},{"$idString":[${getCommaSeparatedQuotedStrs(ids)}]$idStrTime}]';
|
|
return req;
|
|
}
|
|
|
|
|
|
String getMultiUserRequest(String subscriptionId, Set<String> publicKeys, int numUserEvents, int sinceWhen, [Set<int>? kind]) {
|
|
String strTime = "";
|
|
if( sinceWhen != 0) {
|
|
strTime = ', "since": ${sinceWhen.toString()}';
|
|
}
|
|
|
|
String strKind = getCommaSeparatedInts(kind);
|
|
|
|
String strKindSection = "";
|
|
if( strKind.isNotEmpty) {
|
|
strKindSection = '"kinds":[$strKind],';
|
|
}
|
|
|
|
var strSubscription1 = '["REQ","$subscriptionId",{ "authors": [';
|
|
var strSubscription2 ='],$strKindSection"limit": $numUserEvents $strTime } ]';
|
|
String s = "";
|
|
s = getCommaSeparatedQuotedStrs(publicKeys);
|
|
String request = strSubscription1 + s + strSubscription2;
|
|
return request;
|
|
}
|
|
|
|
// ends with a newline
|
|
void printSet( Set<String> toPrint, [ String prefix = "", String separator = ""]) {
|
|
stdout.write(prefix);
|
|
|
|
int i = 0;
|
|
for (var element in toPrint) {
|
|
if( i != 0) {
|
|
stdout.write(separator);
|
|
}
|
|
|
|
stdout.write(element);
|
|
i++;
|
|
}
|
|
stdout.write("\n");
|
|
}
|
|
|
|
|