diff --git a/.gitea/workflows/publish-nuget.yml b/.gitea/workflows/publish-nuget.yml
index 0591e16..8124524 100644
--- a/.gitea/workflows/publish-nuget.yml
+++ b/.gitea/workflows/publish-nuget.yml
@@ -22,14 +22,14 @@ jobs:
uses: actions/checkout@v3
- name: Restore dependencies
- run: dotnet restore ./code/MessengerApi.Contracts/MessengerApi.Contracts.csproj
+ run: dotnet restore ./code/MessengerApi.Client/MessengerApi.Client.csproj
- name: Build project
- run: dotnet build ./code/MessengerApi.Contracts/MessengerApi.Contracts.csproj -c Release
+ run: dotnet build ./code/MessengerApi.Client/MessengerApi.Client.csproj -c Release
- name: Pack NuGet Package
run: |
- dotnet pack ./code/MessengerApi.Contracts/MessengerApi.Contracts.csproj -c Release -o out
+ dotnet pack ./code/MessengerApi.Client/MessengerApi.Client.csproj -c Release -o out
- name: Push to Gitea NuGet Registry
run: |
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d7f66d9..5735813 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,12 +1,5 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d652ca
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# messengerapi.Client
+
+[](https://paypal.me/emsicz) [](https://github.com/masiton?tab=repositories)
+
+Messenger API consumer library. Instead of crafting HTTP requests and parsing responses, you can use this wrapper to consume the API in a strongly-typed fashion in .NET.
+
+
\ No newline at end of file
diff --git a/code/MessengerApi.Client.Example/MessengerApi.Client.Example.csproj b/code/MessengerApi.Client.Example/MessengerApi.Client.Example.csproj
new file mode 100644
index 0000000..3e407bb
--- /dev/null
+++ b/code/MessengerApi.Client.Example/MessengerApi.Client.Example.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/code/MessengerApi.Client.Example/Program.cs b/code/MessengerApi.Client.Example/Program.cs
new file mode 100644
index 0000000..29d3848
--- /dev/null
+++ b/code/MessengerApi.Client.Example/Program.cs
@@ -0,0 +1,59 @@
+using MessengerApi.Model;
+using MessengerApi.Model.Messages;
+
+namespace MessengerApi.Example
+{
+ internal class Program
+ {
+ static void Main(string[] args)
+ {
+ var client1 = new Client(
+ new Credentials(
+ "aab8f7e9-ad13-4bf8-bb2e-0cd93d81adc0",
+ "http://localhost:5259"));
+
+ var client2 = new Client(
+ new Credentials(
+ "8f73f683-7cb3-40df-998e-6e604aef0e53",
+ "http://localhost:5259"));
+
+ var user1 = Guid.Parse("f696442b-e8dc-4074-b34f-94bcece8e74b");
+ var user2 = Guid.Parse("15d97720-f5b7-47aa-9c1a-71f98b0b9248");
+
+ var messageId = client1.SendMessage(new OutboxMessage
+ {
+ ToUserId = user2,
+ PayloadType = "TEST",
+ Payload = "TEST"
+ });
+
+ var state = client1.Verify(messageId);
+
+ if (state.IsDelivered != false)
+ {
+ throw new Exception();
+ }
+
+ var peeking = client2.Peek();
+
+ if (peeking != 1)
+ {
+ throw new Exception();
+ }
+
+ var receive = client2.GetMessages();
+
+ if (receive.Count() != 1)
+ {
+ throw new Exception();
+ }
+
+ state = client1.Verify(messageId);
+
+ if (state.IsDelivered != true)
+ {
+ throw new Exception();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/MessengerApi.Tools.sln b/code/MessengerApi.Client.sln
similarity index 52%
rename from code/MessengerApi.Tools.sln
rename to code/MessengerApi.Client.sln
index 488f164..20b43b1 100644
--- a/code/MessengerApi.Tools.sln
+++ b/code/MessengerApi.Client.sln
@@ -3,13 +3,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35312.102
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.Contracts", "MessengerApi.Contracts\MessengerApi.Contracts.csproj", "{833ED77F-A4E9-4FB3-BB84-4E55898B726A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.SubscriptionClient", "MessengerApi.SubscriptionClient\MessengerApi.SubscriptionClient.csproj", "{127D24B0-47F3-40E9-9136-899AFF206F19}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.QueryClient", "MessengerApi.QueryClient\MessengerApi.QueryClient.csproj", "{6441673B-2621-4E2C-A9A0-971E83C3F80A}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Directory.Packages.props = ..\Directory.Packages.props
+ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
@@ -18,24 +15,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".gitea", ".gitea", "{C3305381-7A52-4E26-9527-1697692DDD5A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Client", "MessengerApi.Client\MessengerApi.Client.csproj", "{67EE7DA2-D248-E323-15E8-30F464EB556E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Client.Example", "MessengerApi.Client.Example\MessengerApi.Client.Example.csproj", "{43F1DDF3-153B-70C6-E626-2031C5D71926}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Release|Any CPU.Build.0 = Release|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Release|Any CPU.Build.0 = Release|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67EE7DA2-D248-E323-15E8-30F464EB556E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67EE7DA2-D248-E323-15E8-30F464EB556E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67EE7DA2-D248-E323-15E8-30F464EB556E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67EE7DA2-D248-E323-15E8-30F464EB556E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {43F1DDF3-153B-70C6-E626-2031C5D71926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {43F1DDF3-153B-70C6-E626-2031C5D71926}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {43F1DDF3-153B-70C6-E626-2031C5D71926}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {43F1DDF3-153B-70C6-E626-2031C5D71926}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/code/MessengerApi.Contracts/MessengerClient.cs b/code/MessengerApi.Client/Client.cs
similarity index 55%
rename from code/MessengerApi.Contracts/MessengerClient.cs
rename to code/MessengerApi.Client/Client.cs
index fea3d5e..4ebf0c0 100644
--- a/code/MessengerApi.Contracts/MessengerClient.cs
+++ b/code/MessengerApi.Client/Client.cs
@@ -1,13 +1,16 @@
-using portaloggy;
+using MessengerApi.Model;
+using MessengerApi.Model.Messages;
+using portaloggy;
using System.Text;
+using System.Text.Json;
using System.Text.Json.Nodes;
-namespace MessengerApi.Contracts
+namespace MessengerApi
{
///
- /// Exists for mocking reason. This is implemented by .
+ /// Exists for mocking reason. This is implemented by .
///
- public interface IMessengerClient
+ public interface IClient
{
///
/// Receives pending messages from the messenger API.
@@ -20,38 +23,51 @@ namespace MessengerApi.Contracts
///
void AckMessage(InboxMessage message);
+ ///
+ /// Acknowledges message reception to the server.
+ ///
+ ///
+ void AckMessage(Guid messageId);
+
///
/// Sends a message.
///
/// Credentials to the API.
- void SendMessage(OutboxMessage outboxMessage);
+ Guid SendMessage(OutboxMessage outboxMessage);
///
/// Returns user ids for allowed message recipients.
///
Contact[] GetYellowPages();
+
+ ///
+ /// Returns number of pending messages, waiting for delivery.
+ ///
+ int Peek();
+
+ ///
+ /// Gets delivery state of a message.
+ ///
+ State Verify(Guid messageId);
}
- public class MessengerClient : IMessengerClient
+ public class Client : IClient
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
- private DateTime _lastReceivedUtc;
private Credentials _credentials;
- public MessengerClient(Credentials credentials, HttpClient httpClient = null, ILogger logger = null)
+ public Client(Credentials credentials, HttpClient httpClient = null, ILogger logger = null)
{
_credentials = credentials;
_httpClient = httpClient ?? new HttpClient();
_logger = logger ?? new ConsoleLogger();
- _lastReceivedUtc = DateTime.MinValue.ToUniversalTime();
}
public IEnumerable GetMessages()
{
- var since = Uri.EscapeDataString(this._lastReceivedUtc.ToString("o"));
- var url = $"{_credentials.ApiUrl}/receive?sinceUtc={since}";
+ var url = $"{_credentials.ApiUrl}/receive";
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _credentials.ApiKey);
@@ -85,11 +101,6 @@ namespace MessengerApi.Contracts
foreach (var item in json["messages"].AsArray())
{
- if (item["id"].GetValue() == -1)
- {
- continue;
- }
-
messages.Add(new InboxMessage
{
Id = item["id"].GetValue(),
@@ -99,13 +110,12 @@ namespace MessengerApi.Contracts
});
}
- _lastReceivedUtc = DateTime.UtcNow.Subtract(TimeSpan.FromSeconds(10));
- _logger.Debug($"Received {messages.Count} messages and last check timestamp is set to {_lastReceivedUtc.ToString("s")}.");
+ _logger.Debug($"Received {messages.Count} messages.");
return messages.ToArray();
}
- public void SendMessage(OutboxMessage outboxMessage)
+ public Guid SendMessage(OutboxMessage outboxMessage)
{
var body = new JsonObject();
@@ -133,17 +143,22 @@ namespace MessengerApi.Contracts
var url = $"{_credentials.ApiUrl}/send";
var request = new HttpRequestMessage(HttpMethod.Post, url);
+ request.Content = content;
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _credentials.ApiKey);
_logger.Debug($"Sending query to {url} with content {body.ToString()} to obtain messages.");
var response = _httpClient.Send(request);
-
+
if (!response.IsSuccessStatusCode)
{
_logger.Error(response.ReasonPhrase);
throw new HttpRequestException("Can't send.", null, response.StatusCode);
}
+
+ var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
+ var guid = JsonSerializer.Deserialize(responseString);
+ return guid;
}
public Contact[] GetYellowPages()
@@ -180,7 +195,90 @@ namespace MessengerApi.Contracts
public void AckMessage(InboxMessage message)
{
+ this.AckMessage(message.Id);
+ }
+
+ public void AckMessage(Guid messageId)
+ {
+ var body = new JsonObject
+ {
+ { "messageId", JsonValue.Create(messageId) }
+ };
+
+ var content = new StringContent(body.ToString(), Encoding.UTF8, "application/json");
+
+ var url = $"{_credentials.ApiUrl}/ack";
+ var request = new HttpRequestMessage(HttpMethod.Post, url);
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _credentials.ApiKey);
+
+ _logger.Debug($"Sending query to {url} with content {body.ToString()} to obtain messages.");
+
+ var response = _httpClient.Send(request);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ _logger.Error(response.ReasonPhrase);
+ throw new HttpRequestException("Can't send.", null, response.StatusCode);
+ }
+ }
+
+ public int Peek()
+ {
+ var url = $"{_credentials.ApiUrl}/peek";
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _credentials.ApiKey);
+
+ _logger.Debug($"Sending query to {url} with content {request.ToString()} to obtain messages.");
+
+ var response = _httpClient.Send(request);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ _logger.Error(response.ReasonPhrase);
+ throw new HttpRequestException("Can't receive.", null, response.StatusCode);
+ }
+
+ var responseContent = response.Content
+ .ReadAsStringAsync()
+ .GetAwaiter()
+ .GetResult();
+
+ if (!string.IsNullOrWhiteSpace(responseContent))
+ {
+ _logger.Debug($"Received response of {responseContent}.");
+ return int.Parse(responseContent);
+ }
+
+ throw new InvalidOperationException("Unreadable response.");
+ }
+
+ public State Verify(Guid messageId)
+ {
+ var url = $"{_credentials.ApiUrl}/verify?messageId={messageId}";
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _credentials.ApiKey);
+
+ _logger.Debug($"Sending query to {url} to get message state.");
+
+ var response = _httpClient.Send(request);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ _logger.Error(response.ReasonPhrase);
+ throw new HttpRequestException("Can't receive.", null, response.StatusCode);
+ }
+
+ var responseContent = response.Content
+ .ReadAsStringAsync()
+ .GetAwaiter()
+ .GetResult();
+ if (!string.IsNullOrWhiteSpace(responseContent))
+ {
+ return JsonSerializer.Deserialize(responseContent);
+ }
+
+ throw new InvalidOperationException("Unreadable response.");
}
}
-}
+}
\ No newline at end of file
diff --git a/code/MessengerApi.Contracts/MessengerApi.Contracts.csproj b/code/MessengerApi.Client/MessengerApi.Client.csproj
similarity index 83%
rename from code/MessengerApi.Contracts/MessengerApi.Contracts.csproj
rename to code/MessengerApi.Client/MessengerApi.Client.csproj
index ed73144..f03f9e1 100644
--- a/code/MessengerApi.Contracts/MessengerApi.Contracts.csproj
+++ b/code/MessengerApi.Client/MessengerApi.Client.csproj
@@ -15,6 +15,10 @@
https://gitea.masita.net/mc/messengerapi-tools
logging;log;logger
mit-0
+ Messenger API consumer library for .NET.
+ mc @ 2024
+ mc
+ true
diff --git a/code/MessengerApi.Contracts/Model/Contact.cs b/code/MessengerApi.Client/Model/Contact.cs
similarity index 76%
rename from code/MessengerApi.Contracts/Model/Contact.cs
rename to code/MessengerApi.Client/Model/Contact.cs
index 1b232de..32b45c8 100644
--- a/code/MessengerApi.Contracts/Model/Contact.cs
+++ b/code/MessengerApi.Client/Model/Contact.cs
@@ -1,4 +1,4 @@
-namespace MessengerApi.Contracts
+namespace MessengerApi.Model
{
public class Contact
{
diff --git a/code/MessengerApi.Contracts/Model/Credentials.cs b/code/MessengerApi.Client/Model/Credentials.cs
similarity index 88%
rename from code/MessengerApi.Contracts/Model/Credentials.cs
rename to code/MessengerApi.Client/Model/Credentials.cs
index 483e43d..b67cd30 100644
--- a/code/MessengerApi.Contracts/Model/Credentials.cs
+++ b/code/MessengerApi.Client/Model/Credentials.cs
@@ -1,4 +1,4 @@
-namespace MessengerApi.Contracts
+namespace MessengerApi.Model
{
public class Credentials
{
diff --git a/code/MessengerApi.Contracts/Model/Messages/InboxMessage.cs b/code/MessengerApi.Client/Model/Messages/InboxMessage.cs
similarity index 90%
rename from code/MessengerApi.Contracts/Model/Messages/InboxMessage.cs
rename to code/MessengerApi.Client/Model/Messages/InboxMessage.cs
index 36e7e2c..2cc3608 100644
--- a/code/MessengerApi.Contracts/Model/Messages/InboxMessage.cs
+++ b/code/MessengerApi.Client/Model/Messages/InboxMessage.cs
@@ -1,4 +1,4 @@
-namespace MessengerApi.Contracts
+namespace MessengerApi.Model.Messages
{
///
/// Message when received is inbox. For server apps, this is request-type of message. For clients, this is a response-type of message.
diff --git a/code/MessengerApi.Contracts/Model/Messages/OutboxMessage.cs b/code/MessengerApi.Client/Model/Messages/OutboxMessage.cs
similarity index 90%
rename from code/MessengerApi.Contracts/Model/Messages/OutboxMessage.cs
rename to code/MessengerApi.Client/Model/Messages/OutboxMessage.cs
index d6d370e..13145fd 100644
--- a/code/MessengerApi.Contracts/Model/Messages/OutboxMessage.cs
+++ b/code/MessengerApi.Client/Model/Messages/OutboxMessage.cs
@@ -1,4 +1,4 @@
-namespace MessengerApi.Contracts
+namespace MessengerApi.Model.Messages
{
///
/// Outbox type of message. A server-app will treat this as a response. A client app will treat this as a request.
diff --git a/code/MessengerApi.Client/Model/State.cs b/code/MessengerApi.Client/Model/State.cs
new file mode 100644
index 0000000..9ac4440
--- /dev/null
+++ b/code/MessengerApi.Client/Model/State.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace MessengerApi.Model
+{
+ public class State
+ {
+ [JsonPropertyName("isDelivered")]
+ public bool IsDelivered { get; set; }
+
+ [JsonPropertyName("isAcknowledged")]
+ public bool IsAcknowledged { get; set; }
+ }
+}