diff --git a/src/ISoulseekClient.cs b/src/ISoulseekClient.cs
index 354338130..dd19815c1 100644
--- a/src/ISoulseekClient.cs
+++ b/src/ISoulseekClient.cs
@@ -1260,6 +1260,15 @@ public interface ISoulseekClient : IDisposable, IDiagnosticGenerator
/// Thrown when an exception is encountered during the operation.
Task SetStatusAsync(UserPresence status, CancellationToken? cancellationToken = null);
+ ///
+ /// Asynchronously informs the server of the user's liked and disliked interests.
+ ///
+ /// A collection of liked interests.
+ /// A collection of disliked interests.
+ /// The token to monitor for cancellation requests.
+ /// The representing the asynchronous operation.
+ Task SetInterestsAsync(IEnumerable likes, IEnumerable dislikes, CancellationToken? cancellationToken = null);
+
///
/// Asynchronously starts receiving public chat messages.
///
diff --git a/src/Messaging/Messages/Server/HatedInterestAddCommand.cs b/src/Messaging/Messages/Server/HatedInterestAddCommand.cs
new file mode 100644
index 000000000..7794825be
--- /dev/null
+++ b/src/Messaging/Messages/Server/HatedInterestAddCommand.cs
@@ -0,0 +1,53 @@
+//
+// Copyright (c) JP Dillingham. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see https://www.gnu.org/licenses/.
+//
+
+namespace Soulseek.Messaging.Messages.Server
+{
+ using Soulseek.Messaging;
+
+ ///
+ /// Adds a disliked interest to the user's profile on the server.
+ ///
+ internal class HatedInterestAddCommand : IOutgoingMessage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The disliked interest to add.
+ public HatedInterestAddCommand(string interest)
+ {
+ Interest = interest;
+ }
+
+ ///
+ /// Gets the disliked interest to add.
+ ///
+ public string Interest { get; }
+
+ ///
+ /// Constructs a array from this message.
+ ///
+ /// The constructed byte array.
+ public byte[] ToByteArray()
+ {
+ return new MessageBuilder()
+ .WriteCode((MessageCode.Server)117)
+ .WriteString(Interest)
+ .Build();
+ }
+ }
+}
diff --git a/src/Messaging/Messages/Server/InterestAddCommand.cs b/src/Messaging/Messages/Server/InterestAddCommand.cs
new file mode 100644
index 000000000..eace24068
--- /dev/null
+++ b/src/Messaging/Messages/Server/InterestAddCommand.cs
@@ -0,0 +1,53 @@
+//
+// Copyright (c) JP Dillingham. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see https://www.gnu.org/licenses/.
+//
+
+namespace Soulseek.Messaging.Messages.Server
+{
+ using Soulseek.Messaging;
+
+ ///
+ /// Adds a liked interest to the user's profile on the server.
+ ///
+ internal class InterestAddCommand : IOutgoingMessage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The interest to add.
+ public InterestAddCommand(string interest)
+ {
+ Interest = interest;
+ }
+
+ ///
+ /// Gets the interest to add.
+ ///
+ public string Interest { get; }
+
+ ///
+ /// Constructs a array from this message.
+ ///
+ /// The constructed byte array.
+ public byte[] ToByteArray()
+ {
+ return new MessageBuilder()
+ .WriteCode((MessageCode.Server)51)
+ .WriteString(Interest)
+ .Build();
+ }
+ }
+}
diff --git a/src/SoulseekClient.cs b/src/SoulseekClient.cs
index 87c55bea0..ae14ddb43 100644
--- a/src/SoulseekClient.cs
+++ b/src/SoulseekClient.cs
@@ -17,6 +17,13 @@
namespace Soulseek
{
+ using Soulseek.Diagnostics;
+ using Soulseek.Messaging;
+ using Soulseek.Messaging.Handlers;
+ using Soulseek.Messaging.Messages;
+ using Soulseek.Messaging.Messages.Server;
+ using Soulseek.Network;
+ using Soulseek.Network.Tcp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -27,12 +34,6 @@ namespace Soulseek
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
- using Soulseek.Diagnostics;
- using Soulseek.Messaging;
- using Soulseek.Messaging.Handlers;
- using Soulseek.Messaging.Messages;
- using Soulseek.Network;
- using Soulseek.Network.Tcp;
///
/// A client for the Soulseek file sharing network.
@@ -2479,6 +2480,58 @@ public Task SetSharedCountsAsync(int directories, int files, CancellationToken?
}
}
+
+
+ ///
+ /// Asynchronously informs the server of the user's and .
+ ///
+ /// A collection of liked interests.
+ /// A collection of disliked interests.
+ /// The token to monitor for cancellation requests.
+ /// The Task representing the asynchronous operation.
+ /// Thrown when the client is not connected or logged in.
+ /// Thrown when the operation has timed out.
+ /// Thrown when the operation has been cancelled.
+ /// Thrown when an exception is encountered during the operation.
+ public async Task SetInterestsAsync(IEnumerable likes, IEnumerable dislikes, CancellationToken? cancellationToken = null)
+ {
+ if (!State.HasFlag(SoulseekClientStates.Connected) || !State.HasFlag(SoulseekClientStates.LoggedIn))
+ {
+ throw new InvalidOperationException($"The server connection must be connected and logged in to set interests; currently {State}.");
+ }
+
+ var token = cancellationToken ?? CancellationToken.None;
+
+ try
+ {
+ if (likes != null)
+ {
+ foreach (var like in likes)
+ {
+ if (string.IsNullOrWhiteSpace(like)) continue;
+
+ await ServerConnection.WriteAsync(new InterestAddCommand(like), token).ConfigureAwait(false);
+ }
+ }
+
+ if (dislikes != null)
+ {
+ foreach (var dislike in dislikes)
+ {
+ if (string.IsNullOrWhiteSpace(dislike)) continue;
+
+ await ServerConnection.WriteAsync(new HatedInterestAddCommand(dislike), token).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (Exception ex) when (!(ex is OperationCanceledException) && !(ex is TimeoutException))
+ {
+ throw new SoulseekClientException($"Failed to set interests: {ex.Message}", ex);
+ }
+ }
+
+
+
///
/// Asynchronously informs the server of the current online of the client.
///