From 7d4d81b8ef36145cfb8172ea318ff0c20fb6cef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tino=20Kr=C3=BCger?= Date: Wed, 7 Feb 2024 15:47:31 +0100 Subject: [PATCH] Added 360Dialog WhatsApp business api cloud client --- README.md | 31 +- .../Waba360DialogCloudController.cs | 212 ++++++++++++ .../WABA360Dialog.NET.Example.csproj | 10 +- .../AbstractWABA360DialogApiClient.cs | 136 ++++++++ .../ApiClient/Payloads/Base/ApiRequestBase.cs | 12 +- .../Base/Models/AbstractMessageObject.cs | 119 +++++++ .../Models/AbstractMessageObjectFactory.cs | 303 ++++++++++++++++++ .../InteractiveObjects/ActionObject.cs | 2 + .../MediaObjects/MediaObject.cs | 4 + .../Models/MessageObjects/MessageObject.cs | 117 +------ .../MessageObjects/MessageObjectFactory.cs | 241 ++------------ .../TemplateObjects/TemplateMessageObject.cs | 2 + .../Payloads/UpdateBussinessProfile.cs | 16 + .../ApiClient/WABA360DialogApiClientBase.cs | 125 +------- .../IWABA360DialogCloudApiClient.cs | 26 ++ .../Converters/MessagingProductConverter.cs | 49 +++ .../Payloads/Enums/MessagingProduct.cs | 12 + .../Models/MessageObjects/MessageObject.cs | 18 ++ .../MessageObjects/MessageObjectFactory.cs | 95 ++++++ .../WebhookObjects/WebhookNotification.cs | 17 + .../WebhookNotificationEntry.cs | 14 + .../WebhookNotificationEntryChange.cs | 13 + .../WebhookNotificationEntryChangeValue.cs | 41 +++ ...ookNotificationEntryChangeValueMetaData.cs | 13 + .../Requests/GetMediaInformationRequest.cs | 16 + .../Payloads/Requests/GetMediaRequest.cs | 32 ++ .../Payloads/Requests/GetWebhookUrlRequest.cs | 13 + .../Requests/MarkMessagesAsReadRequest.cs | 27 ++ .../Requests/SendMessageDynamicRequest.cs | 20 ++ .../Payloads/Requests/SendMessageRequest.cs | 21 ++ .../Payloads/Requests/SetWebhookUrlRequest.cs | 22 ++ .../Requests/UpdateBusinessProfileRequest.cs | 54 ++++ .../Payloads/Requests/UploadMediaRequest.cs | 36 +++ .../Responses/GetMediaInformationResponse.cs | 26 ++ .../Payloads/Responses/GetMediaResponse.cs | 7 + .../Responses/GetWebhookUrlResponse.cs | 14 + .../Responses/MarkMessagesAsReadResponse.cs | 7 + .../Payloads/Responses/SendMessageResponse.cs | 18 ++ .../Responses/SetWebhookUrlResponse.cs | 14 + .../UpdateBusinessProfileResponse.cs | 7 + .../Payloads/Responses/UploadMediaResponse.cs | 10 + .../Cloud/WABA360DialogCloudApiClient.cs | 109 +++++++ WABA360Dialog.NET/WABA360DialogApiClient.cs | 11 +- .../WABA360DialogPartnerClient.cs | 38 +-- .../WABA360DialogSandboxClient.cs | 12 +- global.json | 2 +- 46 files changed, 1641 insertions(+), 503 deletions(-) create mode 100644 WABA360Dialog.NET.Example/Controllers/Waba360DialogCloudController.cs create mode 100644 WABA360Dialog.NET/ApiClient/AbstractWABA360DialogApiClient.cs create mode 100644 WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObject.cs create mode 100644 WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObjectFactory.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Interfaces/IWABA360DialogCloudApiClient.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Converters/MessagingProductConverter.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Enums/MessagingProduct.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotification.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntry.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChange.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValue.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValueMetaData.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaInformationRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetWebhookUrlRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/MarkMessagesAsReadRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageDynamicRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SetWebhookUrlRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UpdateBusinessProfileRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UploadMediaRequest.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaInformationResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetWebhookUrlResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/MarkMessagesAsReadResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SendMessageResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SetWebhookUrlResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UpdateBusinessProfileResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UploadMediaResponse.cs create mode 100644 WABA360Dialog.NET/Cloud/WABA360DialogCloudApiClient.cs diff --git a/README.md b/README.md index 161161a..41ed8e5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # WABA360Dialog.NET -C# .NET API Wrapper Classes for 360Dialog WhatsApp Business APIs (On-Premises) and Partner APIs +C# .NET API Wrapper Classes for 360Dialog WhatsApp Business APIs (On-Premises and Cloud API) and Partner APIs - 360Dialog WhatsApp Business API (On-Premises) Client + - 360Dialog WhatsApp Business API (Cloud API) Client - 360Dialog WhatsApp Business API Sandbox Client - 360Dialog Partner API - Webhook Models @@ -9,7 +10,7 @@ C# .NET API Wrapper Classes for 360Dialog WhatsApp Business APIs (On-Premises) a ## Getting Started To get started, you must own an API Key managed by 360Dialog. -- **WABA360DialogApiClient.cs / WABA360DialogApiSandboxClient.cs** (WhatsApp API Business API) +- **WABA360DialogApiClient.cs / WABA360DialogCloudApiClient.cs / WABA360DialogApiSandboxClient.cs** (WhatsApp API Business API) ```c# var client = new WABA360DialogApiClient("your-api-key"); var createMessageResposne = await client.SendMessageAsync(MessageObjectFactory.CreateTextMessage("whatsapp-id", "Hello World!")); @@ -48,6 +49,23 @@ Task CheckPhoneNumberAsync(CancellationToken cancellat Task HealthCheckAsync(CancellationToken cancellationToken = default); ``` +- **WABA360DialogCloudApiClient.cs ** (WhatsApp API Business API (Cloud API)) +```c# +Task GetWebhookUrlAsync(CancellationToken cancellationToken = default); +Task SetWebhookUrlAsync(string url, Dictionary headers, CancellationToken cancellationToken = default); +Task SendMessageAsync(MessageObject message, CancellationToken cancellationToken = default); +Task SendMessageAsync(object message, CancellationToken cancellationToken = default); +Task MarkMessagesAsReadAsync(string messageId, CancellationToken cancellationToken = default); +Task GetMediaInformationAsync(string mediaId, CancellationToken cancellationToken = default); +Task GetMediaAsync(string relativeUrl, CancellationToken cancellationToken = default); +Task GetMediaAsync(string relativeUrl, Dictionary queryParams, CancellationToken cancellationToken = default); +Task UploadMediaAsync(string fileName, byte[] fileBytes, string contentType, CancellationToken cancellationToken = default); +Task GetTemplateAsync(int limit = 1000, int offset = 0, string sort = null, CancellationToken cancellationToken = default); +Task CreateTemplateAsync(CreateTemplateObject template, CancellationToken cancellationToken = default); +Task DeleteTemplateAsync(string templateName, CancellationToken cancellationToken = default); +Task UpdateBusinessProfileAsync(IEnumerable vertical, IEnumerable websites, string email, string description, string address, CancellationToken cancellationToken = default); +``` + - **WABA360DialogPartnerClient.cs** (360Dialog Partner API) ```c# Task CreatePartnerWhatsAppBusinessApiTemplateAsync(string whatsAppBusinessApiAccountId, string name, string category, WhatsAppLanguage language, TemplateComponentObject components, CancellationToken cancellationToken = default); @@ -99,6 +117,8 @@ The Enums definition, value, converters are located at these folders: ApiClient\Payloads\Converters\... ApiClient\Payloads\Enums\... +Cloud\ApiClient\Payloads\Enums + PartnerClient\Payloads\Converters\... PartnerClient\Payloads\Enums\... @@ -113,11 +133,16 @@ However, once Facebook updated the enums string value may cause issues during JS ## Webhook Models Webhook Models classes (Recommend using Newtonsoft.Json >= 13.01 for JSON deserialization): -- WhatsApp API Business API +- WhatsApp API Business API (On-Premises) ```c# ApiClient\Payloads\Models\WABA360DialogWebhookPayload.cs ``` +- WhatsApp API Business API (Cloud API) +```c# +Cloud\ApiClient\Payloads\Models\WebhookObjects\WebhookNotification.cs +``` + - 360Dialog Partner API ```c# PartnerClient\Payloads\Models\WABA360DialogPartnerWebhookPayload.cs diff --git a/WABA360Dialog.NET.Example/Controllers/Waba360DialogCloudController.cs b/WABA360Dialog.NET.Example/Controllers/Waba360DialogCloudController.cs new file mode 100644 index 0000000..23a5330 --- /dev/null +++ b/WABA360Dialog.NET.Example/Controllers/Waba360DialogCloudController.cs @@ -0,0 +1,212 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Serilog; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WABA360Dialog.Cloud; +using WABA360Dialog.Cloud.ApiClient.Interfaces; +using WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects; +using WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; +using WABA360Dialog.Common.Enums; +using ONPREM = WABA360Dialog.ApiClient.Payloads; + +namespace WABA360Dialog.NET.Example.Controllers +{ + [ApiController] + [Route("/waba/360dialog/[action]")] + public class Waba360DialogCloudController : ControllerBase + { + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + private readonly IWABA360DialogCloudApiClient _client; + + public Waba360DialogCloudController(ILogger logger, IConfiguration configuration, IHttpContextAccessor contextAccessor) + { + _logger = logger; + _configuration = configuration; + + var headerWabaApiKey = contextAccessor.HttpContext?.Request.Headers["360DialogChannelApiKey"]; + _client = new WABA360DialogCloudApiClient(!string.IsNullOrWhiteSpace(headerWabaApiKey) ? headerWabaApiKey : _configuration["WABA360Dialog:ChannelApiKey"]); + } + + [HttpPost] + public async Task Webhook() + { + var stream = new MemoryStream(); + await HttpContext.Request.Body.CopyToAsync(stream); + var byteArr = stream.ToArray(); + + var payloadString = Encoding.UTF8.GetString(byteArr, 0, byteArr.Length); + + Log.Information($"Webhook:{payloadString}"); + var notification = JsonConvert.DeserializeObject(payloadString); + var payload = notification?.Entry?.FirstOrDefault().Changes?.FirstOrDefault()?.Value; + Log.Information($"Contacts: {payload?.Contacts?.Count() ?? 0}, Messages: {payload?.Messages?.Count() ?? 0}."); + + return Ok(payload); + } + + [HttpPost] + public ActionResult WebhookObject([FromBody] WebhookNotification webhookPayload) + { + return Ok(webhookPayload); + } + + public record CheckContactRequest(string PhoneNumber); + + public record SendMessageRequest(string WhatsappId, string TextMessage); + + [HttpPost] + public async Task SendText([FromBody] SendMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateTextMessage(request.WhatsappId, request.TextMessage)); + } + + public record SendMediaMessageRequest(string WhatsappId, string Link, string Caption); + + [HttpPost] + public async Task SendImage([FromBody] SendMediaMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateImageMessageByLink(request.WhatsappId, request.Link, request.Caption)); + } + + public record SendDocumentMessageRequest(string WhatsappId, string Link, string Filename, string Caption); + + [HttpPost] + public async Task SendDocument([FromBody] SendDocumentMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateDocumentMessageByLink(request.WhatsappId, request.Filename, request.Link, request.Caption)); + } + + public record SendNoCaptionMediaMessageRequest(string WhatsappId, string Link); + + [HttpPost] + public async Task SendSticker([FromBody] SendNoCaptionMediaMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateStickerMessageByLink(request.WhatsappId, request.Link)); + } + + public record SendTemplateMessageRequest(string WhatsappId, string TemplateNamespace, string TemplateName, WhatsAppLanguage Language, List Components); + + [HttpPost] + public async Task SendTemplate([FromBody] SendTemplateMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateTemplateMessage(request.WhatsappId, request.TemplateNamespace, request.TemplateName, request.Language, request.Components)); + } + + public record SendInteractiveMessageRequest(string WhatsappId, ONPREM.Models.MessageObjects.InteractiveObjects.InteractiveObject InteractiveObject); + + [HttpPost] + public async Task SendInteractive([FromBody] SendInteractiveMessageRequest request) + { + return await _client.SendMessageAsync(ONPREM.Models.MessageObjects.MessageObjectFactory.CreateInteractiveMessage(request.WhatsappId, request.InteractiveObject)); + } + + public record SendLocationMessageRequest(string WhatsappId, double Latitude, double Longitude, string Name = null, string Address = null); + + [HttpPost] + public async Task SendLocation([FromBody] SendLocationMessageRequest request) + { + return await _client.SendMessageAsync(new MessageObject + { + RecipientType = "individual", + To = request.WhatsappId, + Type = ONPREM.Enums.MessageType.location, + Location = new ONPREM.Models.MessageObjects.LocationObjects.LocationObject() + { + Longitude = request.Longitude, + Latitude = request.Latitude, + Name = request.Name, + Address = request.Address, + } + }); + } + + public record SendContactRequest(string WhatsappId, ONPREM.Models.MessageObjects.ContactObjects.ContactObject ContactObject); + + [HttpPost] + public async Task SendContact([FromBody] SendContactRequest request) + { + return await _client.SendMessageAsync(new MessageObject + { + RecipientType = "individual", + To = request.WhatsappId, + Type = ONPREM.Enums.MessageType.contacts, + Contacts = request.ContactObject + }); + } + + public record UploadMediaRequest(IFormFile File); + + [HttpPost] + public async Task UploadMedia([FromForm] UploadMediaRequest request) + { + await using var ms = new MemoryStream(); + await request.File.CopyToAsync(ms); + + return await _client.UploadMediaAsync(request.File.FileName, ms.ToArray(), request.File.ContentType); + } + + public record MarkMessagesAsReadRequest(string MessageId); + [HttpPost] + public async Task MarkMessagesAsRead([FromForm] MarkMessagesAsReadRequest request) + { + return await _client.MarkMessagesAsReadAsync(request.MessageId); + } + + public record GetTemplatesRequest(int Limit = 1000, int Offset = 0); + [HttpPost] + public async Task GetTemplates([FromBody] GetTemplatesRequest request) + { + var response = await _client.GetTemplateAsync(request.Limit, request.Offset); + + return response; + } + + public record CreateTemplateExampleRequest(ONPREM.Models.MessageObjects.TemplateObjects.CreateTemplateObject TemplateObject); + [HttpPost] + public async Task CreateTemplate([FromBody] CreateTemplateExampleRequest request) + { + var response = await _client.CreateTemplateAsync(request.TemplateObject); + + return response; + } + + public record GetMediaByIdRequest(string MediaId); + [HttpPost] + public async Task GetMediaFile([FromBody] GetMediaByIdRequest request) + { + var mediaInformationResponse = await _client.GetMediaInformationAsync(request.MediaId); + var response = await _client.GetMediaAsync(mediaInformationResponse.Url); + + return File(response.FileBytes, response.ContentType.MediaType, response.ContentDisposition.FileName); + } + public record SetWabaWebhookRequest(string Url, string HeaderName, string HeaderValue); + + [HttpPost] + public async Task SetWebhook([FromBody] SetWabaWebhookRequest request) + { + var dict = new Dictionary + { + { + request.HeaderName, request.HeaderValue + } + }; + + return await _client.SetWebhookUrlAsync(request.Url, dict); + } + + [HttpPost] + public async Task GetWebhook() + { + return await _client.GetWebhookUrlAsync(); + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET.Example/WABA360Dialog.NET.Example.csproj b/WABA360Dialog.NET.Example/WABA360Dialog.NET.Example.csproj index 00ddb0e..e8f87c6 100644 --- a/WABA360Dialog.NET.Example/WABA360Dialog.NET.Example.csproj +++ b/WABA360Dialog.NET.Example/WABA360Dialog.NET.Example.csproj @@ -1,15 +1,15 @@ - + - net5.0 + net6.0 Linux - - + + - + diff --git a/WABA360Dialog.NET/ApiClient/AbstractWABA360DialogApiClient.cs b/WABA360Dialog.NET/ApiClient/AbstractWABA360DialogApiClient.cs new file mode 100644 index 0000000..e9409f0 --- /dev/null +++ b/WABA360Dialog.NET/ApiClient/AbstractWABA360DialogApiClient.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using WABA360Dialog.ApiClient.Exceptions; +using WABA360Dialog.ApiClient.Payloads.Base; +using WABA360Dialog.Common.Helpers; + +namespace WABA360Dialog.ApiClient +{ + public abstract class AbstractWABA360DialogApiClient : IDisposable + { + private readonly string _apiKey; + + protected AbstractWABA360DialogApiClient(string apiKey, HttpClient httpClient) + { + if (string.IsNullOrWhiteSpace(apiKey)) + throw new ArgumentNullException(nameof(apiKey), "API Key cannot be null."); + + _apiKey = apiKey; + HttpClient = httpClient; + } + + public abstract string BasePath { get; } + protected HttpClient HttpClient { get; } + + public void Dispose() + { + HttpClient.Dispose(); + } + + protected virtual async Task MakeHttpRequestAsync(ClientApiRequestBase request, CancellationToken cancellationToken = default) where TResponse : ClientApiResponseBase, new() + { + var requestPath = BasePath + request.MethodName; + var urlBuilder = new UriBuilder(requestPath); + + if (request.QueryParams != null) + { + var query = HttpUtility.ParseQueryString(string.Empty); + + foreach (var queryParam in request.QueryParams.Where(queryParam => queryParam.Value != null)) + query[queryParam.Key] = queryParam.Value; + + urlBuilder.Query = query.ToString(); + } + + var httpRequestMessage = new HttpRequestMessage(request.Method, urlBuilder.Uri); + + if (request.Method != HttpMethod.Get) + { + httpRequestMessage.Content = request.ToHttpContent(); + } + + httpRequestMessage.Headers.Add("D360-API-KEY", _apiKey); + + var httpResponse = await HttpClient.SendAsync(httpRequestMessage, cancellationToken); + + var responseAsString = await httpResponse.Content.ReadAsStringAsync(); + JsonHelper.TryDeserializeJson(responseAsString, out var response); + + if (!httpResponse.IsSuccessStatusCode || response == null) + { + if (!string.IsNullOrWhiteSpace(responseAsString)) + { + JsonHelper.TryDeserializeJson(responseAsString, out var errorResponse); + + if (errorResponse != null) + throw new ApiClientException(errorResponse.Error, errorResponse.Meta, urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); + } + + throw new ApiClientException(urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); + } + + response.ResponseBody = responseAsString; + + return response; + } + + protected virtual async Task MakeFileDownloadHttpRequestAsync(ClientApiRequestBase request, CancellationToken cancellationToken = default) where TResponse : BinaryApiResponseBase, new() + { + var requestPath = BasePath + request.MethodName; + var urlBuilder = new UriBuilder(requestPath); + + if (request.QueryParams != null) + { + var query = HttpUtility.ParseQueryString(string.Empty); + + foreach (var queryParam in request.QueryParams.Where(queryParam => queryParam.Value != null)) + query[queryParam.Key] = queryParam.Value; + + urlBuilder.Query = query.ToString(); + } + + var httpRequestMessage = new HttpRequestMessage(request.Method, urlBuilder.Uri); + + if (request.Method != HttpMethod.Get) + { + httpRequestMessage.Content = request.ToHttpContent(); + } + + httpRequestMessage.Headers.Add("D360-API-KEY", _apiKey); + httpRequestMessage.Headers.Add("User-Agent", "Mozilla/5.0"); + + var httpResponse = await HttpClient.SendAsync(httpRequestMessage, cancellationToken); + + if (!httpResponse.IsSuccessStatusCode) + { + var responseAsString = await httpResponse.Content.ReadAsStringAsync(); + + if (!string.IsNullOrWhiteSpace(responseAsString)) + { + JsonHelper.TryDeserializeJson(responseAsString, out var errorResponse); + + if (errorResponse != null) + throw new ApiClientException(errorResponse.Error, errorResponse.Meta, urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); + } + + throw new ApiClientException(urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); + } + + var responseAsByte = await httpResponse.Content.ReadAsByteArrayAsync(); + + var result = new TResponse + { + FileBytes = responseAsByte, + ContentType = httpResponse.Content.Headers.ContentType, + ContentDisposition = httpResponse.Content.Headers.ContentDisposition, + ContentLength = httpResponse.Content.Headers.ContentLength ?? 0, + }; + + return result; + } + } +} diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Base/ApiRequestBase.cs b/WABA360Dialog.NET/ApiClient/Payloads/Base/ApiRequestBase.cs index 88375f8..a00bdf6 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Base/ApiRequestBase.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Base/ApiRequestBase.cs @@ -13,6 +13,11 @@ namespace WABA360Dialog.ApiClient.Payloads.Base public abstract class ClientApiRequestBase : IRequest where TResponse : ClientApiResponseBase { + protected ClientApiRequestBase(HttpMethod method) + { + Method = method; + } + protected ClientApiRequestBase(string methodName, HttpMethod method) { MethodName = methodName; @@ -37,7 +42,12 @@ protected ClientApiRequestBase(string methodName, HttpMethod method, Dictionary< public virtual HttpContent ToHttpContent() { - var payload = JsonHelper.SerializeObjectToJson(this); + return ToHttpJsonContent(this); + } + + public virtual HttpContent ToHttpJsonContent(object model) + { + var payload = JsonHelper.SerializeObjectToJson(model); var content = new StringContent(payload, Encoding.UTF8, "application/json"); diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObject.cs b/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObject.cs new file mode 100644 index 0000000..e5d344f --- /dev/null +++ b/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObject.cs @@ -0,0 +1,119 @@ +using Newtonsoft.Json; +using WABA360Dialog.ApiClient.Payloads.Enums; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.ContactObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.InteractiveObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.LocationObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.MediaObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TemplateObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TextObjects; + +namespace WABA360Dialog.ApiClient.Payloads.Base.Models +{ + public abstract class AbstractMessageObject + { + /// + /// Optional. + /// The type of recipient the message is being sent to. + /// Supported value: individual + /// + [JsonProperty("recipient_type")] + public string RecipientType { get; set; } + + /// + /// Required. + /// The WhatsApp ID for the recipient of your message. Use the ID returned from the contacts endpoint. + /// + [JsonProperty("to")] + public string To { get; set; } + + /// + /// Optional for text messages. Required for all other message types. + /// The type of message you would like to send. + /// Supported values: + /// audio - Used in media messages. + /// contact - Used in contact messages. + /// document - Used in media messages. + /// image - Used in media messages. + /// location - Used in location messages. + /// sticker - Used in media messages. + /// template - Used to create message templates. + /// text - Default. Used on text messages. + /// video - Used in media messages. + /// interactive - Used in interactive messages. + /// hsm - This option will be deprecated when we launch the WhatsApp Business API v2.39. Use template instead. + /// + [JsonProperty("type")] + public MessageType Type { get; set; } + + /// + /// Required for messages of text type. + /// Contains a text object. + /// + [JsonProperty("text")] + public TextObject Text { get; set; } + + /// + /// Required when type is set to audio. + /// The media object containing audio. + /// + [JsonProperty("audio")] + public MediaObject Audio { get; set; } + + /// + /// Required when type is set to document. + /// The media object containing a document. + /// + [JsonProperty("document")] + public MediaObject Document { get; set; } + + /// + /// Required when type is set to image. + /// The media object containing an image. + /// + [JsonProperty("image")] + public MediaObject Image { get; set; } + + /// + /// Required when type is set to sticker. + /// The media object containing a sticker. + /// + [JsonProperty("sticker")] + public MediaObject Sticker { get; set; } + + /// + /// Required when type is set to video. + /// The media object containing a video. + /// + [JsonProperty("video")] + public MediaObject Video { get; set; } + + /// + /// Required when type is set to contacts. + /// Contains a contacts object. + /// + [JsonProperty("contacts")] + public ContactObject Contacts { get; set; } + + /// + /// Required when type is set to location. + /// Contains a location object. + /// + [JsonProperty("location")] + public LocationObject Location { get; set; } + + /// + /// Only used with message templates. + /// Contains a template object. + /// + [JsonProperty("template")] + public TemplateMessageObject Template { get; set; } + + /// + /// Required for interactive messages. + /// This object contains information about the message you would like to send. + /// The components of each interactive object generally follow a consistent pattern: header, body, footer, and action. See the interactive object below for more information. + /// + [JsonProperty("interactive")] + public InteractiveObject Interactive { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObjectFactory.cs b/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObjectFactory.cs new file mode 100644 index 0000000..8c276f4 --- /dev/null +++ b/WABA360Dialog.NET/ApiClient/Payloads/Base/Models/AbstractMessageObjectFactory.cs @@ -0,0 +1,303 @@ +using System.Collections.Generic; +using WABA360Dialog.ApiClient.Payloads.Enums; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.ContactObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.InteractiveObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.LocationObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.MediaObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TemplateObjects; +using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TextObjects; +using WABA360Dialog.Common.Enums; + +namespace WABA360Dialog.NET.ApiClient.Payloads.Base.Models +{ + public static class AbstractMessageObjectFactory + { + public static TMessageObject CreateTextMessage(string whatsAppId, string textMessage) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.text, + Text = new TextObject() + { + Body = textMessage + } + }; + } + + public static TMessageObject CreateImageMessageByMediaId(string whatsAppId, string mediaId, string caption) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.image, + Image = new MediaObject() + { + Id = mediaId, + Caption = caption + } + }; + } + + public static TMessageObject CreateImageMessageByLink(string whatsAppId, string imageLink, string caption, ProviderObject provider = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.image, + Image = new MediaObject() + { + Link = imageLink, + Caption = caption, + Provider = provider + } + }; + } + + public static TMessageObject CreateVideoMessageByMediaId(string whatsAppId, string mediaId, string caption) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.video, + Video = new MediaObject() + { + Id = mediaId, + Caption = caption + } + }; + } + + public static TMessageObject CreateVideoMessageByLink(string whatsAppId, string imageLink, string caption, ProviderObject provider = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.video, + Video = new MediaObject() + { + Link = imageLink, + Caption = caption, + Provider = provider + } + }; + } + + public static TMessageObject CreateDocumentMessageByMediaId(string whatsAppId, string fileName, string mediaId, string caption) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.document, + Document = new MediaObject() + { + Id = mediaId, + Filename = fileName, + Caption = caption, + } + }; + } + + public static TMessageObject CreateDocumentMessageByLink(string whatsAppId, string fileName, string documentLink, string caption, ProviderObject provider = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.document, + Document = new MediaObject() + { + Link = documentLink, + Caption = caption, + Filename = fileName, + Provider = provider, + } + }; + } + + public static TMessageObject CreateAudioMessageByMediaId(string whatsAppId, string mediaId) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.audio, + Audio = new MediaObject() + { + Id = mediaId, + } + }; + } + + public static TMessageObject CreateAudioMessageByLink(string whatsAppId, string audioLink, ProviderObject provider = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.audio, + Audio = new MediaObject() + { + Link = audioLink, + Provider = provider, + } + }; + } + + public static TMessageObject CreateStickerMessageByMediaId(string whatsAppId, string mediaId) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.sticker, + Sticker = new MediaObject() + { + Id = mediaId, + } + }; + } + + public static TMessageObject CreateStickerMessageByLink(string whatsAppId, string stickerLink, ProviderObject provider = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsAppId, + Type = MessageType.sticker, + Sticker = new MediaObject() + { + Link = stickerLink, + Provider = provider + } + }; + } + + public static TMessageObject CreateTemplateMessage(string whatsAppId, string templateNamespace, string templateName, WhatsAppLanguage language, List components) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + To = whatsAppId, + Type = MessageType.template, + Template = new TemplateMessageObject() + { + Namespace = templateNamespace, + Name = templateName, + Language = new LanguageObject() { Code = language, Policy = "deterministic" }, + Components = components, + } + }; + } + + public static TMessageObject CreateInteractiveMessage(string whatsappId, InteractiveObject interactiveObject) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsappId, + Type = MessageType.interactive, + Interactive = interactiveObject + }; + } + + public static TMessageObject CreateSingleProductMessage(string whatsappId, string bodyText, string footerText, string catalogId, string productRetailerId) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + var productMessageInteractiveObject = new InteractiveObject() + { + Type = InteractiveType.product, + }; + + if (!string.IsNullOrWhiteSpace(bodyText)) productMessageInteractiveObject.Body = new TextBodyObject() { Text = bodyText }; + if (!string.IsNullOrWhiteSpace(footerText)) productMessageInteractiveObject.Footer = new TextFooterObject() { Text = footerText }; + + productMessageInteractiveObject.Action = new ActionObject() + { + CatalogId = catalogId, + ProductRetailerId = productRetailerId + }; + + return new TMessageObject + { + RecipientType = "individual", + To = whatsappId, + Type = MessageType.interactive, + Interactive = productMessageInteractiveObject + }; + } + + public static TMessageObject CreateMultiProductMessage(string whatsappId, string bodyText, string footerText, IEnumerable productSections) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + var multiProductMessageInteractiveObject = new InteractiveObject() + { + Type = InteractiveType.product_list, + }; + + if (!string.IsNullOrWhiteSpace(bodyText)) multiProductMessageInteractiveObject.Body = new TextBodyObject() { Text = bodyText }; + if (!string.IsNullOrWhiteSpace(footerText)) multiProductMessageInteractiveObject.Footer = new TextFooterObject() { Text = footerText }; + + multiProductMessageInteractiveObject.Action = new ActionObject() + { + Sections = productSections + }; + + return new TMessageObject + { + RecipientType = "individual", + To = whatsappId, + Type = MessageType.interactive, + Interactive = multiProductMessageInteractiveObject + }; + } + + public static TMessageObject CreateLocationMessage(string whatsappId, double latitude, double longitude, string name = null, string address = null) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsappId, + Type = MessageType.location, + Location = new LocationObject() + { + Latitude = latitude, + Longitude = longitude, + Name = name, + Address = address, + } + }; + } + + public static TMessageObject CreateContactsMessage(string whatsappId, ContactObject contact) + where TMessageObject : WABA360Dialog.ApiClient.Payloads.Base.Models.AbstractMessageObject, new() + { + return new TMessageObject + { + RecipientType = "individual", + To = whatsappId, + Type = MessageType.contacts, + Contacts = contact + }; + } + } +} diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/InteractiveObjects/ActionObject.cs b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/InteractiveObjects/ActionObject.cs index a163b39..7f4832f 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/InteractiveObjects/ActionObject.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/InteractiveObjects/ActionObject.cs @@ -37,6 +37,8 @@ public class ActionObject /// /// Required for Single Product Messages and Multi-Product Messages. /// Unique identifier of the product in a catalog. + /// OnPrem: Maximum 100 characters for both Single Product and Multi-Product messages + /// Cloud: Maximum characters not specified /// [JsonProperty("product_retailer_id")] public string ProductRetailerId { get; set; } diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MediaObjects/MediaObject.cs b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MediaObjects/MediaObject.cs index 7f20ba6..253dbee 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MediaObjects/MediaObject.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MediaObjects/MediaObject.cs @@ -24,6 +24,8 @@ public class MediaObject /// Optional. /// Describes the specified document, image, or video media. /// Do not use with audio or sticker media. + /// OnPrem: Maximum 1024 characters + /// Cloud: Maximum characters not specified /// [JsonProperty("caption")] public string Caption { get; set; } @@ -38,6 +40,8 @@ public class MediaObject /// /// Optional. /// This path is optionally used with a link when the HTTP/HTTPS link is not directly accessible and requires additional configurations like a bearer token. + /// OnPrem: Only + /// Cloud: Not supported /// [JsonProperty("provider")] public ProviderObject Provider { get; set; } diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs index bdc2aff..796dabb 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs @@ -1,52 +1,12 @@ using Newtonsoft.Json; -using WABA360Dialog.ApiClient.Payloads.Enums; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.ContactObjects; +using WABA360Dialog.ApiClient.Payloads.Base.Models; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.HsmObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.InteractiveObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.LocationObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.MediaObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TemplateObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TextObjects; namespace WABA360Dialog.ApiClient.Payloads.Models.MessageObjects { [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] - public class MessageObject - { - /// - /// Optional. - /// The type of recipient the message is being sent to. - /// Supported value: individual - /// - [JsonProperty("recipient_type")] - public string RecipientType { get; set; } - - /// - /// Required. - /// The WhatsApp ID for the recipient of your message. Use the ID returned from the contacts endpoint. - /// - [JsonProperty("to")] - public string To { get; set; } - - /// - /// Optional for text messages. Required for all other message types. - /// The type of message you would like to send. - /// Supported values: - /// audio - Used in media messages. - /// contact - Used in contact messages. - /// document - Used in media messages. - /// image - Used in media messages. - /// location - Used in location messages. - /// sticker - Used in media messages. - /// template - Used to create message templates. - /// text - Default. Used on text messages. - /// video - Used in media messages. - /// interactive - Used in interactive messages. - /// hsm - This option will be deprecated when we launch the WhatsApp Business API v2.39. Use template instead. - /// - [JsonProperty("type")] - public MessageType Type { get; set; } - + public class MessageObject : AbstractMessageObject + { /// /// Only used with messages of text type. /// Allows for URL previews in text messages. For more information, see the Sending URLs in Text Messages. @@ -56,82 +16,11 @@ public class MessageObject [JsonProperty("preview_url")] public bool? PreviewUrl { get; set; } - /// - /// Required for messages of text type. - /// Contains a text object. - /// - [JsonProperty("text")] - public TextObject Text { get; set; } - - /// - /// Required when type is set to audio. - /// The media object containing audio. - /// - [JsonProperty("audio")] - public MediaObject Audio { get; set; } - - /// - /// Required when type is set to document. - /// The media object containing a document. - /// - [JsonProperty("document")] - public MediaObject Document { get; set; } - - /// - /// Required when type is set to image. - /// The media object containing an image. - /// - [JsonProperty("image")] - public MediaObject Image { get; set; } - - /// - /// Required when type is set to sticker. - /// The media object containing a sticker. - /// - [JsonProperty("sticker")] - public MediaObject Sticker { get; set; } - - /// - /// Required when type is set to video. - /// The media object containing a video. - /// - [JsonProperty("video")] - public MediaObject Video { get; set; } - - /// - /// Required when type is set to contacts. - /// Contains a contacts object. - /// - [JsonProperty("contacts")] - public ContactObject Contacts { get; set; } - - /// - /// Required when type is set to location. - /// Contains a location object. - /// - [JsonProperty("location")] - public LocationObject Location { get; set; } - - /// - /// Only used with message templates. - /// Contains a template object. - /// - [JsonProperty("template")] - public TemplateMessageObject Template { get; set; } - /// /// Only used with message templates. /// Contains an hsm object. This option will be deprecated when we launch the WhatsApp Business API v2.39. Use the template object instead. /// [JsonProperty("hsm")] public HsmObject Hsm { get; set; } - - /// - /// Required for interactive messages. - /// This object contains information about the message you would like to send. - /// The components of each interactive object generally follow a consistent pattern: header, body, footer, and action. See the interactive object below for more information. - /// - [JsonProperty("interactive")] - public InteractiveObject Interactive { get; set; } } } \ No newline at end of file diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs index 6b19ea4..359bfba 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs @@ -1,287 +1,100 @@ using System.Collections.Generic; -using WABA360Dialog.ApiClient.Payloads.Enums; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.ContactObjects; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.InteractiveObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.LocationObjects; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.MediaObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TemplateObjects; -using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TextObjects; using WABA360Dialog.Common.Enums; +using WABA360Dialog.NET.ApiClient.Payloads.Base.Models; namespace WABA360Dialog.ApiClient.Payloads.Models.MessageObjects { - public static class MessageObjectFactory + public static class MessageObjectFactory { public static MessageObject CreateTextMessage(string whatsAppId, string textMessage, bool previewUrl = false) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.text, - PreviewUrl = previewUrl, - Text = new TextObject() - { - Body = textMessage - } - }; + var messageObject = AbstractMessageObjectFactory.CreateTextMessage(whatsAppId, textMessage); + messageObject.PreviewUrl = previewUrl; + + return messageObject; } public static MessageObject CreateImageMessageByMediaId(string whatsAppId, string mediaId, string caption) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.image, - Image = new MediaObject() - { - Id = mediaId, - Caption = caption - } - }; + return AbstractMessageObjectFactory.CreateImageMessageByMediaId(whatsAppId, mediaId, caption); } public static MessageObject CreateImageMessageByLink(string whatsAppId, string imageLink, string caption, ProviderObject provider = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.image, - Image = new MediaObject() - { - Link = imageLink, - Caption = caption, - Provider = provider - } - }; + return AbstractMessageObjectFactory.CreateImageMessageByLink(whatsAppId, imageLink, caption, provider); } public static MessageObject CreateVideoMessageByMediaId(string whatsAppId, string mediaId, string caption) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.video, - Video = new MediaObject() - { - Id = mediaId, - Caption = caption - } - }; + return AbstractMessageObjectFactory.CreateVideoMessageByMediaId(whatsAppId, mediaId, caption); } public static MessageObject CreateVideoMessageByLink(string whatsAppId, string imageLink, string caption, ProviderObject provider = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.video, - Video = new MediaObject() - { - Link = imageLink, - Caption = caption, - Provider = provider - } - }; + return AbstractMessageObjectFactory.CreateVideoMessageByLink(whatsAppId, imageLink, caption, provider); } public static MessageObject CreateDocumentMessageByMediaId(string whatsAppId, string fileName, string mediaId, string caption) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.document, - Document = new MediaObject() - { - Id = mediaId, - Filename = fileName, - Caption = caption, - } - }; + return AbstractMessageObjectFactory.CreateDocumentMessageByMediaId(whatsAppId, fileName, mediaId, caption); } public static MessageObject CreateDocumentMessageByLink(string whatsAppId, string fileName, string documentLink, string caption, ProviderObject provider = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.document, - Document = new MediaObject() - { - Link = documentLink, - Caption = caption, - Filename = fileName, - Provider = provider, - } - }; + return AbstractMessageObjectFactory.CreateDocumentMessageByLink(whatsAppId, fileName, documentLink, caption, provider); } public static MessageObject CreateAudioMessageByMediaId(string whatsAppId, string mediaId) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.audio, - Audio = new MediaObject() - { - Id = mediaId, - } - }; + return AbstractMessageObjectFactory.CreateAudioMessageByMediaId(whatsAppId, mediaId); } public static MessageObject CreateAudioMessageByLink(string whatsAppId, string audioLink, ProviderObject provider = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.audio, - Audio = new MediaObject() - { - Link = audioLink, - Provider = provider, - } - }; + return AbstractMessageObjectFactory.CreateAudioMessageByLink(whatsAppId, audioLink, provider); } public static MessageObject CreateStickerMessageByMediaId(string whatsAppId, string mediaId) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.sticker, - Sticker = new MediaObject() - { - Id = mediaId, - } - }; + return AbstractMessageObjectFactory.CreateStickerMessageByMediaId(whatsAppId, mediaId); } public static MessageObject CreateStickerMessageByLink(string whatsAppId, string stickerLink, ProviderObject provider = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsAppId, - Type = MessageType.sticker, - Sticker = new MediaObject() - { - Link = stickerLink, - Provider = provider - } - }; + return AbstractMessageObjectFactory.CreateStickerMessageByLink(whatsAppId, stickerLink, provider); } public static MessageObject CreateTemplateMessage(string whatsAppId, string templateNamespace, string templateName, WhatsAppLanguage language, List components) { - return new MessageObject - { - To = whatsAppId, - Type = MessageType.template, - Template = new TemplateMessageObject() - { - Namespace = templateNamespace, - Name = templateName, - Language = new LanguageObject() { Code = language, Policy = "deterministic" }, - Components = components, - } - }; + return AbstractMessageObjectFactory.CreateTemplateMessage(whatsAppId, templateNamespace, templateName, language, components); } - public static MessageObject CreateInteractiveMessage(string whatsappId, InteractiveObject interactiveObject) + public static MessageObject CreateInteractiveMessage(string whatsAppId, InteractiveObject interactiveObject) { - return new MessageObject - { - RecipientType = "individual", - To = whatsappId, - Type = MessageType.interactive, - Interactive = interactiveObject - }; + return AbstractMessageObjectFactory.CreateInteractiveMessage(whatsAppId, interactiveObject); } - public static MessageObject CreateSingleProductMessage(string whatsappId, string bodyText, string footerText, string catalogId, string productRetailerId) + public static MessageObject CreateSingleProductMessage(string whatsAppId, string bodyText, string footerText, string catalogId, string productRetailerId) { - var productMessageInteractiveObject = new InteractiveObject() - { - Type = InteractiveType.product, - }; - - if (!string.IsNullOrWhiteSpace(bodyText)) productMessageInteractiveObject.Body = new TextBodyObject() { Text = bodyText }; - if (!string.IsNullOrWhiteSpace(footerText)) productMessageInteractiveObject.Footer = new TextFooterObject() { Text = footerText }; - - productMessageInteractiveObject.Action = new ActionObject() - { - CatalogId = catalogId, - ProductRetailerId = productRetailerId - }; - - return new MessageObject - { - RecipientType = "individual", - To = whatsappId, - Type = MessageType.interactive, - Interactive = productMessageInteractiveObject - }; + return AbstractMessageObjectFactory.CreateSingleProductMessage(whatsAppId, bodyText, footerText, catalogId, productRetailerId); } - public static MessageObject CreateMultiProductMessage(string whatsappId, string bodyText, string footerText, IEnumerable productSections) + public static MessageObject CreateMultiProductMessage(string whatsAppId, string bodyText, string footerText, IEnumerable productSections) { - var multiProductMessageInteractiveObject = new InteractiveObject() - { - Type = InteractiveType.product_list, - }; - - if (!string.IsNullOrWhiteSpace(bodyText)) multiProductMessageInteractiveObject.Body = new TextBodyObject() { Text = bodyText }; - if (!string.IsNullOrWhiteSpace(footerText)) multiProductMessageInteractiveObject.Footer = new TextFooterObject() { Text = footerText }; - - multiProductMessageInteractiveObject.Action = new ActionObject() - { - Sections = productSections - }; - - return new MessageObject - { - RecipientType = "individual", - To = whatsappId, - Type = MessageType.interactive, - Interactive = multiProductMessageInteractiveObject - }; + return AbstractMessageObjectFactory.CreateMultiProductMessage(whatsAppId, bodyText, footerText, productSections); } - public static MessageObject CreateLocationMessage(string whatsappId, double latitude, double longitude, string name = null, string address = null) + public static MessageObject CreateLocationMessage(string whatsAppId, double latitude, double longitude, string name = null, string address = null) { - return new MessageObject - { - RecipientType = "individual", - To = whatsappId, - Type = MessageType.location, - Location = new LocationObject() - { - Latitude = latitude, - Longitude = longitude, - Name = name, - Address = address, - } - }; + return AbstractMessageObjectFactory.CreateLocationMessage(whatsAppId, latitude, longitude, name, address); } - public static MessageObject CreateContactsMessage(string whatsappId, ContactObject contact) + public static MessageObject CreateContactsMessage(string whatsAppId, ContactObject contact) { - return new MessageObject - { - RecipientType = "individual", - To = whatsappId, - Type = MessageType.contacts, - Contacts = contact - }; + return AbstractMessageObjectFactory.CreateContactsMessage(whatsAppId, contact); } } } \ No newline at end of file diff --git a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/TemplateObjects/TemplateMessageObject.cs b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/TemplateObjects/TemplateMessageObject.cs index 99b654d..04ba247 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/TemplateObjects/TemplateMessageObject.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/Models/MessageObjects/TemplateObjects/TemplateMessageObject.cs @@ -11,6 +11,8 @@ public class TemplateMessageObject /// /// Required. /// Namespace of the template. + /// OnPrem: Optional since WABA v2.41 + /// Cloud: not used /// [JsonProperty("namespace")] public string Namespace { get; set; } diff --git a/WABA360Dialog.NET/ApiClient/Payloads/UpdateBussinessProfile.cs b/WABA360Dialog.NET/ApiClient/Payloads/UpdateBussinessProfile.cs index 0a01191..a993137 100644 --- a/WABA360Dialog.NET/ApiClient/Payloads/UpdateBussinessProfile.cs +++ b/WABA360Dialog.NET/ApiClient/Payloads/UpdateBussinessProfile.cs @@ -20,18 +20,34 @@ public UpdateBusinessProfileRequest(IEnumerable vertical, Address = address; } + /// + /// Industry of the business. + /// The business vertical cannot be set back to an empty value after it is created. + /// [JsonProperty("vertical")] public IEnumerable Vertical { get; set; } + /// + /// Maximum of 2 websites with a maximum of 256 characters each. + /// [JsonProperty("websites")] public IEnumerable Websites { get; set; } + /// + /// Maximum of 128 characters + /// [JsonProperty("email")] public string Email { get; set; } + /// + /// Maximum of 512 characters + /// [JsonProperty("description")] public string Description { get; set; } + /// + /// Maximum of 256 characters + /// [JsonProperty("address")] public string Address { get; set; } } diff --git a/WABA360Dialog.NET/ApiClient/WABA360DialogApiClientBase.cs b/WABA360Dialog.NET/ApiClient/WABA360DialogApiClientBase.cs index 2e643e1..4d9cace 100644 --- a/WABA360Dialog.NET/ApiClient/WABA360DialogApiClientBase.cs +++ b/WABA360Dialog.NET/ApiClient/WABA360DialogApiClientBase.cs @@ -1,35 +1,22 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using System.Web; -using WABA360Dialog.ApiClient.Exceptions; using WABA360Dialog.ApiClient.Interfaces; using WABA360Dialog.ApiClient.Payloads; -using WABA360Dialog.ApiClient.Payloads.Base; using WABA360Dialog.ApiClient.Payloads.Enums; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects; using WABA360Dialog.ApiClient.Payloads.Models.MessageObjects.TemplateObjects; -using WABA360Dialog.Common.Helpers; namespace WABA360Dialog.ApiClient { - public abstract class WABA360DialogApiClientBase : IWABA360DialogApiClient, IDisposable + public abstract class WABA360DialogApiClientBase : AbstractWABA360DialogApiClient, IWABA360DialogApiClient, IDisposable { - private string BasePath { get; } - private readonly string _apiKey; - protected readonly HttpClient HttpClient; - - protected WABA360DialogApiClientBase(string apiKey, string basePath, HttpClient httpClient) + public WABA360DialogApiClientBase(string apiKey, HttpClient httpClient) + : base(apiKey, httpClient) { - if (string.IsNullOrWhiteSpace(apiKey)) - throw new ArgumentNullException(nameof(apiKey), "API Key cannot be null."); - _apiKey = apiKey; - BasePath = basePath; - HttpClient = httpClient; } public async Task GetWebhookUrlAsync(CancellationToken cancellationToken = default) @@ -118,111 +105,5 @@ public async Task HealthCheckAsync(CancellationToken cancel { return await MakeHttpRequestAsync(new HealthCheckRequest(), cancellationToken); } - - protected virtual async Task MakeHttpRequestAsync(ClientApiRequestBase request, CancellationToken cancellationToken = default) where TResponse : ClientApiResponseBase, new() - { - var requestPath = BasePath + request.MethodName; - var urlBuilder = new UriBuilder(requestPath); - - if (request.QueryParams != null) - { - var query = HttpUtility.ParseQueryString(string.Empty); - - foreach (var queryParam in request.QueryParams.Where(queryParam => queryParam.Value != null)) - query[queryParam.Key] = queryParam.Value; - - urlBuilder.Query = query.ToString(); - } - - var httpRequestMessage = new HttpRequestMessage(request.Method, urlBuilder.Uri); - - if (request.Method != HttpMethod.Get) - { - httpRequestMessage.Content = request.ToHttpContent(); - } - - httpRequestMessage.Headers.Add("D360-API-KEY", _apiKey); - - var httpResponse = await HttpClient.SendAsync(httpRequestMessage, cancellationToken); - - var responseAsString = await httpResponse.Content.ReadAsStringAsync(); - JsonHelper.TryDeserializeJson(responseAsString, out var response); - - if (!httpResponse.IsSuccessStatusCode || response == null) - { - if (!string.IsNullOrWhiteSpace(responseAsString)) - { - JsonHelper.TryDeserializeJson(responseAsString, out var errorResponse); - - if (errorResponse != null) - throw new ApiClientException(errorResponse.Error, errorResponse.Meta, urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); - } - - throw new ApiClientException(urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); - } - - response.ResponseBody = responseAsString; - - return response; - } - - protected virtual async Task MakeFileDownloadHttpRequestAsync(ClientApiRequestBase request, CancellationToken cancellationToken = default) where TResponse : BinaryApiResponseBase, new() - { - var requestPath = BasePath + request.MethodName; - var urlBuilder = new UriBuilder(requestPath); - - if (request.QueryParams != null) - { - var query = HttpUtility.ParseQueryString(string.Empty); - - foreach (var queryParam in request.QueryParams.Where(queryParam => queryParam.Value != null)) - query[queryParam.Key] = queryParam.Value; - - urlBuilder.Query = query.ToString(); - } - - var httpRequestMessage = new HttpRequestMessage(request.Method, urlBuilder.Uri); - - if (request.Method != HttpMethod.Get) - { - httpRequestMessage.Content = request.ToHttpContent(); - } - - httpRequestMessage.Headers.Add("D360-API-KEY", _apiKey); - - var httpResponse = await HttpClient.SendAsync(httpRequestMessage, cancellationToken); - - if (!httpResponse.IsSuccessStatusCode) - { - var responseAsString = await httpResponse.Content.ReadAsStringAsync(); - - if (!string.IsNullOrWhiteSpace(responseAsString)) - { - JsonHelper.TryDeserializeJson(responseAsString, out var errorResponse); - - if (errorResponse != null) - throw new ApiClientException(errorResponse.Error, errorResponse.Meta, urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); - } - - throw new ApiClientException(urlBuilder.ToString(), (int)httpResponse.StatusCode, await request.ToHttpContent().ReadAsStringAsync(), responseAsString); - } - - var responseAsByte = await httpResponse.Content.ReadAsByteArrayAsync(); - - var result = new TResponse - { - FileBytes = responseAsByte, - ContentType = httpResponse.Content.Headers.ContentType, - ContentDisposition = httpResponse.Content.Headers.ContentDisposition, - ContentLength = httpResponse.Content.Headers.ContentLength ?? 0, - }; - - return result; - } - - public void Dispose() - { - HttpClient.Dispose(); - } } } \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Interfaces/IWABA360DialogCloudApiClient.cs b/WABA360Dialog.NET/Cloud/ApiClient/Interfaces/IWABA360DialogCloudApiClient.cs new file mode 100644 index 0000000..8276db5 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Interfaces/IWABA360DialogCloudApiClient.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; +using ONPREM = WABA360Dialog.ApiClient.Payloads; + +namespace WABA360Dialog.Cloud.ApiClient.Interfaces +{ + public interface IWABA360DialogCloudApiClient + { + Task GetWebhookUrlAsync(CancellationToken cancellationToken = default); + Task SetWebhookUrlAsync(string url, Dictionary headers, CancellationToken cancellationToken = default); + Task SendMessageAsync(MessageObject message, CancellationToken cancellationToken = default); + Task SendMessageAsync(object message, CancellationToken cancellationToken = default); + Task MarkMessagesAsReadAsync(string messageId, CancellationToken cancellationToken = default); + Task GetMediaInformationAsync(string mediaId, CancellationToken cancellationToken = default); + Task GetMediaAsync(string relativeUrl, CancellationToken cancellationToken = default); + Task GetMediaAsync(string relativeUrl, Dictionary queryParams, CancellationToken cancellationToken = default); + Task UploadMediaAsync(string fileName, byte[] fileBytes, string contentType, CancellationToken cancellationToken = default); + Task GetTemplateAsync(int limit = 1000, int offset = 0, string sort = null, CancellationToken cancellationToken = default); + Task CreateTemplateAsync(ONPREM.Models.MessageObjects.TemplateObjects.CreateTemplateObject template, CancellationToken cancellationToken = default); + Task DeleteTemplateAsync(string templateName, CancellationToken cancellationToken = default); + Task UpdateBusinessProfileAsync(IEnumerable vertical, IEnumerable websites, string email, string description, string address, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Converters/MessagingProductConverter.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Converters/MessagingProductConverter.cs new file mode 100644 index 0000000..45889c8 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Converters/MessagingProductConverter.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using WABA360Dialog.Cloud.ApiClient.Payloads.Enums; +using WABA360Dialog.Common.Converters.Base; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Converters +{ + internal class MessagingProductConverter : EnumConverter + { + protected override MessagingProduct GetEnumValue(string value) + { + return EnumStringConverter.GetMessagingProduct(value); + } + + protected override string GetStringValue(MessagingProduct value) + { + return value.GetString(); + } + } + public static partial class EnumStringConverter + { + private static readonly IReadOnlyDictionary MessagingProductStringToEnum = + new Dictionary + { + { "unknown", MessagingProduct.unknown }, + { "whatsapp", MessagingProduct.whatsapp }, + }; + + private static readonly IReadOnlyDictionary MessagingProductEnumToString = + new Dictionary + { + { MessagingProduct.unknown, "unknown" }, + { MessagingProduct.whatsapp, "whatsapp" } + }; + + public static string GetString(this MessagingProduct status) + { + return MessagingProductEnumToString.TryGetValue(status, out var stringValue) + ? stringValue + : "unknown"; + } + + public static MessagingProduct GetMessagingProduct(string status) + { + return MessagingProductStringToEnum.TryGetValue(status, out var enumValue) + ? enumValue + : 0; + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Enums/MessagingProduct.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Enums/MessagingProduct.cs new file mode 100644 index 0000000..f617c53 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Enums/MessagingProduct.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using WABA360Dialog.Cloud.ApiClient.Payloads.Converters; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Enums +{ + [JsonConverter(typeof(MessagingProductConverter))] + public enum MessagingProduct + { + unknown = 0, + whatsapp = 1 + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs new file mode 100644 index 0000000..adff32b --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObject.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using WABA360Dialog.ApiClient.Payloads.Base.Models; +using WABA360Dialog.Cloud.ApiClient.Payloads.Enums; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects +{ + [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] + public class MessageObject : AbstractMessageObject + { + public MessageObject() + { + RecipientType = "individual"; + } + + [JsonProperty("messaging_product")] + public MessagingProduct MessagingProduct { get; set; } = MessagingProduct.whatsapp; + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs new file mode 100644 index 0000000..80eb5d9 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/MessageObjects/MessageObjectFactory.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using WABA360Dialog.Common.Enums; +using WABA360Dialog.NET.ApiClient.Payloads.Base.Models; +using ONPREM = WABA360Dialog.ApiClient.Payloads.Models.MessageObjects; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects +{ + public static class MessageObjectFactory + { + public static MessageObject CreateTextMessage(string whatsAppId, string textMessage) + { + return AbstractMessageObjectFactory.CreateTextMessage(whatsAppId, textMessage); + } + + public static MessageObject CreateImageMessageByMediaId(string whatsAppId, string mediaId, string caption) + { + return AbstractMessageObjectFactory.CreateImageMessageByMediaId(whatsAppId, mediaId, caption); + } + + public static MessageObject CreateImageMessageByLink(string whatsAppId, string imageLink, string caption, ONPREM.MediaObjects.ProviderObject provider = null) + { + return AbstractMessageObjectFactory.CreateImageMessageByLink(whatsAppId, imageLink, caption, provider); + } + + public static MessageObject CreateVideoMessageByMediaId(string whatsAppId, string mediaId, string caption) + { + return AbstractMessageObjectFactory.CreateVideoMessageByMediaId(whatsAppId, mediaId, caption); + } + + public static MessageObject CreateVideoMessageByLink(string whatsAppId, string imageLink, string caption, ONPREM.MediaObjects.ProviderObject provider = null) + { + return AbstractMessageObjectFactory.CreateVideoMessageByLink(whatsAppId, imageLink, caption, provider); + } + + public static MessageObject CreateDocumentMessageByMediaId(string whatsAppId, string fileName, string mediaId, string caption) + { + return AbstractMessageObjectFactory.CreateDocumentMessageByMediaId(whatsAppId, fileName, mediaId, caption); + } + + public static MessageObject CreateDocumentMessageByLink(string whatsAppId, string fileName, string documentLink, string caption, ONPREM.MediaObjects.ProviderObject provider = null) + { + return AbstractMessageObjectFactory.CreateDocumentMessageByLink(whatsAppId, fileName, documentLink, caption, provider); + } + + public static MessageObject CreateAudioMessageByMediaId(string whatsAppId, string mediaId) + { + return AbstractMessageObjectFactory.CreateAudioMessageByMediaId(whatsAppId, mediaId); + } + + public static MessageObject CreateAudioMessageByLink(string whatsAppId, string audioLink, ONPREM.MediaObjects.ProviderObject provider = null) + { + return AbstractMessageObjectFactory.CreateAudioMessageByLink(whatsAppId, audioLink, provider); + } + + public static MessageObject CreateStickerMessageByMediaId(string whatsAppId, string mediaId) + { + return AbstractMessageObjectFactory.CreateStickerMessageByMediaId(whatsAppId, mediaId); + } + + public static MessageObject CreateStickerMessageByLink(string whatsAppId, string stickerLink, ONPREM.MediaObjects.ProviderObject provider = null) + { + return AbstractMessageObjectFactory.CreateStickerMessageByLink(whatsAppId, stickerLink, provider); + } + + public static MessageObject CreateTemplateMessage(string whatsAppId, string templateNamespace, string templateName, WhatsAppLanguage language, List components) + { + return AbstractMessageObjectFactory.CreateTemplateMessage(whatsAppId, templateNamespace, templateName, language, components); + } + + public static MessageObject CreateInteractiveMessage(string whatsAppId, ONPREM.InteractiveObjects.InteractiveObject interactiveObject) + { + return AbstractMessageObjectFactory.CreateInteractiveMessage(whatsAppId, interactiveObject); + } + + public static MessageObject CreateSingleProductMessage(string whatsAppId, string bodyText, string footerText, string catalogId, string productRetailerId) + { + return AbstractMessageObjectFactory.CreateSingleProductMessage(whatsAppId, bodyText, footerText, catalogId, productRetailerId); + } + + public static MessageObject CreateMultiProductMessage(string whatsAppId, string bodyText, string footerText, IEnumerable productSections) + { + return AbstractMessageObjectFactory.CreateMultiProductMessage(whatsAppId, bodyText, footerText, productSections); + } + + public static MessageObject CreateLocationMessage(string whatsAppId, double latitude, double longitude, string name = null, string address = null) + { + return AbstractMessageObjectFactory.CreateLocationMessage(whatsAppId, latitude, longitude, name, address); + } + + public static MessageObject CreateContactsMessage(string whatsAppId, ONPREM.ContactObjects.ContactObject contact) + { + return AbstractMessageObjectFactory.CreateContactsMessage(whatsAppId, contact); + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotification.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotification.cs new file mode 100644 index 0000000..b5cbed3 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotification.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects +{ + /// + /// https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/payload-examples#user-changed-number-notification + /// + public class WebhookNotification + { + [JsonProperty("object")] + public string Object { get; set; } + + [JsonProperty("entry")] + public IEnumerable Entry { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntry.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntry.cs new file mode 100644 index 0000000..4a3a7b0 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntry.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects +{ + public class WebhookNotificationEntry + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("changes")] + public IEnumerable Changes { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChange.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChange.cs new file mode 100644 index 0000000..9e5ef2c --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChange.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects +{ + public class WebhookNotificationEntryChange + { + [JsonProperty("field")] + public string Field { get; set; } + + [JsonProperty("value")] + public WebhookNotificationEntryChangeValue Value { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValue.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValue.cs new file mode 100644 index 0000000..15a6aa1 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValue.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using ONPREM = WABA360Dialog.ApiClient.Payloads.Models; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects +{ + public class WebhookNotificationEntryChangeValue + { + [JsonProperty("messaging_product")] + public string MessagingProduct { get; set; } + + [JsonProperty("metadata")] + public WebhookNotificationEntryChangeValueMetaData Metadata { get; set; } + + /// + /// Provides all the information about the contact —see contacts object. + /// This object only applies to text, contacts, and location messages. + /// It is not currently supported for media messages and is not applicable for system messages. + /// + [JsonProperty("contacts")] + public IEnumerable Contacts { get; set; } + + /// + /// Webhook notifications of received messages are contained within a messages object. + /// + [JsonProperty("messages")] + public IEnumerable Messages { get; set; } + + /// + /// The statuses object keeps you apprised of the status of messages between you and users or groups. + /// + [JsonProperty("statuses")] + public IEnumerable Statuses { get; set; } + + /// + /// When there are any out-of-band errors that occur in the normal operation of the application, the errors array provides a description of the error. + /// + [JsonProperty("errors")] + public IEnumerable Errors { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValueMetaData.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValueMetaData.cs new file mode 100644 index 0000000..6bd7f32 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Models/WebhookObjects/WebhookNotificationEntryChangeValueMetaData.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Models.WebhookObjects +{ + public class WebhookNotificationEntryChangeValueMetaData + { + [JsonProperty("display_phone_number")] + public string DisplayPhoneNumber { get; set; } + + [JsonProperty("phone_number_id")] + public string PhoneNumberId { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaInformationRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaInformationRequest.cs new file mode 100644 index 0000000..a670d04 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaInformationRequest.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class GetMediaInformationRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public GetMediaInformationRequest(string mediaId) : base(mediaId, HttpMethod.Get) + { + MediaId = mediaId; + } + + [JsonIgnore] + public string MediaId { get; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaRequest.cs new file mode 100644 index 0000000..fb5c55d --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetMediaRequest.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class GetMediaRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public GetMediaRequest(string relativeUrlPath, Dictionary queryParams) : base(relativeUrlPath, HttpMethod.Get, queryParams) + { + + } + + public GetMediaRequest(string relativeUrlPath) : base(HttpMethod.Get) + { + var urlParts = relativeUrlPath.Split(new[] { '?', '&' }, System.StringSplitOptions.RemoveEmptyEntries); + MethodName = urlParts[0]; + + if (urlParts.Length > 0) + { + var queryParams = new Dictionary(); + for (var index = 1; index < urlParts.Length; index++) + { + var parameter = urlParts[index].Split('='); + queryParams.Add(parameter[0], parameter[1]); + } + + QueryParams = queryParams; + } + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetWebhookUrlRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetWebhookUrlRequest.cs new file mode 100644 index 0000000..17f1656 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/GetWebhookUrlRequest.cs @@ -0,0 +1,13 @@ +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class GetWebhookUrlRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public GetWebhookUrlRequest() : base("waba_webhook", HttpMethod.Get) + { + + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/MarkMessagesAsReadRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/MarkMessagesAsReadRequest.cs new file mode 100644 index 0000000..7114218 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/MarkMessagesAsReadRequest.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + internal class MarkMessagesAsReadRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public MarkMessagesAsReadRequest(string messageId) : base($"messages/{messageId}", HttpMethod.Put) + { + MessageId = messageId; + } + + public string MessageId { get; } + + public override HttpContent ToHttpContent() + { + return ToHttpJsonContent(new MarkMessageAsReadRequestBody { Status = "read" }); + } + + private class MarkMessageAsReadRequestBody + { + [JsonProperty("status")] + public string Status { get; set; } + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageDynamicRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageDynamicRequest.cs new file mode 100644 index 0000000..471929d --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageDynamicRequest.cs @@ -0,0 +1,20 @@ +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + internal class SendMessageDynamicRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public SendMessageDynamicRequest(object message) : base("messages", HttpMethod.Post) + { + Message = message; + } + + public object Message { get; } + + public override HttpContent ToHttpContent() + { + return ToHttpJsonContent(Message); + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageRequest.cs new file mode 100644 index 0000000..bef44e4 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SendMessageRequest.cs @@ -0,0 +1,21 @@ +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class SendMessageRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public SendMessageRequest(MessageObject message) : base("messages", HttpMethod.Post) + { + Message = message; + } + + public MessageObject Message { get; } + + public override HttpContent ToHttpContent() + { + return ToHttpJsonContent(Message); + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SetWebhookUrlRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SetWebhookUrlRequest.cs new file mode 100644 index 0000000..cce4c05 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/SetWebhookUrlRequest.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class SetWebhookUrlRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public SetWebhookUrlRequest(string url, Dictionary headers) : base("waba_webhook", HttpMethod.Post) + { + Url = url; + Headers = headers; + } + + [JsonProperty("url")] + public string Url { get; } + + [JsonProperty("headers")] + public Dictionary Headers { get; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UpdateBusinessProfileRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UpdateBusinessProfileRequest.cs new file mode 100644 index 0000000..e4b854a --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UpdateBusinessProfileRequest.cs @@ -0,0 +1,54 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net.Http; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class UpdateBusinessProfileRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public UpdateBusinessProfileRequest(IEnumerable vertical, + IEnumerable websites, + string email, + string description, + string address) : base("whatsapp_business_profile", HttpMethod.Post) + { + Vertical = vertical; + Websites = websites; + Email = email; + Description = description; + Address = address; + } + + /// + /// Industry of the business. + /// The business vertical cannot be set back to an empty value after it is created. + /// + [JsonProperty("vertical")] + public IEnumerable Vertical { get; set; } + + /// + /// Maximum of 2 websites with a maximum of 256 characters each. + /// + [JsonProperty("websites")] + public IEnumerable Websites { get; set; } + + /// + /// Maximum of 128 characters + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Maximum of 512 characters + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// Maximum of 256 characters + /// + [JsonProperty("address")] + public string Address { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UploadMediaRequest.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UploadMediaRequest.cs new file mode 100644 index 0000000..f13bae4 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Requests/UploadMediaRequest.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using WABA360Dialog.Cloud.ApiClient.Payloads.Enums; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Requests +{ + public class UploadMediaRequest : WABA360Dialog.ApiClient.Payloads.Base.ClientApiRequestBase + { + public UploadMediaRequest(string fileName, byte[] fileBytes, string contentType) : base("media", HttpMethod.Post) + { + FileName = fileName; + FileBytes = fileBytes; + ContentType = contentType; + } + + public string FileName { get; } + public byte[] FileBytes { get; } + public string ContentType { get; } + + public override HttpContent ToHttpContent() + { + var fileContent = new ByteArrayContent(FileBytes); + fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType); + + var content = new MultipartFormDataContent + { + { new StringContent(MessagingProduct.whatsapp.ToString()), "messaging_product" }, + { fileContent, "file", FileName } + }; + + return content; + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaInformationResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaInformationResponse.cs new file mode 100644 index 0000000..fe2622d --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaInformationResponse.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using WABA360Dialog.Cloud.ApiClient.Payloads.Enums; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class GetMediaInformationResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("mime_type")] + public string MimeType { get; set; } + + [JsonProperty("sha256")] + public string Sha256 { get; set; } + + [JsonProperty("file_size")] + public int FileSize { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("messaging_product")] + public MessagingProduct MessagingProduct { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaResponse.cs new file mode 100644 index 0000000..e551ba7 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetMediaResponse.cs @@ -0,0 +1,7 @@ +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class GetMediaResponse : WABA360Dialog.ApiClient.Payloads.Base.BinaryApiResponseBase + { + + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetWebhookUrlResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetWebhookUrlResponse.cs new file mode 100644 index 0000000..4968fad --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/GetWebhookUrlResponse.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class GetWebhookUrlResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("headers")] + public Dictionary Headers { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/MarkMessagesAsReadResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/MarkMessagesAsReadResponse.cs new file mode 100644 index 0000000..8aba708 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/MarkMessagesAsReadResponse.cs @@ -0,0 +1,7 @@ +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class MarkMessagesAsReadResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SendMessageResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SendMessageResponse.cs new file mode 100644 index 0000000..3bc0769 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SendMessageResponse.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using WABA360Dialog.Cloud.ApiClient.Payloads.Enums; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class SendMessageResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + [JsonProperty("contacts")] + public IEnumerable Contacts { get; set; } + + [JsonProperty("messages")] + public IEnumerable CreatedMessages { get; set; } + + [JsonProperty("messaging_product")] + public MessagingProduct MessagingProduct { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SetWebhookUrlResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SetWebhookUrlResponse.cs new file mode 100644 index 0000000..1f5fd93 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/SetWebhookUrlResponse.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class SetWebhookUrlResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("headers")] + public Dictionary Headers { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UpdateBusinessProfileResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UpdateBusinessProfileResponse.cs new file mode 100644 index 0000000..8af6c15 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UpdateBusinessProfileResponse.cs @@ -0,0 +1,7 @@ +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class UpdateBusinessProfileResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UploadMediaResponse.cs b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UploadMediaResponse.cs new file mode 100644 index 0000000..939445c --- /dev/null +++ b/WABA360Dialog.NET/Cloud/ApiClient/Payloads/Responses/UploadMediaResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace WABA360Dialog.Cloud.ApiClient.Payloads.Responses +{ + public class UploadMediaResponse : WABA360Dialog.ApiClient.Payloads.Base.ClientApiResponseBase + { + [JsonProperty("id")] + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/Cloud/WABA360DialogCloudApiClient.cs b/WABA360Dialog.NET/Cloud/WABA360DialogCloudApiClient.cs new file mode 100644 index 0000000..50ba7b2 --- /dev/null +++ b/WABA360Dialog.NET/Cloud/WABA360DialogCloudApiClient.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using WABA360Dialog.ApiClient; +using WABA360Dialog.Cloud.ApiClient.Interfaces; +using WABA360Dialog.Cloud.ApiClient.Payloads.Models.MessageObjects; +using WABA360Dialog.Cloud.ApiClient.Payloads.Requests; +using WABA360Dialog.Cloud.ApiClient.Payloads.Responses; +using ONPREM = WABA360Dialog.ApiClient.Payloads; + +namespace WABA360Dialog.Cloud +{ + public class WABA360DialogCloudApiClient : AbstractWABA360DialogApiClient, IWABA360DialogCloudApiClient + { + public const string BASEURL = "https://waba-v2.360dialog.io/"; + + public WABA360DialogCloudApiClient(string apiKey) : base(apiKey, new HttpClient()) + { + + } + + public WABA360DialogCloudApiClient(string apiKey, HttpClient httpClient) : base(apiKey, httpClient) + { + + } + + public override string BasePath => BASEURL; + + public async Task GetWebhookUrlAsync(CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new GetWebhookUrlRequest(), cancellationToken); + } + + public async Task SetWebhookUrlAsync(string url, Dictionary headers, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new SetWebhookUrlRequest(url, headers), cancellationToken); + } + + public async Task SendMessageAsync(MessageObject message, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new SendMessageRequest(message), cancellationToken); + } + + public async Task SendMessageAsync(object message, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new SendMessageDynamicRequest(message), cancellationToken); + } + public async Task MarkMessagesAsReadAsync(string messageId, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new MarkMessagesAsReadRequest(messageId), cancellationToken); + } + + public async Task GetMediaInformationAsync(string mediaId, CancellationToken cancellationToken = default) + { + var mediaResult = await MakeHttpRequestAsync(new GetMediaInformationRequest(mediaId), cancellationToken); + if (mediaResult.Id is null || mediaResult.Id == String.Empty) + { + return mediaResult; + } + + mediaResult.Url = mediaResult.Url.Replace("https://lookaside.fbsbx.com/", ""); + + return mediaResult; + } + + public async Task GetMediaAsync(string relativeUrl, CancellationToken cancellationToken = default) + { + return await MakeFileDownloadHttpRequestAsync(new GetMediaRequest(relativeUrl), cancellationToken); + } + + public async Task GetMediaAsync(string relativeUrl, Dictionary queryParams, CancellationToken cancellationToken = default) + { + return await MakeFileDownloadHttpRequestAsync(new GetMediaRequest(relativeUrl, queryParams), cancellationToken); + } + + public async Task UploadMediaAsync(string fileName, byte[] fileBytes, string contentType, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new UploadMediaRequest(fileName, fileBytes, contentType), cancellationToken); + } + + public async Task GetTemplateAsync(int limit = 1000, int offset = 0, string sort = null, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new ONPREM.GetTemplateRequest(limit, offset, sort), cancellationToken); + } + + public async Task CreateTemplateAsync(ONPREM.Models.MessageObjects.TemplateObjects.CreateTemplateObject template, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new ONPREM.CreateTemplateRequest(template), cancellationToken); + } + + public async Task DeleteTemplateAsync(string templateName, CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new ONPREM.DeleteTemplateRequest(templateName), cancellationToken); + } + + public async Task UpdateBusinessProfileAsync( + IEnumerable vertical, + IEnumerable websites, + string email, + string description, + string address, + CancellationToken cancellationToken = default) + { + return await MakeHttpRequestAsync(new UpdateBusinessProfileRequest(vertical, websites, email, description, address), cancellationToken); + } + } +} \ No newline at end of file diff --git a/WABA360Dialog.NET/WABA360DialogApiClient.cs b/WABA360Dialog.NET/WABA360DialogApiClient.cs index 097f42d..4948cfc 100644 --- a/WABA360Dialog.NET/WABA360DialogApiClient.cs +++ b/WABA360Dialog.NET/WABA360DialogApiClient.cs @@ -5,14 +5,15 @@ namespace WABA360Dialog { public class WABA360DialogApiClient : WABA360DialogApiClientBase { - private const string BasePath = "https://waba.360dialog.io/"; - - public WABA360DialogApiClient(string apiKey) : base(apiKey, BasePath, new HttpClient()) + public const string BASEURL = "https://waba.360dialog.io/"; + public WABA360DialogApiClient(string apiKey) : base(apiKey, new HttpClient()) { } - - public WABA360DialogApiClient(string apiKey, HttpClient httpClient) : base(apiKey, BasePath, httpClient) + + public WABA360DialogApiClient(string apiKey, HttpClient httpClient) : base(apiKey, httpClient) { } + + public override string BasePath => BASEURL; } } \ No newline at end of file diff --git a/WABA360Dialog.NET/WABA360DialogPartnerClient.cs b/WABA360Dialog.NET/WABA360DialogPartnerClient.cs index 8a520c8..fa05bd8 100644 --- a/WABA360Dialog.NET/WABA360DialogPartnerClient.cs +++ b/WABA360Dialog.NET/WABA360DialogPartnerClient.cs @@ -18,23 +18,16 @@ namespace WABA360Dialog { public class WABA360DialogPartnerClient : IWABA360DialogPartnerClient, IDisposable { - private const string BasePath = "https://hub.360dialog.io/api/v2/"; + public const string BASEURL = "https://hub.360dialog.io/"; + private const string BasePath = $"{BASEURL}api/v2/"; private readonly PartnerInfo _partnerInfo; private string _accessToken; protected readonly HttpClient HttpClient; - public WABA360DialogPartnerClient(PartnerInfo partnerInfo) + public WABA360DialogPartnerClient(PartnerInfo partnerInfo) : this(partnerInfo, new HttpClient()) { - if (partnerInfo == null) - throw new ArgumentNullException(nameof(partnerInfo), "Partner Info cannot be null."); - - if (string.IsNullOrEmpty(partnerInfo.PartnerId) || string.IsNullOrEmpty(partnerInfo.Username) || string.IsNullOrEmpty(partnerInfo.Password)) - throw new ArgumentException("Partner Info cannot be null."); - _partnerInfo = partnerInfo; - - HttpClient = new HttpClient(); } public WABA360DialogPartnerClient(PartnerInfo partnerInfo, HttpClient httpClient) @@ -50,38 +43,17 @@ public WABA360DialogPartnerClient(PartnerInfo partnerInfo, HttpClient httpClient HttpClient = httpClient; } - public WABA360DialogPartnerClient(PartnerInfo partnerInfo, string accessToken) + public WABA360DialogPartnerClient(PartnerInfo partnerInfo, string accessToken) : this(partnerInfo, accessToken, new HttpClient()) { - if (partnerInfo == null) - throw new ArgumentNullException(nameof(partnerInfo), "Partner Info cannot be null."); - - if (string.IsNullOrEmpty(partnerInfo.PartnerId)) - throw new ArgumentNullException(nameof(partnerInfo.PartnerId), "Partner ID cannot be null."); - - if (string.IsNullOrEmpty(accessToken)) - throw new ArgumentNullException(nameof(accessToken), "Access Token cannot be null."); - _partnerInfo = partnerInfo; - _accessToken = accessToken; - - HttpClient = new HttpClient(); } - public WABA360DialogPartnerClient(PartnerInfo partnerInfo, string accessToken, HttpClient httpClient) + public WABA360DialogPartnerClient(PartnerInfo partnerInfo, string accessToken, HttpClient httpClient) : this(partnerInfo, httpClient) { - if (partnerInfo == null) - throw new ArgumentNullException(nameof(partnerInfo), "Partner Info cannot be null."); - - if (string.IsNullOrEmpty(partnerInfo.PartnerId)) - throw new ArgumentNullException(nameof(partnerInfo.PartnerId), "Partner ID cannot be null."); - if (string.IsNullOrEmpty(accessToken)) throw new ArgumentNullException(nameof(accessToken), "Access Token cannot be null."); - _partnerInfo = partnerInfo; _accessToken = accessToken; - - HttpClient = httpClient; } public async Task CreatePartnerWhatsAppBusinessApiTemplateAsync(string whatsAppBusinessApiAccountId, diff --git a/WABA360Dialog.NET/WABA360DialogSandboxClient.cs b/WABA360Dialog.NET/WABA360DialogSandboxClient.cs index f47ce2b..885968e 100644 --- a/WABA360Dialog.NET/WABA360DialogSandboxClient.cs +++ b/WABA360Dialog.NET/WABA360DialogSandboxClient.cs @@ -5,13 +5,15 @@ namespace WABA360Dialog { public class WABA360DialogSandboxClient : WABA360DialogApiClientBase { - private const string SandboxClientPath = "https://waba-sandbox.360dialog.io/"; - - public WABA360DialogSandboxClient(string apiKey) : base(apiKey, SandboxClientPath, new HttpClient()) + public const string BASEURL = "https://waba-sandbox.360dialog.io/"; + + public WABA360DialogSandboxClient(string apiKey) : base(apiKey, new HttpClient()) { - } - public WABA360DialogSandboxClient(string apiKey, HttpClient httpClient) : base(apiKey, SandboxClientPath, httpClient) + } + public WABA360DialogSandboxClient(string apiKey, HttpClient httpClient) : base(apiKey, httpClient) { } + + public override string BasePath => BASEURL; } } \ No newline at end of file diff --git a/global.json b/global.json index e5674e1..1bcf6c0 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "5.0.0", + "version": "6.0.0", "rollForward": "latestMinor", "allowPrerelease": false }