1
0

Send Text Message

This commit is contained in:
2025-12-02 22:49:51 +01:00
parent 23ad083d1d
commit 17ff8f7371
27 changed files with 2157 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
using System.Net.Http;
using System.Net.Http.Headers;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Implements the <see cref="IAuthentication"/> interface for BEARER authentication.
/// </summary>
public class AccessTokenAuthentication : IAuthentication
{
private readonly string _token;
/// <summary>
/// Initializes a new instance of the <see cref="AccessTokenAuthentication"/> class.
/// </summary>
/// <param name="token">The bearer token.</param>
public AccessTokenAuthentication(string token)
{
if (string.IsNullOrWhiteSpace(token))
throw new ArgumentNullException(nameof(token), "The token cannot be null or whitespace.");
_token = token;
}
/// <inheritdoc/>
public void AddHeader(HttpClient httpClient)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Implements the <see cref="IAuthentication"/> interface for BASIC authentication.
/// </summary>
public class BasicAuthentication : IAuthentication
{
private readonly string _username;
private readonly string _password;
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthentication"/> class.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
public BasicAuthentication(string username, string password)
{
if (string.IsNullOrWhiteSpace(username))
throw new ArgumentNullException(nameof(username), "The username cannot be null or whitespace.");
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentNullException(nameof(password), "The password cannot be null or whitespace.");
_username = username;
_password = password;
}
/// <inheritdoc/>
public void AddHeader(HttpClient httpClient)
{
string plainText = $"{_username}:{_password}";
byte[] plainBytes = Encoding.ASCII.GetBytes(plainText);
string base64 = Convert.ToBase64String(plainBytes);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64);
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Net.Http;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Defines the interface to add authentication information.
/// </summary>
public interface IAuthentication
{
/// <summary>
/// Adds the required authentication header to the provided <see cref="HttpClient"/> instance.
/// </summary>
/// <param name="httpClient"></param>
void AddHeader(HttpClient httpClient);
}
}

View File

@@ -0,0 +1,45 @@
using System.Net;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Options for the LinkMobility API.
/// </summary>
public class ClientOptions
{
/// <summary>
/// Gets or sets the default base url for the API.
/// </summary>
public virtual string BaseUrl { get; set; } = "https://api.linkmobility.eu/rest/";
/// <summary>
/// Gets or sets the default timeout for the API.
/// </summary>
public virtual TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(100);
/// <summary>
/// Gets or sets additional default headers to every request.
/// </summary>
public virtual IDictionary<string, string> DefaultHeaders { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Gets or sets additional default query parameters to every request.
/// </summary>
public virtual IDictionary<string, string> DefaultQueryParams { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Gets or sets a value indicating whether to allow redirects.
/// </summary>
public virtual bool AllowRedirects { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to use a proxy.
/// </summary>
public virtual bool UseProxy { get; set; }
/// <summary>
/// Gets or sets the proxy information.
/// </summary>
public virtual IWebProxy Proxy { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using System.Runtime.Serialization;
using Newtonsoft.Json.Converters;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Content categories as defined by <see href="https://developer.linkmobility.eu/sms-api/rest-api#operation/sendUsingPOST">Link Mobility</see>.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum ContentCategory
{
/// <summary>
/// Represents content that is classified as informational.
/// </summary>
[EnumMember(Value = "informational")]
Informational = 1,
/// <summary>
/// Represents content that is classified as an advertisement.
/// </summary>
[EnumMember(Value = "advertisement")]
Advertisement = 2,
}
}

View File

@@ -0,0 +1,18 @@
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Specifies the message type.
/// </summary>
public enum MessageType
{
/// <summary>
/// The message is sent as defined in the account settings.
/// </summary>
Default = 1,
/// <summary>
/// The message is sent as voice call.
/// </summary>
Voice = 2,
}
}

View File

@@ -0,0 +1,36 @@
using System.Runtime.Serialization;
using Newtonsoft.Json.Converters;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Specifies the type of sender address.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum SenderAddressType
{
/// <summary>
/// National number.
/// </summary>
[EnumMember(Value = "national")]
National = 1,
/// <summary>
/// International number.
/// </summary>
[EnumMember(Value = "international")]
International = 2,
/// <summary>
/// Alphanumeric sender ID.
/// </summary>
[EnumMember(Value = "alphanumeric")]
Alphanumeric = 3,
/// <summary>
/// Shortcode.
/// </summary>
[EnumMember(Value = "shortcode")]
Shortcode = 4,
}
}

View File

@@ -0,0 +1,149 @@
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Custom status codes as defined by <see href="https://developer.linkmobility.eu/sms-api/rest-api#section/Status-codes">Link Mobility</see>.
/// </summary>
public enum StatusCodes
{
/// <summary>
/// Request accepted, Message(s) sent.
/// </summary>
Ok = 2000,
/// <summary>
/// Request accepted, Message(s) queued.
/// </summary>
OkQueued = 2001,
/// <summary>
/// Invalid Credentials. Inactive account or customer.
/// </summary>
InvalidCredentials = 4001,
/// <summary>
/// One or more recipients are not in the correct format or are containing invalid MSISDNs.
/// </summary>
InvalidRecipient = 4002,
/// <summary>
/// Invalid Sender. Sender address or type is invalid.
/// </summary>
InvalidSender = 4003,
/// <summary>
/// Invalid messageType.
/// </summary>
InvalidMessageType = 4004,
/// <summary>
/// Invalid clientMessageId.
/// </summary>
InvalidMessageId = 4008,
/// <summary>
/// Message text (messageContent) is invalid.
/// </summary>
InvalidText = 4009,
/// <summary>
/// Message limit is reached.
/// </summary>
MessageLimitExceeded = 4013,
/// <summary>
/// Sender IP address is not authorized.
/// </summary>
UnauthorizedIp = 4014,
/// <summary>
/// Invalid Message Priority.
/// </summary>
InvalidMessagePriority = 4015,
/// <summary>
/// Invalid notification callback url.
/// </summary>
InvalidCallbackAddress = 4016,
/// <summary>
/// A required parameter was not given. The parameter name is shown in the status message.
/// </summary>
ParameterMissing = 4019,
/// <summary>
/// Account is invalid.
/// </summary>
InvalidAccount = 4021,
/// <summary>
/// Access to the API was denied.
/// </summary>
AccessDenied = 4022,
/// <summary>
/// Request limit exceeded for this IP address.
/// </summary>
ThrottlingSpammingIp = 4023,
/// <summary>
/// Transfer rate for immediate transmissions exceeded.
/// Too many recipients in this request (1000).
/// </summary>
ThrottlingTooManyRecipients = 4025,
/// <summary>
/// The message content results in too many (automatically generated) sms segments.
/// </summary>
MaxSmsPerMessageExceeded = 4026,
/// <summary>
/// A message content segment is invalid
/// </summary>
InvalidMessageSegment = 4027,
/// <summary>
/// Recipients not allowed.
/// </summary>
RecipientsNotAllowed = 4029,
/// <summary>
/// All recipients blacklisted.
/// </summary>
RecipientBlacklisted = 4030,
/// <summary>
/// Not allowed to send sms messages.
/// </summary>
SmsDisabled = 4035,
/// <summary>
/// Invalid content category.
/// </summary>
InvalidContentCategory = 4040,
/// <summary>
/// Invalid validity periode.
/// </summary>
InvalidValidityPeriode = 4041,
/// <summary>
/// All of the recipients are blocked by quality rating.
/// </summary>
RecipientsBlockedByQualityRating = 4042,
/// <summary>
/// All of the recipients are blocked by spamcheck.
/// </summary>
RecipientsBlockedBySpamcheck = 4043,
/// <summary>
/// Internal error.
/// </summary>
InternalError = 5000,
/// <summary>
/// Service unavailable.
/// </summary>
ServiceUnavailable = 5003
}
}

View File

@@ -0,0 +1,19 @@
using System.Threading;
using System.Threading.Tasks;
using AMWD.Net.Api.LinkMobility.Requests;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Defines the interface for a Link Mobility API client.
/// </summary>
public interface ILinkMobilityClient
{
/// <summary>
/// Sends a text message to a list of recipients.
/// </summary>
/// <param name="request">The request data.</param>
/// <param name="cancellationToken">A cancellation token to propagate notification that operations should be canceled.</param>
Task<SendTextMessageResponse> SendTextMessage(SendTextMessageRequest request, CancellationToken cancellationToken = default);
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<Nullable>enable</Nullable>
<PackageId>AMWD.Net.Api.LinkMobility</PackageId>
<PackageTags>link mobility api</PackageTags>
<AssemblyName>amwd-linkmobility</AssemblyName>
<RootNamespace>AMWD.Net.Api.LinkMobility</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<PackageIcon>package-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>LinkMobility API</Product>
<Description>Implementation of the Link Mobility REST API</Description>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Include="../../package-icon.png" Pack="true" PackagePath="/" />
<None Include="../../LICENSE.txt" Pack="true" PackagePath="/" />
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,43 @@
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using AMWD.Net.Api.LinkMobility.Requests;
namespace AMWD.Net.Api.LinkMobility
{
public partial class LinkMobilityClient
{
/// <summary>
/// Sends a text message to a list of recipients.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">A cancellation token to propagate notification that operations should be canceled.</param>
public Task<SendTextMessageResponse> SendTextMessage(SendTextMessageRequest request, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (string.IsNullOrWhiteSpace(request.MessageContent))
throw new ArgumentException("A message must be provided.", nameof(request.MessageContent));
if (request.RecipientAddressList == null || request.RecipientAddressList.Count == 0)
throw new ArgumentException("At least one recipient must be provided.", nameof(request.RecipientAddressList));
foreach (string recipient in request.RecipientAddressList)
{
if (!IsValidMSISDN(recipient))
throw new ArgumentException($"Recipient address '{recipient}' is not a valid MSISDN format.", nameof(request.RecipientAddressList));
}
return PostAsync<SendTextMessageResponse, SendTextMessageRequest>("/smsmessaging/text", request, cancellationToken: cancellationToken);
}
private static bool IsValidMSISDN(string msisdn)
{
if (string.IsNullOrWhiteSpace(msisdn))
return false;
return Regex.IsMatch(msisdn, @"^[1-9][0-9]{7,14}$", RegexOptions.Compiled);
}
}
}

View File

@@ -0,0 +1,223 @@
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Provides a client for interacting with the Link Mobility API.
/// </summary>
public partial class LinkMobilityClient : ILinkMobilityClient, IDisposable
{
private static readonly JsonSerializerSettings _jsonSerializerSettings = new()
{
Culture = CultureInfo.InvariantCulture,
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore
};
private readonly ClientOptions _clientOptions;
private readonly HttpClient _httpClient;
private bool _isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="LinkMobilityClient" /> class using basic authentication.
/// </summary>
/// <param name="username">The username used for basic authentication.</param>
/// <param name="password">The password used for basic authentication.</param>
/// <param name="clientOptions">Optional configuration settings for the client.</param>
public LinkMobilityClient(string username, string password, ClientOptions? clientOptions = null)
: this(new BasicAuthentication(username, password), clientOptions)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LinkMobilityClient"/> class using access token authentication.
/// </summary>
/// <param name="token">The bearer token used for authentication.</param>
/// <param name="clientOptions">Optional configuration settings for the client.</param>
public LinkMobilityClient(string token, ClientOptions? clientOptions = null)
: this(new AccessTokenAuthentication(token), clientOptions)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LinkMobilityClient"/> class using authentication and optional client
/// configuration.
/// </summary>
/// <param name="authentication">The authentication mechanism used to authorize requests.</param>
/// <param name="clientOptions">Optional client configuration settings.</param>
public LinkMobilityClient(IAuthentication authentication, ClientOptions? clientOptions = null)
{
if (authentication == null)
throw new ArgumentNullException(nameof(authentication));
_clientOptions = clientOptions ?? new ClientOptions();
ValidateClientOptions();
_httpClient = CreateHttpClient();
authentication.AddHeader(_httpClient);
}
/// <summary>
/// Disposes of the resources used by the <see cref="LinkMobilityClient"/> object.
/// </summary>
public void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
_httpClient.Dispose();
GC.SuppressFinalize(this);
}
private void ValidateClientOptions()
{
if (string.IsNullOrWhiteSpace(_clientOptions.BaseUrl))
throw new ArgumentNullException(nameof(_clientOptions.BaseUrl), "BaseUrl cannot be null or whitespace.");
if (_clientOptions.Timeout <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(_clientOptions.Timeout), "Timeout must be greater than zero.");
if (_clientOptions.UseProxy && _clientOptions.Proxy == null)
throw new ArgumentNullException(nameof(_clientOptions.Proxy), "Proxy cannot be null when UseProxy is true.");
}
private static void ValidateRequestPath(string requestPath)
{
if (string.IsNullOrWhiteSpace(requestPath))
throw new ArgumentNullException(nameof(requestPath), "The request path is required");
if (requestPath.Contains('?'))
throw new ArgumentException("Query parameters are not allowed in the request path", nameof(requestPath));
}
private HttpClient CreateHttpClient()
{
string version = GetType().Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion ?? "unknown";
HttpMessageHandler handler;
try
{
handler = new HttpClientHandler
{
AllowAutoRedirect = _clientOptions.AllowRedirects,
UseProxy = _clientOptions.UseProxy,
Proxy = _clientOptions.Proxy
};
}
catch (PlatformNotSupportedException)
{
handler = new HttpClientHandler
{
AllowAutoRedirect = _clientOptions.AllowRedirects
};
}
string baseUrl = _clientOptions.BaseUrl.Trim().TrimEnd('/');
var client = new HttpClient(handler, disposeHandler: true)
{
BaseAddress = new Uri($"{baseUrl}/"),
Timeout = _clientOptions.Timeout
};
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(nameof(LinkMobilityClient), version));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (_clientOptions.DefaultHeaders.Count > 0)
{
foreach (var headerKvp in _clientOptions.DefaultHeaders)
client.DefaultRequestHeaders.Add(headerKvp.Key, headerKvp.Value);
}
return client;
}
private async Task<TResponse> PostAsync<TResponse, TRequest>(string requestPath, TRequest? request, IQueryParameter? queryParams = null, CancellationToken cancellationToken = default)
{
ThrowIfDisposed();
ValidateRequestPath(requestPath);
string requestUrl = BuildRequestUrl(requestPath, queryParams);
var httpContent = ConvertRequest(request);
var response = await _httpClient.PostAsync(requestUrl, httpContent, cancellationToken).ConfigureAwait(false);
return await GetResponse<TResponse>(response, cancellationToken).ConfigureAwait(false);
}
private string BuildRequestUrl(string requestPath, IQueryParameter? queryParams = null)
{
string path = requestPath.Trim().TrimStart('/');
var param = new Dictionary<string, string>();
if (_clientOptions.DefaultQueryParams.Count > 0)
{
foreach (var kvp in _clientOptions.DefaultQueryParams)
param[kvp.Key] = kvp.Value;
}
var customQueryParams = queryParams?.GetQueryParameters();
if (customQueryParams?.Count > 0)
{
foreach (var kvp in customQueryParams)
param[kvp.Key] = kvp.Value;
}
if (param.Count == 0)
return path;
string queryString = string.Join("&", param.Select(kvp => $"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));
return $"{path}?{queryString}";
}
private static HttpContent? ConvertRequest<T>(T request)
{
if (request == null)
return null;
if (request is HttpContent httpContent)
return httpContent;
string json = JsonConvert.SerializeObject(request, _jsonSerializerSettings);
return new StringContent(json, Encoding.UTF8, "application/json");
}
private static async Task<TResponse> GetResponse<TResponse>(HttpResponseMessage httpResponse, CancellationToken cancellationToken)
{
#if NET6_0_OR_GREATER
string content = await httpResponse.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
string content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
#endif
return httpResponse.StatusCode switch
{
HttpStatusCode.Unauthorized => throw new AuthenticationException($"HTTP auth missing: {httpResponse.StatusCode}"),
HttpStatusCode.Forbidden => throw new AuthenticationException($"HTTP auth missing: {httpResponse.StatusCode}"),
HttpStatusCode.OK =>
JsonConvert.DeserializeObject<TResponse>(content, _jsonSerializerSettings)
?? throw new ApplicationException("Response could not be deserialized"),
_ => throw new ApplicationException($"Unknown HTTP response: {httpResponse.StatusCode}"),
};
}
private void ThrowIfDisposed()
{
if (_isDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
}
}

View File

@@ -0,0 +1,20 @@
namespace AMWD.Net.Api.LinkMobility
{
public class LinkMobilityResponse
{
[JsonProperty("clientMessageId")]
public string ClientMessageId { get; set; }
[JsonProperty("smsCount")]
public int SmsCount { get; set; }
[JsonProperty("statusCode")]
public StatusCodes StatusCode { get; set; }
[JsonProperty("statusMessage")]
public string StatusMessage { get; set; }
[JsonProperty("transferId")]
public string TransferId { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Represents options defined via query parameters.
/// </summary>
public interface IQueryParameter
{
/// <summary>
/// Retrieves the query parameters.
IReadOnlyDictionary<string, string> GetQueryParameters();
}
}

View File

@@ -0,0 +1,18 @@
# LinkMobility API
This project aims to implement the [LinkMobility REST API].
## Overview
Link Mobility is a provider for communication with customers via SMS, RCS or WhatsApp.
With this project the REST API of Link Mobility will be implemented.
- [SMS API](https://developer.linkmobility.eu/sms-api/rest-api)
---
Published under MIT License (see [**tl;dr**Legal])
[LinkMobility REST API]: https://developer.linkmobility.eu/
[**tl;dr**Legal]: https://www.tldrlegal.com/license/mit-license

View File

@@ -0,0 +1,139 @@
namespace AMWD.Net.Api.LinkMobility.Requests
{
/// <summary>
/// Request to send a text message to a list of recipients.
/// </summary>
public class SendTextMessageRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="SendTextMessageRequest"/> class.
/// </summary>
/// <param name="messageContent">The message.</param>
/// <param name="recipientAddressList">The recipient list.</param>
public SendTextMessageRequest(string messageContent, IReadOnlyCollection<string> recipientAddressList)
{
MessageContent = messageContent;
RecipientAddressList = recipientAddressList;
}
/// <summary>
/// <em>Optional</em>.
/// May contain a freely definable message id.
/// </summary>
[JsonProperty("clientMessageId")]
public string? ClientMessageId { get; set; }
/// <summary>
/// <em>Optional</em>.
/// The content category that is used to categorize the message (used for blacklisting).
/// </summary>
/// <remarks>
/// The following content categories are supported: <see cref="ContentCategory.Informational"/> or <see cref="ContentCategory.Advertisement"/>.
/// If no content category is provided, the default setting is used (may be changed inside the web interface).
/// </remarks>
[JsonProperty("contentCategory")]
public ContentCategory? ContentCategory { get; set; }
/// <summary>
/// <em>Optional</em>.
/// Specifies the maximum number of SMS to be generated.
/// </summary>
/// <remarks>
/// If the system generates more than this number of SMS, the status code <see cref="StatusCodes.MaxSmsPerMessageExceeded"/> is returned.
/// The default value of this parameter is <c>0</c>.
/// If set to <c>0</c>, no limitation is applied.
/// </remarks>
[JsonProperty("maxSmsPerMessage")]
public int? MaxSmsPerMessage { get; set; }
/// <summary>
/// <em>UTF-8</em> encoded message content.
/// </summary>
[JsonProperty("messageContent")]
public string MessageContent { get; set; }
/// <summary>
/// <em>Optional</em>.
/// Specifies the message type.
/// </summary>
/// <remarks>
/// Allowed values are <see cref="MessageType.Default"/> and <see cref="MessageType.Voice"/>.
/// When using the message type <see cref="MessageType.Default"/>, the outgoing message type is determined based on account settings.
/// Using the message type <see cref="MessageType.Voice"/> triggers a voice call.
/// </remarks>
[JsonProperty("messageType")]
public MessageType? MessageType { get; set; }
/// <summary>
/// <em>Optional</em>.
/// When setting a <c>NotificationCallbackUrl</c> all delivery reports are forwarded to this URL.
/// </summary>
[JsonProperty("notificationCallbackUrl")]
public string? NotificationCallbackUrl { get; set; }
/// <summary>
/// <em>Optional</em>.
/// Priority of the message.
/// </summary>
/// <remarks>
/// Must not exceed the value configured for the account used to send the message.
/// For more information please contact our customer service.
/// </remarks>
[JsonProperty("priority")]
public int? Priority { get; set; }
/// <summary>
/// List of recipients (E.164 formatted <see href="https://en.wikipedia.org/wiki/MSISDN">MSISDN</see>s)
/// to whom the message should be sent.
/// <br/>
/// The list of recipients may contain a maximum of <em>1000</em> entries.
/// </summary>
[JsonProperty("recipientAddressList")]
public IReadOnlyCollection<string> RecipientAddressList { get; set; }
/// <summary>
/// <em>Optional</em>.
/// <br/>
/// <see langword="true"/>: The message is sent as flash SMS (displayed directly on the screen of the mobile phone).
/// <br/>
/// <see langword="false"/>: The message is sent as standard text SMS (default).
/// </summary>
[JsonProperty("sendAsFlashSms")]
public bool? SendAsFlashSms { get; set; }
/// <summary>
/// <em>Optional</em>.
/// Address of the sender (assigned to the account) from which the message is sent.
/// </summary>
[JsonProperty("senderAddress")]
public string? SenderAddress { get; set; }
/// <summary>
/// <em>Optional</em>.
/// The sender address type.
/// </summary>
[JsonProperty("senderAddressType")]
public SenderAddressType? SenderAddressType { get; set; }
/// <summary>
/// <em>Optional</em>.
/// <br/>
/// <see langword="true"/>: The transmission is only simulated, no SMS is sent.
/// Depending on the number of recipients the status code <see cref="StatusCodes.Ok"/> or <see cref="StatusCodes.OkQueued"/> is returned.
/// <br/>
/// <see langword="false"/>: No simulation is done. The SMS is sent via the SMS Gateway. (default)
/// </summary>
[JsonProperty("test")]
public bool? Test { get; set; }
/// <summary>
/// <em>Optional</em>.
/// Specifies the validity periode (in seconds) in which the message is tried to be delivered to the recipient.
/// </summary>
/// <remarks>
/// A minimum of 1 minute (<c>60</c> seconds) and a maximum of 3 days (<c>259200</c> seconds) are allowed.
/// </remarks>
[JsonProperty("validityPeriode")]
public int? ValidityPeriode { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
namespace AMWD.Net.Api.LinkMobility
{
/// <summary>
/// Response of a text message sent to a list of recipients.
/// </summary>
public class SendTextMessageResponse
{
/// <summary>
/// Contains the message id defined in the request.
/// </summary>
[JsonProperty("clientMessageId")]
public string? ClientMessageId { get; set; }
/// <summary>
/// The actual number of generated SMS.
/// </summary>
[JsonProperty("smsCount")]
public int? SmsCount { get; set; }
/// <summary>
/// Status code.
/// </summary>
[JsonProperty("statusCode")]
public StatusCodes? StatusCode { get; set; }
/// <summary>
/// Description of the response status code.
/// </summary>
[JsonProperty("statusMessage")]
public string? StatusMessage { get; set; }
/// <summary>
/// Unique identifier that is set after successful processing of the request.
/// </summary>
[JsonProperty("transferId")]
public string? TransferId { get; set; }
}
}