Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kfrancis committed Jun 3, 2023
1 parent bff1912 commit fcc53b5
Show file tree
Hide file tree
Showing 14 changed files with 792 additions and 264 deletions.
23 changes: 11 additions & 12 deletions All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C70613A8
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NostrClient.Helpers.Tests", "tests\NostrClient.Helpers.Tests\NostrClient.Helpers.Tests.csproj", "{FCF36045-186D-4F3C-9437-C37730D728EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuSocial.AvaUI", "src\NuSocial.AvaUI\NuSocial.AvaUI\NuSocial.AvaUI.csproj", "{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuSocial.AvaUI", "src\NuSocial.AvaUI\NuSocial.AvaUI\NuSocial.AvaUI.csproj", "{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuSocial.AvaUI.Android", "src\NuSocial.AvaUI\NuSocial.AvaUI.Android\NuSocial.AvaUI.Android.csproj", "{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuSocial.AvaUI.Android", "src\NuSocial.AvaUI\NuSocial.AvaUI.Android\NuSocial.AvaUI.Android.csproj", "{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuSocial.AvaUI.Desktop", "src\NuSocial.AvaUI\NuSocial.AvaUI.Desktop\NuSocial.AvaUI.Desktop.csproj", "{048F76D5-68DE-4DDF-A769-A2E614FA7B10}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuSocial.AvaUI.Desktop", "src\NuSocial.AvaUI\NuSocial.AvaUI.Desktop\NuSocial.AvaUI.Desktop.csproj", "{048F76D5-68DE-4DDF-A769-A2E614FA7B10}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuSocial.AvaUI.iOS", "src\NuSocial.AvaUI\NuSocial.AvaUI.iOS\NuSocial.AvaUI.iOS.csproj", "{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuSocial.AvaUI.iOS", "src\NuSocial.AvaUI\NuSocial.AvaUI.iOS\NuSocial.AvaUI.iOS.csproj", "{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuSocial.AvaUI.Web", "src\NuSocial.AvaUI\NuSocial.AvaUI.Web\NuSocial.AvaUI.Web.csproj", "{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuSocial.AvaUI.Web", "src\NuSocial.AvaUI\NuSocial.AvaUI.Web\NuSocial.AvaUI.Web.csproj", "{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{6A2F610C-972D-42D2-84B0-408305015199}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NostrConsoleClient", "src\NostrConsoleClient\NostrConsoleClient.csproj", "{E033033D-9A4A-4E3D-A9BE-335D12F783BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -67,29 +69,26 @@ Global
{FCF36045-186D-4F3C-9437-C37730D728EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCF36045-186D-4F3C-9437-C37730D728EE}.Release|Any CPU.Build.0 = Release|Any CPU
{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2C2ED19-A5ED-44EC-99C9-B8C510122D08}.Release|Any CPU.Build.0 = Release|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Release|Any CPU.Build.0 = Release|Any CPU
{6A9A7D31-ED17-45B0-B915-3D0EBF28081F}.Release|Any CPU.Deploy.0 = Release|Any CPU
{048F76D5-68DE-4DDF-A769-A2E614FA7B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{048F76D5-68DE-4DDF-A769-A2E614FA7B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{048F76D5-68DE-4DDF-A769-A2E614FA7B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{048F76D5-68DE-4DDF-A769-A2E614FA7B10}.Release|Any CPU.Build.0 = Release|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Release|Any CPU.Build.0 = Release|Any CPU
{F370C701-87D0-4140-BFB4-59AE9C8ABD9F}.Release|Any CPU.Deploy.0 = Release|Any CPU
{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5052D4F2-F125-4A1B-80A1-E0FE91FCCF71}.Release|Any CPU.Build.0 = Release|Any CPU
{E033033D-9A4A-4E3D-A9BE-335D12F783BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E033033D-9A4A-4E3D-A9BE-335D12F783BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E033033D-9A4A-4E3D-A9BE-335D12F783BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E033033D-9A4A-4E3D-A9BE-335D12F783BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
18 changes: 18 additions & 0 deletions src/NostrConsoleClient/NostrConsoleClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Nostr.Client" Version="1.4.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>

</Project>
116 changes: 116 additions & 0 deletions src/NostrConsoleClient/NostrViewer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using Newtonsoft.Json;
using Nostr.Client.Client;
using Nostr.Client.Messages.Contacts;
using Nostr.Client.Messages.Direct;
using Nostr.Client.Messages.Metadata;
using Nostr.Client.Messages;
using Nostr.Client.Requests;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;

namespace NostrConsoleClient;

internal class NostrViewer
{
private readonly INostrClient _client;

public NostrViewer(INostrClient client)
{
_client = client;
}

public void Subscribe()
{
var events = _client.Streams.EventStream
.Where(x => x.Event != null);

events.Subscribe(x =>
Log.Information("[{relay}] {kind}: {content}", x.CommunicatorName, x.Event?.Kind, x.Event?.Content));

events
.Select(x => x.Event!)
.OfType<NostrMetadataEvent>()
.Subscribe(x =>
Log.Information("Name: {name}, about: {about}", x.Metadata?.Name, x.Metadata?.About));

events
.Select(x => x.Event!)
.OfType<NostrContactEvent>()
.Subscribe(x =>
{
foreach (var relay in x.Relays)
{
Log.Information("Relay: {url}, write: {about}, read: {read}", relay.Key, relay.Value.Write,
relay.Value.Read);
}
});

events
.Select(x => x.Event!)
.OfType<NostrEncryptedEvent>()
.Subscribe(x =>
{
Log.Information("DM message: {content}, from {from} to {to}", x.EncryptedContent, x.Pubkey?[..4],
x.RecipientPubkey?[..4]);
});

_client.Streams.NoticeStream.Subscribe(x => Log.Information("[{relay}] Notice: {message}", x.CommunicatorName, x.Message));
_client.Streams.EoseStream.Subscribe(x => Log.Information("[{relay}] EOSE of subscription {subscription}", x.CommunicatorName, x.Subscription));
_client.Streams.OkStream.Subscribe(x => Log.Information("[{relay}] OK {subscription} success: {success} {message}", x.CommunicatorName, x.EventId, x.Accepted, x.Message));
_client.Streams.UnknownMessageStream.Subscribe(x => Log.Information("[{relay}] Unknown {messageType} message, data: {data}", x.CommunicatorName, x.MessageType, JsonConvert.SerializeObject(x.AdditionalData)));
_client.Streams.UnknownRawStream.Subscribe(x => Log.Warning("[{relay}] Unknown data: {data}", x.CommunicatorName, x.Message?.ToString()));
}

public void SendRequests()
{
_client.Send(new NostrRequest("timeline:pubkey:follows", new NostrFilter
{
Authors = new[]
{
"6b75a3b4832f265989254ca560b700da3343d707d2319e7a45f4e01afe4a0c31",
"82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2",
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
"e9e4276490374a0daf7759fd5f475deff6ffb9b0fc5fa98c902b5f4b2fe3bba2",
"604e96e099936a104883958b040b47672e0f048c98ac793f37ffe4c720279eb2",
"7b6461d02c6f0be1cacdcf968c4246105a2db51c7770993bf8bb25e59cedffa7",
"559dc217d58a74982396fea8b4e9af4b6fc9c96f11abb134da285fec028658fd",
"23518fb6a27dc83e475eca500600e2160c71e554786dfda9658d5d9f57819b66",
"91c9a5e1a9744114c6fe2d61ae4de82629eaaa0fb52f48288093c7e7e036f832",
"c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0",
"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9",
"339d7804b6a69b7ef05a169d72ca3e977f64eb00ab6eedf21af0a2c2327691b3",
"85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204",
"020f2d21ae09bf35fcdfb65decf1478b846f5f728ab30c5eaabcd6d081a81c3e",
"6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
"e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42",
"a341f45ff9758f570a21b000c17d4e53a3a497c8397f26c0e6d61e5acffc7a98",
"83e818dfbeccea56b0f551576b3fd39a7a50e1d8159343500368fa085ccd964b",
"7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194",
"46bb7d86f84da649ff8a2404533de360d7baa6fe48fc03f779848c5f4c95d3b9",
"a575563c6b5b7a029f472e859ec2af026938cd8a03cf0fe2e6b82472b54aa638",
"7e8575871843980ffee6f8bcd37cc381589b5653bb8a1b3e585bf5e2a5c15f78",
"d27790fcb3f9afa0d709b2e9c5995151bc5ad008079bd0a474aa101d80e0eed3"
},
Kinds = new[]
{
NostrKind.Metadata,
NostrKind.ShortTextNote,
NostrKind.EncryptedDm,
NostrKind.Reaction,
NostrKind.Contacts,
NostrKind.RecommendRelay,
NostrKind.EventDeletion,
NostrKind.Reporting,
NostrKind.ClientAuthentication
},
Since = DateTime.UtcNow.AddHours(-12),
Until = DateTime.UtcNow.AddHours(4)
}));
}
}
160 changes: 160 additions & 0 deletions src/NostrConsoleClient/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using Microsoft.Extensions.Logging;
using Nostr.Client.Client;
using Nostr.Client.Communicator;
using Nostr.Client.Keys;
using Nostr.Client.Messages;
using Nostr.Client.Requests;
using NostrConsoleClient;
using Serilog;
using Serilog.Events;
using Serilog.Extensions.Logging;
using Serilog.Sinks.SystemConsole.Themes;

var exitEvent = new ManualResetEvent(false);

var logFactory = InitLogging();

AppDomain.CurrentDomain.ProcessExit += CurrentDomainOnProcessExit;
AssemblyLoadContext.Default.Unloading += DefaultOnUnloading;
Console.CancelKeyPress += ConsoleOnCancelKeyPress;

Console.WriteLine("|======================|");
Console.WriteLine("| NOSTR CLIENT |");
Console.WriteLine("|======================|");
Console.WriteLine();

Log.Debug("====================================");
Log.Debug(" STARTING ");
Log.Debug("====================================");

var relays = new[]
{
new Uri("wss://relayable.org"),
new Uri("wss://relay.damus.io")
};

using var multiClient = new NostrMultiWebsocketClient(logFactory.CreateLogger<NostrWebsocketClient>());
var communicators = new List<NostrWebsocketCommunicator>();

foreach (var relay in relays)
{
var communicator = CreateCommunicator(relay);
communicators.Add(communicator);
multiClient.RegisterCommunicator(communicator);
}

var viewer = new NostrViewer(multiClient);

viewer.Subscribe();

communicators.ForEach(x => x.Start());

viewer.SendRequests();

exitEvent.WaitOne();

Log.Debug("====================================");
Log.Debug(" STOPPING ");
Log.Debug("====================================");
Log.CloseAndFlush();

foreach (var communicator in communicators)
{
await communicator.Stop(WebSocketCloseStatus.NormalClosure, string.Empty);
await Task.Delay(500);
communicator.Dispose();
}

static SerilogLoggerFactory InitLogging()
{
Console.OutputEncoding = Encoding.UTF8;
var executingDir = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) ?? Directory.GetCurrentDirectory();
var logPath = Path.Combine(executingDir, "logs", "verbose.log");
var logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.File(logPath, rollingInterval: RollingInterval.Day)
.WriteTo.Console(LogEventLevel.Debug,
outputTemplate: "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message:lj}{NewLine}{Exception}",
theme: AnsiConsoleTheme.Code)
.CreateLogger();
Log.Logger = logger;
return new SerilogLoggerFactory(logger);
}

NostrWebsocketCommunicator CreateCommunicator(Uri uri)
{
var comm = new NostrWebsocketCommunicator(uri, () =>
{
var client = new ClientWebSocket();
client.Options.SetRequestHeader("Origin", "http://localhost");
return client;
});

comm.Name = uri.Host;
comm.ReconnectTimeout = null; //TimeSpan.FromSeconds(30);
comm.ErrorReconnectTimeout = TimeSpan.FromSeconds(60);

comm.ReconnectionHappened.Subscribe(info =>
Log.Information("[{relay}] Reconnection happened, type: {type}", comm.Name, info.Type));
comm.DisconnectionHappened.Subscribe(info =>
Log.Information("[{relay}] Disconnection happened, type: {type}, reason: {reason}", comm.Name, info.Type, info.CloseStatus));
return comm;
}

void CurrentDomainOnProcessExit(object? sender, EventArgs eventArgs)
{
Log.Warning("Exiting process");
exitEvent.Set();
}

void DefaultOnUnloading(AssemblyLoadContext assemblyLoadContext)
{
Log.Warning("Unloading process");
exitEvent.Set();
}

void ConsoleOnCancelKeyPress(object? sender, ConsoleCancelEventArgs e)
{
Log.Warning("Canceling process");
e.Cancel = true;
exitEvent.Set();
}

void SendEvent(INostrClient client, int counter)
{
var ev = new NostrEvent
{
Kind = NostrKind.ShortTextNote,
CreatedAt = DateTime.UtcNow,
Content = $"Test message {counter} from C# client"
};

var key = NostrPrivateKey.FromBech32("nsec1xjyhgzm2cjv2wp64wnh64d2n4s9ylguhwelekh5r38rlsfgk6mes62duaa");
var signed = ev.Sign(key);

client.Send(new NostrEventRequest(signed));
}

void SendDirectMessage(INostrClient client)
{
Log.Information("Sending encrypted direct message");

var sender = NostrPrivateKey.FromBech32("nsec1l0a7m5dlg4h9wurhnmgsq5nv9cqyvdwsutk4yf3w4fzzaqw7n80ssdfzkg");
var receiver = NostrPublicKey.FromHex("d27790fcb3f9afa0d709b2e9c5995151bc5ad008079bd0a474aa101d80e0eed3");

var ev = new NostrEvent
{
CreatedAt = DateTime.UtcNow,
Content = $"Test private message from C# client"
};

var encrypted = ev.EncryptDirect(sender, receiver);
var signed = encrypted.Sign(sender);

client.Send(new NostrEventRequest(signed));
}
2 changes: 2 additions & 0 deletions src/NuSocial.AvaUI/NuSocial.AvaUI/NuSocial.AvaUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageReference Include="FluentValidation" Version="11.5.2" />
<PackageReference Include="ReactiveUI.Fody" Version="18.4.44" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

namespace NuSocial.AvaUI.ViewModels
{
public class MainViewModel : ViewModelBase
public partial class MainViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia 123!";

public string TestString => "Test!";

[Reactive] public bool ShowLatencyWarning { get; set; } = false;
}
}
Loading

0 comments on commit fcc53b5

Please sign in to comment.