Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6bab80a
add minimal .NET Console UI chat application
leny62 Mar 4, 2025
9bb80d2
add chat room commands
leny62 Mar 4, 2025
c03bc48
feat: Enhance dotnet-libp2p-chat with improved protocol handling and …
leny62 Mar 4, 2025
b2211a8
add dotnet p2p chat with go-peer
leny62 Mar 18, 2025
f1161db
fix 2way messaging
leny62 Mar 18, 2025
d07ce08
Add .NET peer with beautiful console UI and proper .gitignore files
leny62 Mar 18, 2025
7017c32
Restore .gitignore to match main branch
leny62 Mar 21, 2025
7cfc2be
Restore go-peer to match main branch
leny62 Mar 21, 2025
c7ae16d
Restore js-peer to match main branch
leny62 Mar 21, 2025
3256394
update gitignore
leny62 Mar 21, 2025
1a30870
Delete .cursor/rules directory
leny62 Mar 21, 2025
5efdb5a
Merge branch 'main' into add-dotnet-peer-example
leny62 Mar 21, 2025
412d30a
Update .gitignore
leny62 Mar 21, 2025
24f0ab8
Merge pull request #1 from leny62/add-dotnet-peer-example
leny62 Mar 21, 2025
0a0954a
Remove bin and obj directories from Git tracking
leny62 Mar 21, 2025
a823100
Merge pull request #2 from leny62/add-dotnet-peer-example
leny62 Mar 21, 2025
c5f4b5a
Merge branch 'libp2p:main' into main
leny62 Apr 2, 2025
3b01fd6
add connected peers tracking
leny62 Apr 2, 2025
2c03cf1
refactor project structure
leny62 Apr 2, 2025
3af28e3
add connected peers tracking (#3)
leny62 Apr 2, 2025
3d93e77
code clean up
leny62 May 19, 2025
8be9f22
Merge branch 'main' into add-dotnet-peer-example
leny62 May 19, 2025
5aa582e
Add dotnet peer example (#4)
leny62 May 19, 2025
8a7a94a
Merge branch 'libp2p:main' into main
leny62 May 19, 2025
c60dc0a
add ILogger interface, clean up
leny62 May 20, 2025
782023e
Merge branch 'main' into add-dotnet-peer-example
leny62 May 20, 2025
23c40f0
Merge pull request #5 from leny62/add-dotnet-peer-example
leny62 May 20, 2025
4446369
Merge branch 'dotnet-peer-development' of github.com:libp2p/universal…
leny62 May 27, 2025
e719191
setup .NET peer Dockerfile
leny62 Jun 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ yarn.lock
.vscode/
.DS_Store
go-peer/go-peer
**/.idea
.cursor/
.qodo
40 changes: 40 additions & 0 deletions dotnet-peer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# .NET Core
bin/
obj/
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio files
.vs/
.vscode/
*.nupkg
*.pfx
*.snk
*.[Pp]ublish.xml
*.azurePubxml
*.pubxml
*.publishproj

# NuGet Packages
*.nupkg
*.snupkg
**/packages/*
!**/packages/build/
41 changes: 41 additions & 0 deletions dotnet-peer/Libp2pChat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# .NET Core
bin/
obj/
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio files
.vs/
.vscode/
*.nupkg
*.pfx
*.snk
*.[Pp]ublish.xml
*.azurePubxml
*.pubxml
*.publishproj

# NuGet Packages
*.nupkg
*.snupkg
**/packages/*
!**/packages/build/
.qodo
22 changes: 22 additions & 0 deletions dotnet-peer/Libp2pChat/Application/Interfaces/IAppLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Libp2pChat.Application.Interfaces;

/// <summary>
/// Legacy interface for application logging. Use Microsoft.Extensions.Logging.ILogger instead for new code.
/// </summary>
public interface IAppLogger
{
/// <summary>Logs an informational message.</summary>
void LogInformation(string message);

/// <summary>Logs a warning message.</summary>
void LogWarning(string message);

/// <summary>Logs an error message.</summary>
void LogError(string message);

/// <summary>Logs an error message with an exception.</summary>
void LogError(string message, Exception exception);

/// <summary>Logs a debug message.</summary>
void LogDebug(string message);
}
54 changes: 54 additions & 0 deletions dotnet-peer/Libp2pChat/Application/Interfaces/IChatService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Libp2pChat.Domain.Models;

namespace Libp2pChat.Application.Interfaces;

/// <summary>
/// Defines the operations for a chat service.
/// </summary>
public interface IChatService
{
/// <summary>
/// Gets the current peer ID.
/// </summary>
string PeerId { get; }

/// <summary>
/// Gets the multiaddress for this peer.
/// </summary>
string GetMultiaddress();

/// <summary>
/// Gets the list of currently known peers.
/// </summary>
IReadOnlyCollection<Peer> GetKnownPeers();

/// <summary>
/// Sends a message to all peers.
/// </summary>
/// <param name="message">The message to send.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task SendMessageAsync(string message);

/// <summary>
/// Event raised when a new message is received.
/// </summary>
event EventHandler<ChatMessage> MessageReceived;

/// <summary>
/// Event raised when a peer is detected.
/// </summary>
event EventHandler<Peer> PeerDiscovered;

/// <summary>
/// Starts the chat service.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task StartAsync(CancellationToken cancellationToken);

/// <summary>
/// Stops the chat service.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
Task StopAsync();
}
203 changes: 203 additions & 0 deletions dotnet-peer/Libp2pChat/Application/Services/ChatApplicationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
using Libp2pChat.Application.Interfaces;
using Libp2pChat.Domain.Models;
using Libp2pChat.Presentation.UI;
using Microsoft.Extensions.Logging;

namespace Libp2pChat.Application.Services;

/// <summary>
/// Application service that coordinates the chat components.
/// </summary>
public class ChatApplicationService : IDisposable
{
private readonly IChatService _chatService;
private readonly IChatUI _chatUI;
private readonly ILogger<ChatApplicationService> _logger;
private readonly CancellationTokenSource _cancellationTokenSource;
private bool _isDisposed;

/// <summary>
/// Creates a new instance of the chat application service.
/// </summary>
public ChatApplicationService(IChatService chatService, IChatUI chatUI, ILogger<ChatApplicationService> logger)
{
_chatService = chatService;
_chatUI = chatUI;
_logger = logger;
_cancellationTokenSource = new CancellationTokenSource();
}

/// <summary>
/// Starts the chat application.
/// </summary>
public async Task RunAsync()
{
try
{
// Initialize the UI
_chatUI.Initialize();

// Wire up events
_chatService.MessageReceived += OnMessageReceived;
_chatService.PeerDiscovered += OnPeerDiscovered;
_chatUI.MessageSent += OnMessageSent;
_chatUI.ExitRequested += OnExitRequested;

// Start the chat service in a background task
_ = Task.Run(async () =>
{
try
{
// Start the chat service
_logger.LogInformation("Starting chat application...");
await _chatService.StartAsync(_cancellationTokenSource.Token);

// Update the UI with peer info
string peerId = _chatService.PeerId;
string multiaddress = _chatService.GetMultiaddress();
_chatUI.UpdatePeerInfo(peerId, multiaddress);

// Log connection info
_logger.LogInformation("Peer ID: {PeerId}", peerId);
_logger.LogInformation("Multiaddress: {Multiaddress}", multiaddress);

// Add welcome messages directly to the chat area
_chatUI.AddChatMessage("Welcome to Libp2p Chat!");
_chatUI.AddChatMessage("You can exchange messages with other libp2p peers.");
_chatUI.AddChatMessage("Use the 'Send' button or press Enter to send messages.");
_chatUI.AddChatMessage("Press Ctrl+Q to exit.");

// Add help information
_logger.LogInformation("\n[Help] CONNECTION INSTRUCTIONS:");
_logger.LogInformation("[Help] Connect to this peer using your libp2p client:");
_logger.LogInformation("[Help] Multiaddress: {Multiaddress}", multiaddress);
_logger.LogInformation("[Help] For localhost connections: /ip4/127.0.0.1/tcp/{Port}/p2p/{PeerId}", multiaddress.Split('/')[4], peerId);
_logger.LogInformation("[Help] Use the above multiaddress with your libp2p client's connect command.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in background task");
}
});

// Run the UI on the main thread
_chatUI.Run();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error starting chat application");
throw;
}
}

/// <summary>
/// Stops the chat application.
/// </summary>
public async Task StopAsync()
{
try
{
_logger.LogInformation("Stopping chat application...");

if (!_cancellationTokenSource.IsCancellationRequested)
_cancellationTokenSource.Cancel();

await _chatService.StopAsync();

_logger.LogInformation("Chat application stopped");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error stopping chat application");
throw;
}
}

/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Disposes resources.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;

if (disposing)
{
// Unwire events
_chatService.MessageReceived -= OnMessageReceived;
_chatService.PeerDiscovered -= OnPeerDiscovered;
_chatUI.MessageSent -= OnMessageSent;
_chatUI.ExitRequested -= OnExitRequested;

// Dispose cancellation token source
_cancellationTokenSource.Dispose();

// Dispose other disposables
if (_chatService is IDisposable disposableService)
disposableService.Dispose();

if (_chatUI is IDisposable disposableUI)
disposableUI.Dispose();
}

_isDisposed = true;
}

private void OnMessageReceived(object? sender, ChatMessage message)
{
try
{
_chatUI.AddChatMessage(message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling received message");
}
}

private void OnPeerDiscovered(object? sender, Peer peer)
{
try
{
_chatUI.AddPeer(peer);
_logger.LogInformation("Peer discovered: {PeerId} ({DisplayName})", peer.Id, peer.DisplayName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling peer discovery");
}
}

private async void OnMessageSent(object? sender, string message)
{
try
{
await _chatService.SendMessageAsync(message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending message");
}
}

private async void OnExitRequested(object? sender, EventArgs e)
{
try
{
await StopAsync();

Terminal.Gui.Application.RequestStop();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling exit request");
}
}
}
Loading