From a27921361728fc0cd487a9219cb81cb634580fa3 Mon Sep 17 00:00:00 2001 From: Jonathan Channon Date: Mon, 17 Feb 2020 20:26:56 +0000 Subject: [PATCH 01/20] Post as app apis --- src/Slack.Webhooks/SlackClient.cs | 27 ++++++++++++++++++++++++--- src/Slack.Webhooks/SlackResponse.cs | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 src/Slack.Webhooks/SlackResponse.cs diff --git a/src/Slack.Webhooks/SlackClient.cs b/src/Slack.Webhooks/SlackClient.cs index 4cc7237..b7a8c50 100644 --- a/src/Slack.Webhooks/SlackClient.cs +++ b/src/Slack.Webhooks/SlackClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -27,11 +28,20 @@ public class SlackClient : ISlackClient, IDisposable /// internal int TimeoutMs { get { return _timeout * 1000; } } - public SlackClient(string webhookUrl, int timeout = 100, HttpClient httpClient = null) + public SlackClient(string webhookUrl = null, int timeout = 100, HttpClient httpClient = null, string authorizationToken = null) { _httpClient = httpClient ?? new HttpClient(); - if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out _webhookUri)) - throw new ArgumentException("Please enter a valid webhook url"); + if (!string.IsNullOrWhiteSpace(authorizationToken)) + { + _webhookUri = new Uri("https://slack.com/api/chat.postMessage"); + _httpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {authorizationToken}"); + } + else + { + if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out _webhookUri)) + throw new ArgumentException("Please enter a valid webhook url"); + } + _timeout = timeout; } @@ -65,6 +75,17 @@ public async Task PostAsync(SlackMessage slackMessage) return content.Equals(POST_SUCCESS, StringComparison.OrdinalIgnoreCase); } } + + public async Task PostAsAppAsync(SlackMessage slackMessage) + { + using (var request = new HttpRequestMessage(HttpMethod.Post, _webhookUri)) + { + request.Content = new StringContent(slackMessage.AsJson(), System.Text.Encoding.UTF8, "application/json"); + var response = await _httpClient.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(content); + } + } /// /// Deserialize SlackMessage from a JSON string diff --git a/src/Slack.Webhooks/SlackResponse.cs b/src/Slack.Webhooks/SlackResponse.cs new file mode 100644 index 0000000..9231841 --- /dev/null +++ b/src/Slack.Webhooks/SlackResponse.cs @@ -0,0 +1,20 @@ +namespace Slack.Webhooks +{ + public class SlackResponse + { + public bool ok { get; set; } + public string channel { get; set; } + public string ts { get; set; } + public Message message { get; set; } + } + + public class Message + { + public string type { get; set; } + public string subtype { get; set; } + public string text { get; set; } + public string ts { get; set; } + public string username { get; set; } + public string bot_id { get; set; } + } +} \ No newline at end of file From e47071e53dff063e07b31d987cfc261d5b6b993e Mon Sep 17 00:00:00 2001 From: Jonathan Channon Date: Mon, 17 Feb 2020 20:40:36 +0000 Subject: [PATCH 02/20] Added json net attributes --- src/Slack.Webhooks/SlackResponse.cs | 40 +++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Slack.Webhooks/SlackResponse.cs b/src/Slack.Webhooks/SlackResponse.cs index 9231841..e7efbb3 100644 --- a/src/Slack.Webhooks/SlackResponse.cs +++ b/src/Slack.Webhooks/SlackResponse.cs @@ -1,20 +1,40 @@ +using Newtonsoft.Json; + namespace Slack.Webhooks { public class SlackResponse { - public bool ok { get; set; } - public string channel { get; set; } - public string ts { get; set; } - public Message message { get; set; } + [JsonProperty(PropertyName = "ok")] + public bool Ok { get; set; } + + [JsonProperty(PropertyName = "channel")] + public string Channel { get; set; } + + [JsonProperty(PropertyName = "ts")] + public string ThreadId { get; set; } + + [JsonProperty(PropertyName = "message")] + public Message Message { get; set; } } public class Message { - public string type { get; set; } - public string subtype { get; set; } - public string text { get; set; } - public string ts { get; set; } - public string username { get; set; } - public string bot_id { get; set; } + [JsonProperty(PropertyName = "type")] + public string Type { get; set; } + + [JsonProperty(PropertyName = "subtype")] + public string SubType { get; set; } + + [JsonProperty(PropertyName = "text")] + public string Text { get; set; } + + [JsonProperty(PropertyName = "ts")] + public string ThreadId { get; set; } + + [JsonProperty(PropertyName = "username")] + public string Username { get; set; } + + [JsonProperty(PropertyName = "bot_id")] + public string BotId { get; set; } } } \ No newline at end of file From 4bae38e56de09411fed51e4b91ff1b9136a9ae5e Mon Sep 17 00:00:00 2001 From: Benn Date: Tue, 18 Feb 2020 00:45:20 +0000 Subject: [PATCH 03/20] Use alternate secret --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8070df4..742bf3b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: shell: pwsh id: build env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} NUGET_TOKEN: ${{ secrets.NugetKey }} - name: Add artifacts uses: actions/upload-artifact@v1 From 955ae338d75d2dcfd3365b2a324c66bb05ebfc27 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Tue, 25 Feb 2020 16:56:22 +0000 Subject: [PATCH 04/20] First stab at restructuring the project for full Api implementation --- .../SlackClientFixtures.cs | 7 +- src/Slack.Webhooks/Api/ApiBase.cs | 63 +++++++++++ src/Slack.Webhooks/Api/Chat.cs | 26 +++++ src/Slack.Webhooks/Api/Webhook.cs | 41 +++++++ src/Slack.Webhooks/ISlackClient.cs | 1 - src/Slack.Webhooks/SlackClient.cs | 100 +++++++----------- src/Slack.Webhooks/SlackConfiguration.cs | 12 +++ 7 files changed, 182 insertions(+), 68 deletions(-) create mode 100644 src/Slack.Webhooks/Api/ApiBase.cs create mode 100644 src/Slack.Webhooks/Api/Chat.cs create mode 100644 src/Slack.Webhooks/Api/Webhook.cs create mode 100644 src/Slack.Webhooks/SlackConfiguration.cs diff --git a/src/Slack.Webhooks.Tests/SlackClientFixtures.cs b/src/Slack.Webhooks.Tests/SlackClientFixtures.cs index 8f52ad2..43439a0 100644 --- a/src/Slack.Webhooks.Tests/SlackClientFixtures.cs +++ b/src/Slack.Webhooks.Tests/SlackClientFixtures.cs @@ -101,6 +101,7 @@ public void ShouldContainSerializedMessage() var httpMessageHandler = GetMockHttpMessageHandler(callback: (req, token) => { var json = req.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + Console.WriteLine(json); postedMessage = SlackClient.DeserializeObject(json); }); @@ -167,9 +168,9 @@ private static Mock GetMockHttpMessageHandler(string respons } private static SlackMessage GetSlackMessage( - string text = "Test Message", - string channel = "#test", - string username = "testbot", + string text = "Test Message", + string channel = "#test", + string username = "testbot", string iconEmoji = Emoji.Ghost) { return new SlackMessage diff --git a/src/Slack.Webhooks/Api/ApiBase.cs b/src/Slack.Webhooks/Api/ApiBase.cs new file mode 100644 index 0000000..fbf4384 --- /dev/null +++ b/src/Slack.Webhooks/Api/ApiBase.cs @@ -0,0 +1,63 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Slack.Webhooks.Api +{ + public class ApiBase + { + protected readonly SlackConfiguration configuration; + + private readonly static DefaultContractResolver _resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; + private readonly static JsonSerializerSettings _serializerSettings = new JsonSerializerSettings + { + ContractResolver = _resolver, + NullValueHandling = NullValueHandling.Ignore + }; + + public ApiBase(SlackConfiguration configuration) + { + this.configuration = configuration; + } + + protected async Task PostAsync(Uri uri, object payload, bool requireAuthToken = true) where T : class + { + using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) + { + if (requireAuthToken) + request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {configuration.AuthToken}"); + + var json = SerializeObject(payload); + request.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); + var response = await configuration.HttpClient.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + + return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : DeserializeObject(content); + } + } + + /// + /// Deserialize SlackMessage from a JSON string + /// + /// string containing serialized JSON + /// SlackMessage + public static T DeserializeObject(string json) where T : class + { + return JsonConvert.DeserializeObject(json, _serializerSettings); + } + + /// + /// Serialize SlackMessage to a JSON string + /// + /// An instance of SlackMessage + /// string containing serialized JSON + public static string SerializeObject(object obj) + { + return JsonConvert.SerializeObject(obj, _serializerSettings); + } + + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Api/Chat.cs b/src/Slack.Webhooks/Api/Chat.cs new file mode 100644 index 0000000..21411b7 --- /dev/null +++ b/src/Slack.Webhooks/Api/Chat.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; + +namespace Slack.Webhooks.Api +{ + public class Chat : ApiBase + { + public Chat(SlackConfiguration configuration) : base(configuration) + { + } + + public SlackResponse PostMessage(SlackMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postMessage"); + return PostAsync(uri, message).GetAwaiter().GetResult(); + } + + public async Task PostMessageAsync(SlackMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postMessage"); + return await PostAsync(uri, message); + } + + + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Api/Webhook.cs b/src/Slack.Webhooks/Api/Webhook.cs new file mode 100644 index 0000000..1d7eeeb --- /dev/null +++ b/src/Slack.Webhooks/Api/Webhook.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Slack.Webhooks.Api +{ + public class Webhook : ApiBase + { + private const string POST_SUCCESS = "ok"; + public Webhook(SlackConfiguration configuration) : base(configuration) + { + } + + public virtual bool Post(SlackMessage slackMessage) + { + var result = PostWebhook(slackMessage).GetAwaiter().GetResult(); + return result; + } + + public bool PostToChannels(SlackMessage message, IEnumerable channels) + { + return channels.DefaultIfEmpty(message.Channel) + .Select(message.Clone) + .Select(Post).All(r => r); + } + + public IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels) + { + return channels.DefaultIfEmpty(message.Channel) + .Select(message.Clone) + .Select(PostWebhook); + } + + public async Task PostWebhook(SlackMessage slackMessage) + { + var response = await PostAsync(configuration.WebhookUri, slackMessage, false); + return response.Equals(POST_SUCCESS, StringComparison.OrdinalIgnoreCase); + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/ISlackClient.cs b/src/Slack.Webhooks/ISlackClient.cs index 9cfb502..1123f73 100644 --- a/src/Slack.Webhooks/ISlackClient.cs +++ b/src/Slack.Webhooks/ISlackClient.cs @@ -7,7 +7,6 @@ public interface ISlackClient { bool Post(SlackMessage slackMessage); bool PostToChannels(SlackMessage message, IEnumerable channels); - Task PostAsync(SlackMessage slackMessage); IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels); } } diff --git a/src/Slack.Webhooks/SlackClient.cs b/src/Slack.Webhooks/SlackClient.cs index b7a8c50..78c0f67 100644 --- a/src/Slack.Webhooks/SlackClient.cs +++ b/src/Slack.Webhooks/SlackClient.cs @@ -1,90 +1,60 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Slack.Webhooks.Api; namespace Slack.Webhooks { public class SlackClient : ISlackClient, IDisposable { - private readonly HttpClient _httpClient; - private readonly Uri _webhookUri; - private const string POST_SUCCESS = "ok"; - private int _timeout = 100; + private readonly SlackConfiguration _configuration; + public int TimeoutMs { get { return (int)_configuration.HttpClient.Timeout.TotalMilliseconds; } } - private static readonly DefaultContractResolver resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; - private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings - { - ContractResolver = resolver, - NullValueHandling = NullValueHandling.Ignore - }; + #region Api + public Webhook Webhook { get; private set; } + public Chat Chat { get; private set; } + #endregion - /// - /// Returns the current Timeout value. - /// - internal int TimeoutMs { get { return _timeout * 1000; } } - - public SlackClient(string webhookUrl = null, int timeout = 100, HttpClient httpClient = null, string authorizationToken = null) + public SlackClient(string webhookUrl = null, int timeout = 100, HttpClient httpClient = null) { - _httpClient = httpClient ?? new HttpClient(); - if (!string.IsNullOrWhiteSpace(authorizationToken)) - { - _webhookUri = new Uri("https://slack.com/api/chat.postMessage"); - _httpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {authorizationToken}"); - } - else + Uri webhookUri; + if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out webhookUri)) + throw new ArgumentException("Please enter a valid webhook url"); + + _configuration = new SlackConfiguration { - if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out _webhookUri)) - throw new ArgumentException("Please enter a valid webhook url"); - } + HttpClient = httpClient ?? new HttpClient { Timeout = TimeSpan.FromSeconds(timeout) }, + WebhookUri = webhookUri + }; - _timeout = timeout; - } - - public virtual bool Post(SlackMessage slackMessage) - { - var result = PostAsync(slackMessage).GetAwaiter().GetResult(); - return result; + Webhook = new Webhook(_configuration); + Chat = new Chat(_configuration); } - public bool PostToChannels(SlackMessage message, IEnumerable channels) + public SlackClient(SlackConfiguration configuration) { - return channels.DefaultIfEmpty(message.Channel) - .Select(message.Clone) - .Select(Post).All(r => r); + _configuration = configuration; } - - public IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels) + + [Obsolete("Please use SlackClient.Webhook.Post(SlackMessage) instead.")] + public virtual bool Post(SlackMessage slackMessage) { - return channels.DefaultIfEmpty(message.Channel) - .Select(message.Clone) - .Select(PostAsync); + return Webhook.Post(slackMessage); } - public async Task PostAsync(SlackMessage slackMessage) + [Obsolete("Please use SlackClient.Webhook.PostToChannels(SlackMessage, IEnumerable) instead.")] + public bool PostToChannels(SlackMessage message, IEnumerable channels) { - using (var request = new HttpRequestMessage(HttpMethod.Post, _webhookUri)) - { - request.Content = new StringContent(slackMessage.AsJson(), System.Text.Encoding.UTF8, "application/json"); - var response = await _httpClient.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); - return content.Equals(POST_SUCCESS, StringComparison.OrdinalIgnoreCase); - } + return Webhook.PostToChannels(message, channels); } - - public async Task PostAsAppAsync(SlackMessage slackMessage) + + [Obsolete("Please use SlackClient.Webhook.PostToChannelsAsync(SlackMessage, IEnumerable) instead.")] + public IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels) { - using (var request = new HttpRequestMessage(HttpMethod.Post, _webhookUri)) - { - request.Content = new StringContent(slackMessage.AsJson(), System.Text.Encoding.UTF8, "application/json"); - var response = await _httpClient.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); - return JsonConvert.DeserializeObject(content); - } + return Webhook.PostToChannelsAsync(message, channels); } /// @@ -92,9 +62,10 @@ public async Task PostAsAppAsync(SlackMessage slackMessage) /// /// string containing serialized JSON /// SlackMessage + [Obsolete("Please use ApiBase.DeserializeObject instead.")] public static SlackMessage DeserializeObject(string json) { - return JsonConvert.DeserializeObject(json, serializerSettings); + return ApiBase.DeserializeObject(json); } /// @@ -102,14 +73,15 @@ public static SlackMessage DeserializeObject(string json) /// /// An instance of SlackMessage /// string containing serialized JSON + [Obsolete("Please use ApiBase.SerializeObject instead.")] public static string SerializeObject(object obj) { - return JsonConvert.SerializeObject(obj, serializerSettings); + return ApiBase.SerializeObject(obj); } public void Dispose() { - _httpClient.Dispose(); + _configuration.HttpClient.Dispose(); } } } diff --git a/src/Slack.Webhooks/SlackConfiguration.cs b/src/Slack.Webhooks/SlackConfiguration.cs new file mode 100644 index 0000000..13a0467 --- /dev/null +++ b/src/Slack.Webhooks/SlackConfiguration.cs @@ -0,0 +1,12 @@ +using System.Net.Http; +using Newtonsoft.Json; + +namespace Slack.Webhooks +{ + public class SlackConfiguration + { + public System.Uri WebhookUri { get; set; } + public string AuthToken { get; set; } + public HttpClient HttpClient { get; set; } + } +} \ No newline at end of file From b8b4a2d4ac6c7164472d3dc3cb3d2b5c5aac3cc5 Mon Sep 17 00:00:00 2001 From: Jonathan Channon Date: Wed, 26 Feb 2020 15:25:27 +0000 Subject: [PATCH 05/20] Tidy up to support netstandard2.0 (#76) --- .gitignore | 1 + build.cake | 12 ---- .../Properties/AssemblyInfo.cs | 37 ---------- .../Slack.Webhooks.Tests.csproj | 71 +++++++------------ src/Slack.Webhooks.Tests/packages.config | 41 ----------- src/Slack.Webhooks/Api/ApiBase.cs | 7 +- src/Slack.Webhooks/Emoji.cs | 5 +- src/Slack.Webhooks/Properties/AssemblyInfo.cs | 39 ---------- src/Slack.Webhooks/Slack.Webhooks.csproj | 47 ++++++------ src/Slack.Webhooks/Slack.Webhooks.nuspec | 62 ---------------- src/Slack.Webhooks/SlackClient.cs | 2 - src/Slack.Webhooks/SlackConfiguration.cs | 4 +- src/Slack.Webhooks/SlackMessage.cs | 1 - 13 files changed, 57 insertions(+), 272 deletions(-) delete mode 100644 src/Slack.Webhooks.Tests/Properties/AssemblyInfo.cs delete mode 100644 src/Slack.Webhooks.Tests/packages.config delete mode 100644 src/Slack.Webhooks/Properties/AssemblyInfo.cs delete mode 100644 src/Slack.Webhooks/Slack.Webhooks.nuspec diff --git a/.gitignore b/.gitignore index 99c0218..41c1cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,4 @@ Backup*/ UpgradeLog*.XML /src/.vs/Slack.Webhooks/v15/Server/sqlite3 /.vs +.idea \ No newline at end of file diff --git a/build.cake b/build.cake index 804d0f3..7bdc528 100644 --- a/build.cake +++ b/build.cake @@ -140,20 +140,8 @@ Task("DeployNuGet") }); Task("Test") - .IsDependentOn("TestFramework") .IsDependentOn("TestCore"); -Task("TestFramework") - .IsDependentOn("Build") - .Does(() => -{ - var testAssemblies = GetFiles($"./src/**/bin/{configuration}/net45/*.Tests.dll"); - XUnit2(testAssemblies, new XUnit2Settings { - Parallelism = ParallelismOption.All, - NoAppDomain = true - }); -}); - Task("TestCore") .IsDependentOn("Build") .Does(() => diff --git a/src/Slack.Webhooks.Tests/Properties/AssemblyInfo.cs b/src/Slack.Webhooks.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index a0a6e8d..0000000 --- a/src/Slack.Webhooks.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Slack.Webhooks.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Slack.Webhooks.Tests")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4e3be2c7-ab4d-4e23-adc1-dc4ac99716e8")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.1.1.0")] -[assembly: AssemblyVersion("1.1.1.0")] -[assembly: AssemblyFileVersion("1.1.1.0")] -[assembly: AssemblyInformationalVersion("1.1.1+Branch.master.Sha.0a3eda61ad8e047b4e5d539a7ceae05de0d8d170")] diff --git a/src/Slack.Webhooks.Tests/Slack.Webhooks.Tests.csproj b/src/Slack.Webhooks.Tests/Slack.Webhooks.Tests.csproj index 6ae39a5..307ec08 100644 --- a/src/Slack.Webhooks.Tests/Slack.Webhooks.Tests.csproj +++ b/src/Slack.Webhooks.Tests/Slack.Webhooks.Tests.csproj @@ -1,49 +1,26 @@ - - net45;netcoreapp2 - False - False - - - - - - - - - - ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - - - - - - - - - - - - - {53751b1b-a63e-4e4f-988c-bc783e68b15f} - Slack.Webhooks - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + netcoreapp2.0 + False + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/src/Slack.Webhooks.Tests/packages.config b/src/Slack.Webhooks.Tests/packages.config deleted file mode 100644 index b5757f0..0000000 --- a/src/Slack.Webhooks.Tests/packages.config +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Slack.Webhooks/Api/ApiBase.cs b/src/Slack.Webhooks/Api/ApiBase.cs index fbf4384..a09a0a7 100644 --- a/src/Slack.Webhooks/Api/ApiBase.cs +++ b/src/Slack.Webhooks/Api/ApiBase.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Net.Http.Headers; +using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -11,8 +12,8 @@ public class ApiBase { protected readonly SlackConfiguration configuration; - private readonly static DefaultContractResolver _resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; - private readonly static JsonSerializerSettings _serializerSettings = new JsonSerializerSettings + private static readonly DefaultContractResolver _resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; + private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { ContractResolver = _resolver, NullValueHandling = NullValueHandling.Ignore @@ -31,7 +32,7 @@ protected async Task PostAsync(Uri uri, object payload, bool requireAuthTo request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {configuration.AuthToken}"); var json = SerializeObject(payload); - request.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await configuration.HttpClient.SendAsync(request); var content = await response.Content.ReadAsStringAsync(); diff --git a/src/Slack.Webhooks/Emoji.cs b/src/Slack.Webhooks/Emoji.cs index b326534..f4f3cc0 100644 --- a/src/Slack.Webhooks/Emoji.cs +++ b/src/Slack.Webhooks/Emoji.cs @@ -1,7 +1,4 @@ -using Newtonsoft.Json; -using System; - -namespace Slack.Webhooks +namespace Slack.Webhooks { public class Emoji diff --git a/src/Slack.Webhooks/Properties/AssemblyInfo.cs b/src/Slack.Webhooks/Properties/AssemblyInfo.cs deleted file mode 100644 index bbadfbd..0000000 --- a/src/Slack.Webhooks/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Slack.Webhooks")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Slack.Webhooks")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("98c19dcd-28da-4ebf-8e81-c9e051d65625")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.1.1.0")] -[assembly: AssemblyFileVersion("1.1.1.0")] -[assembly: AssemblyVersion("1.1.1.0")] - -[assembly: InternalsVisibleTo("Slack.Webhooks.Tests")] -[assembly: AssemblyInformationalVersion("1.1.1+Branch.master.Sha.0a3eda61ad8e047b4e5d539a7ceae05de0d8d170")] diff --git a/src/Slack.Webhooks/Slack.Webhooks.csproj b/src/Slack.Webhooks/Slack.Webhooks.csproj index ccc484b..375a4b8 100644 --- a/src/Slack.Webhooks/Slack.Webhooks.csproj +++ b/src/Slack.Webhooks/Slack.Webhooks.csproj @@ -1,26 +1,29 @@  - - net45;netstandard1.3 - False - False - https://github.com/mrb0nj/Slack.Webhooks.git - - - - - - - - - - - - - - - - - + + netstandard2.0 + False + 1.1.0 + Benn Lazell and contributors + Benn Lazell and contributors + https://github.com/nerdfury/Slack.Webhooks/blob/master/LICENSE + http://files.fivedegrees.co.uk/Slack.Webhooks/webhook.png + False + Even simpler integration with Slack's Incoming webhook API + Slack Webhooks + + + + + + + \ No newline at end of file diff --git a/src/Slack.Webhooks/Slack.Webhooks.nuspec b/src/Slack.Webhooks/Slack.Webhooks.nuspec deleted file mode 100644 index f31e413..0000000 --- a/src/Slack.Webhooks/Slack.Webhooks.nuspec +++ /dev/null @@ -1,62 +0,0 @@ - - - - Slack.Webhooks - 1.0.0 - mrb0nj and contributors - MIT - images\slack.png - false - - - - mrb0nj and contributors - en-GB - Slack Webhooks - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Slack.Webhooks/SlackClient.cs b/src/Slack.Webhooks/SlackClient.cs index 78c0f67..dd75578 100644 --- a/src/Slack.Webhooks/SlackClient.cs +++ b/src/Slack.Webhooks/SlackClient.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using Slack.Webhooks.Api; namespace Slack.Webhooks diff --git a/src/Slack.Webhooks/SlackConfiguration.cs b/src/Slack.Webhooks/SlackConfiguration.cs index 13a0467..9cfd924 100644 --- a/src/Slack.Webhooks/SlackConfiguration.cs +++ b/src/Slack.Webhooks/SlackConfiguration.cs @@ -1,11 +1,11 @@ +using System; using System.Net.Http; -using Newtonsoft.Json; namespace Slack.Webhooks { public class SlackConfiguration { - public System.Uri WebhookUri { get; set; } + public Uri WebhookUri { get; set; } public string AuthToken { get; set; } public HttpClient HttpClient { get; set; } } diff --git a/src/Slack.Webhooks/SlackMessage.cs b/src/Slack.Webhooks/SlackMessage.cs index 95ae1f3..d0ac3d0 100644 --- a/src/Slack.Webhooks/SlackMessage.cs +++ b/src/Slack.Webhooks/SlackMessage.cs @@ -1,5 +1,4 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; From c983e3f5d50e542f0f015845770940cf51fc8161 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Wed, 26 Feb 2020 16:37:29 +0000 Subject: [PATCH 06/20] Fix obsolete usages --- .../ActionsBlockFixtures.cs | 19 ++++++------- src/Slack.Webhooks.Tests/BlockFixtures.cs | 3 ++- .../ButtonElementFixtures.cs | 19 ++++++------- .../ConfirmationElementFixtures.cs | 17 ++++++------ .../ContextBlockFixtures.cs | 7 ++--- .../DatePickerElementFixtures.cs | 15 ++++++----- src/Slack.Webhooks.Tests/ElementFixtures.cs | 3 ++- src/Slack.Webhooks.Tests/FileBlockFixtures.cs | 7 ++--- .../ImageBlockFixtures.cs | 11 ++++---- .../ImageElementFixtures.cs | 7 ++--- .../InputBlockFixtures.cs | 21 ++++++++------- .../MultiSelectExternalElementFixtures.cs | 9 ++++--- .../MultiSelectStaticElementFixtures.cs | 21 ++++++++------- .../MultiSelectUsersElementFixtures.cs | 15 ++++++----- .../OptionElementFixtures.cs | 9 ++++--- .../OptionGroupElementFixtures.cs | 11 ++++---- .../OverflowElementFixtures.cs | 23 ++++++++-------- .../PlainTextInputElementFixtures.cs | 19 ++++++------- .../RadioButtonElementFixtures.cs | 27 ++++++++++--------- .../SectionBlockFixtures.cs | 15 ++++++----- .../SelectElementFixtures.cs | 13 ++++----- .../SelectExternalElementFixtures.cs | 7 ++--- .../SelectStaticElementFixtures.cs | 19 ++++++------- .../SelectUsersElementFixtures.cs | 13 ++++----- .../SlackClientFixtures.cs | 17 ++++++------ .../TextElementFixtures.cs | 7 ++--- src/Slack.Webhooks/SlackMessage.cs | 3 ++- 27 files changed, 192 insertions(+), 165 deletions(-) diff --git a/src/Slack.Webhooks.Tests/ActionsBlockFixtures.cs b/src/Slack.Webhooks.Tests/ActionsBlockFixtures.cs index 9b6b3d4..7bc8325 100644 --- a/src/Slack.Webhooks.Tests/ActionsBlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/ActionsBlockFixtures.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Slack.Webhooks.Elements; using Slack.Webhooks.Interfaces; @@ -13,13 +14,13 @@ public class ActionsBlockFixtures public void ShouldSerializeActionElements() { // arrange - var elementList = new List - { - new Button(), - new SelectChannels(), - new SelectConversations(), - new SelectExternal(), - new SelectStatic(), + var elementList = new List + { + new Button(), + new SelectChannels(), + new SelectConversations(), + new SelectExternal(), + new SelectStatic(), new SelectUsers(), new Overflow(), new DatePicker() @@ -27,8 +28,8 @@ public void ShouldSerializeActionElements() var actions = new Actions { Elements = elementList }; // act - var elementListPayload = SlackClient.SerializeObject(elementList); - var payload = SlackClient.SerializeObject(actions); + var elementListPayload = ApiBase.SerializeObject(elementList); + var payload = ApiBase.SerializeObject(actions); // assert payload.Should().Contain($"\"elements\":{elementListPayload}"); diff --git a/src/Slack.Webhooks.Tests/BlockFixtures.cs b/src/Slack.Webhooks.Tests/BlockFixtures.cs index 64ef8b8..a5720fe 100644 --- a/src/Slack.Webhooks.Tests/BlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/BlockFixtures.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using FluentAssertions; using Newtonsoft.Json; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Xunit; @@ -13,7 +14,7 @@ public class BlockFixtures public void ShouldHaveBlockTypeAndBlockId(Block block, string expectedType, string expectedBlockId) { // arrange/act - var payload = SlackClient.SerializeObject(block); + var payload = ApiBase.SerializeObject(block); // assert payload.Should().Contain($"\"type\":\"{expectedType}\""); diff --git a/src/Slack.Webhooks.Tests/ButtonElementFixtures.cs b/src/Slack.Webhooks.Tests/ButtonElementFixtures.cs index 8652563..3206494 100644 --- a/src/Slack.Webhooks.Tests/ButtonElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/ButtonElementFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Elements; using Xunit; @@ -13,7 +14,7 @@ public void ShouldSerializeType() var button = new Button(); // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"type\":\"button\""); @@ -23,10 +24,10 @@ public void ShouldSerializeType() public void ShouldSerializeText() { // arrange - var button = new Button { Text = new TextObject { Text = "Test Text" }}; + var button = new Button { Text = new TextObject { Text = "Test Text" } }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"text\":{\"type\":\"plain_text\""); @@ -39,7 +40,7 @@ public void ShouldSerializeActionId() var button = new Button { ActionId = "Action123" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"action_id\":\"Action123\""); @@ -52,7 +53,7 @@ public void ShouldSerializeUrl() var button = new Button { Url = "http://someurl.com" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"url\":\"http://someurl.com\""); @@ -65,7 +66,7 @@ public void ShouldSerializeValue() var button = new Button { Value = "Value123" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"value\":\"Value123\""); @@ -78,7 +79,7 @@ public void ShouldSerializeStyle() var button = new Button { Style = "Style123" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"style\":\"Style123\""); @@ -92,8 +93,8 @@ public void ShouldSerializeConfirm() var button = new Button { Confirm = confirm }; // act - var confirmPayload = SlackClient.SerializeObject(confirm); - var payload = SlackClient.SerializeObject(button); + var confirmPayload = ApiBase.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain($"\"confirm\":{confirmPayload}"); diff --git a/src/Slack.Webhooks.Tests/ConfirmationElementFixtures.cs b/src/Slack.Webhooks.Tests/ConfirmationElementFixtures.cs index 77278f6..8af5678 100644 --- a/src/Slack.Webhooks.Tests/ConfirmationElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/ConfirmationElementFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Elements; using Xunit; @@ -10,10 +11,10 @@ public class ConfirmationElementFixtures public void ShouldSerializeTitle() { // arrange - var confirm = new Confirmation { Title = new TextObject { Text = "Title Test"} }; + var confirm = new Confirmation { Title = new TextObject { Text = "Title Test" } }; // act - var payload = SlackClient.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(confirm); // assert payload.Should().Contain("\"title\":{"); @@ -24,10 +25,10 @@ public void ShouldSerializeTitle() public void ShouldSerializeText() { // arrange - var confirm = new Confirmation { Text = new TextObject { Text = "Title Test"} }; + var confirm = new Confirmation { Text = new TextObject { Text = "Title Test" } }; // act - var payload = SlackClient.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(confirm); // assert payload.Should().Contain("\"text\":{"); @@ -38,10 +39,10 @@ public void ShouldSerializeText() public void ShouldSerializeConfirm() { // arrange - var confirm = new Confirmation { Confirm = new TextObject { Text = "Title Test"} }; + var confirm = new Confirmation { Confirm = new TextObject { Text = "Title Test" } }; // act - var payload = SlackClient.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(confirm); // assert payload.Should().Contain("\"confirm\":{"); @@ -52,10 +53,10 @@ public void ShouldSerializeConfirm() public void ShouldSerializeDeny() { // arrange - var confirm = new Confirmation { Deny = new TextObject { Text = "Title Test"} }; + var confirm = new Confirmation { Deny = new TextObject { Text = "Title Test" } }; // act - var payload = SlackClient.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(confirm); // assert payload.Should().Contain("\"deny\":{"); diff --git a/src/Slack.Webhooks.Tests/ContextBlockFixtures.cs b/src/Slack.Webhooks.Tests/ContextBlockFixtures.cs index 77bf8fd..1565fc4 100644 --- a/src/Slack.Webhooks.Tests/ContextBlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/ContextBlockFixtures.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Slack.Webhooks.Elements; using Slack.Webhooks.Interfaces; @@ -19,7 +20,7 @@ public void ShouldBeAbleToContainImageElements() }; // act - var payload = SlackClient.SerializeObject(context); + var payload = ApiBase.SerializeObject(context); // assert payload.Should().Contain("\"elements\":["); // bleeugh @@ -29,13 +30,13 @@ public void ShouldBeAbleToContainImageElements() public void ShouldBeAbleToContainTextElements() { // arrange - var context = new Context + var context = new Context { Elements = new List() { new TextObject() } }; // act - var payload = SlackClient.SerializeObject(context); + var payload = ApiBase.SerializeObject(context); // assert payload.Should().Contain("\"elements\":["); // bleeugh diff --git a/src/Slack.Webhooks.Tests/DatePickerElementFixtures.cs b/src/Slack.Webhooks.Tests/DatePickerElementFixtures.cs index 007950a..49ff75a 100644 --- a/src/Slack.Webhooks.Tests/DatePickerElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/DatePickerElementFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Elements; using Xunit; @@ -13,7 +14,7 @@ public void ShouldSerializeType() var button = new DatePicker(); // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"type\":\"datepicker\""); @@ -26,7 +27,7 @@ public void ShouldSerializeActionId() var button = new DatePicker { ActionId = "Action123" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"action_id\":\"Action123\""); @@ -39,7 +40,7 @@ public void ShouldSerializeInitialDate() var button = new DatePicker { InitialDate = "2019-11-01" }; // act - var payload = SlackClient.SerializeObject(button); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain("\"initial_date\":\"2019-11-01\""); @@ -53,8 +54,8 @@ public void ShouldSerializePlaceholder() var button = new DatePicker { Placeholder = text }; // act - var textPayload = SlackClient.SerializeObject(text); - var payload = SlackClient.SerializeObject(button); + var textPayload = ApiBase.SerializeObject(text); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain($"\"placeholder\":{textPayload}"); @@ -68,8 +69,8 @@ public void ShouldSerializeConfirm() var button = new DatePicker { Confirm = confirm }; // act - var confirmPayload = SlackClient.SerializeObject(confirm); - var payload = SlackClient.SerializeObject(button); + var confirmPayload = ApiBase.SerializeObject(confirm); + var payload = ApiBase.SerializeObject(button); // assert payload.Should().Contain($"\"confirm\":{confirmPayload}"); diff --git a/src/Slack.Webhooks.Tests/ElementFixtures.cs b/src/Slack.Webhooks.Tests/ElementFixtures.cs index f83c334..38a5ae0 100644 --- a/src/Slack.Webhooks.Tests/ElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/ElementFixtures.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using Slack.Webhooks.Api; using Xunit; namespace Slack.Webhooks.Tests @@ -11,7 +12,7 @@ public class ElementFixtures public void ShouldHaveBlockTypeAndBlockId(Elements.Element element, string expectedType) { // arrange/act - var payload = SlackClient.SerializeObject(element); + var payload = ApiBase.SerializeObject(element); // assert payload.Should().Contain($"\"type\":\"{expectedType}\""); diff --git a/src/Slack.Webhooks.Tests/FileBlockFixtures.cs b/src/Slack.Webhooks.Tests/FileBlockFixtures.cs index b90d936..31bf46d 100644 --- a/src/Slack.Webhooks.Tests/FileBlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/FileBlockFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Xunit; @@ -10,10 +11,10 @@ public class FileBlockFixtures public void ShouldHaveExternalId() { // arrange - var file = new File { ExternalId = "AB_1234"}; + var file = new File { ExternalId = "AB_1234" }; // act - var payload = SlackClient.SerializeObject(file); + var payload = ApiBase.SerializeObject(file); // assert payload.Should().Contain("\"external_id\":\"AB_1234\""); @@ -26,7 +27,7 @@ public void ShouldHaveRemoteSourceByDefault() var file = new File(); // act - var payload = SlackClient.SerializeObject(file); + var payload = ApiBase.SerializeObject(file); // assert file.Source.Should().Be("remote"); diff --git a/src/Slack.Webhooks.Tests/ImageBlockFixtures.cs b/src/Slack.Webhooks.Tests/ImageBlockFixtures.cs index f525120..428c69f 100644 --- a/src/Slack.Webhooks.Tests/ImageBlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/ImageBlockFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Slack.Webhooks.Elements; using Xunit; @@ -14,7 +15,7 @@ public void ShouldContainImageUrl() var image = new Blocks.Image { ImageUrl = "http://someimage" }; // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"image_url\":\"http://someimage\""); @@ -27,7 +28,7 @@ public void ShouldContainAltText() var image = new Blocks.Image { AltText = "The Text" }; // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"alt_text\":\"The Text\""); @@ -37,15 +38,15 @@ public void ShouldContainAltText() public void ShouldContainTitle() { // arrange - var image = new Blocks.Image { Title = new TextObject { Text = "The Title"} }; + var image = new Blocks.Image { Title = new TextObject { Text = "The Title" } }; // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"text\":\"The Title\""); } - + } } \ No newline at end of file diff --git a/src/Slack.Webhooks.Tests/ImageElementFixtures.cs b/src/Slack.Webhooks.Tests/ImageElementFixtures.cs index abe1dc5..0f44e16 100644 --- a/src/Slack.Webhooks.Tests/ImageElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/ImageElementFixtures.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Elements; using Xunit; @@ -13,7 +14,7 @@ public void ShouldSerializeType() var image = new Image(); // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"type\":\"image\""); @@ -26,7 +27,7 @@ public void ShouldSerializeImageUrl() var image = new Image { ImageUrl = "http://someurl.com" }; // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"image_url\":\"http://someurl.com\""); @@ -39,7 +40,7 @@ public void ShouldSerializeAltText() var image = new Image { AltText = "Alternate Text" }; // act - var payload = SlackClient.SerializeObject(image); + var payload = ApiBase.SerializeObject(image); // assert payload.Should().Contain("\"alt_text\":\"Alternate Text\""); diff --git a/src/Slack.Webhooks.Tests/InputBlockFixtures.cs b/src/Slack.Webhooks.Tests/InputBlockFixtures.cs index 2621489..119f33f 100644 --- a/src/Slack.Webhooks.Tests/InputBlockFixtures.cs +++ b/src/Slack.Webhooks.Tests/InputBlockFixtures.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Blocks; using Slack.Webhooks.Elements; using Slack.Webhooks.Interfaces; @@ -13,12 +14,12 @@ public class InputBlockFixtures public void ShouldSerializeLabel() { // arrange - var textObject = new TextObject { Text = "Test label"}; + var textObject = new TextObject { Text = "Test label" }; var input = new Input { Label = textObject }; // act - var textPayload = SlackClient.SerializeObject(textObject); - var payload = SlackClient.SerializeObject(input); + var textPayload = ApiBase.SerializeObject(textObject); + var payload = ApiBase.SerializeObject(input); // assert payload.Should().Contain($"\"label\":{textPayload}"); @@ -28,17 +29,17 @@ public void ShouldSerializeLabel() public void ShouldSerializeHint() { // arrange - var textObject = new TextObject { Text = "Test hint"}; + var textObject = new TextObject { Text = "Test hint" }; var input = new Input { Hint = textObject }; // act - var textPayload = SlackClient.SerializeObject(textObject); - var payload = SlackClient.SerializeObject(input); + var textPayload = ApiBase.SerializeObject(textObject); + var payload = ApiBase.SerializeObject(input); // assert payload.Should().Contain($"\"hint\":{textPayload}"); } - + [Fact] public void ShouldSerializeOptional() { @@ -46,7 +47,7 @@ public void ShouldSerializeOptional() var input = new Input { Optional = true }; // act - var payload = SlackClient.SerializeObject(input); + var payload = ApiBase.SerializeObject(input); // assert payload.Should().Contain("\"optional\":true"); @@ -60,8 +61,8 @@ public void ShouldSerializeInputElementTypes(object element) var input = new Input { Element = (IInputElement)element }; // act - var elementPayload = SlackClient.SerializeObject(element); - var payload = SlackClient.SerializeObject(input); + var elementPayload = ApiBase.SerializeObject(element); + var payload = ApiBase.SerializeObject(input); // arrange payload.Should().Contain($"\"element\":{elementPayload}"); diff --git a/src/Slack.Webhooks.Tests/MultiSelectExternalElementFixtures.cs b/src/Slack.Webhooks.Tests/MultiSelectExternalElementFixtures.cs index 59dc38f..e29e380 100644 --- a/src/Slack.Webhooks.Tests/MultiSelectExternalElementFixtures.cs +++ b/src/Slack.Webhooks.Tests/MultiSelectExternalElementFixtures.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentAssertions; +using Slack.Webhooks.Api; using Slack.Webhooks.Elements; using Xunit; @@ -14,7 +15,7 @@ public void ShouldSerializeMinQueryLength() var select = new MultiSelectExternal { MinQueryLength = 5 }; // act - var payload = SlackClient.SerializeObject(select); + var payload = ApiBase.SerializeObject(select); // assert payload.Should().Contain($"\"min_query_length\":5"); @@ -24,12 +25,12 @@ public void ShouldSerializeMinQueryLength() public void ShouldSerializeInitialOptions() { // arrange - var options = new List - /// - public SlackActionStyle Style + /// + public ActionStyle Style { get { return _style; } set { _style = value; } @@ -68,8 +70,8 @@ public SlackActionStyle Style /// Our clever default behavior is default, which means the menu's /// options are provided directly in the posted message under options. /// - /// - public SlackActionDataSource DataSource + /// + public ActionDataSource DataSource { get { return _dataSource; } set { _dataSource = value; } @@ -103,6 +105,6 @@ public SlackActionDataSource DataSource /// by asking them to confirm their button click one more time. /// Use confirmation dialogs with care. /// - public Confirm Confirm { get; set; } + public ConfirmAction ConfirmAction { get; set; } } } diff --git a/src/Slack.Webhooks/Action/Confirm.cs b/src/Slack.Webhooks/Actions/ConfirmAction.cs similarity index 87% rename from src/Slack.Webhooks/Action/Confirm.cs rename to src/Slack.Webhooks/Actions/ConfirmAction.cs index 2ba84a7..9262ea2 100644 --- a/src/Slack.Webhooks/Action/Confirm.cs +++ b/src/Slack.Webhooks/Actions/ConfirmAction.cs @@ -1,6 +1,6 @@ namespace Slack.Webhooks.Action { - public class Confirm + public class ConfirmAction { public string Title { get; set; } public string Text { get; set; } diff --git a/src/Slack.Webhooks/Action/Option.cs b/src/Slack.Webhooks/Actions/Option.cs similarity index 100% rename from src/Slack.Webhooks/Action/Option.cs rename to src/Slack.Webhooks/Actions/Option.cs diff --git a/src/Slack.Webhooks/Action/OptionGroup.cs b/src/Slack.Webhooks/Actions/OptionGroup.cs similarity index 100% rename from src/Slack.Webhooks/Action/OptionGroup.cs rename to src/Slack.Webhooks/Actions/OptionGroup.cs diff --git a/src/Slack.Webhooks/Api/ApiBase.cs b/src/Slack.Webhooks/Api/ApiBase.cs deleted file mode 100644 index a09a0a7..0000000 --- a/src/Slack.Webhooks/Api/ApiBase.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Slack.Webhooks.Api -{ - public class ApiBase - { - protected readonly SlackConfiguration configuration; - - private static readonly DefaultContractResolver _resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; - private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings - { - ContractResolver = _resolver, - NullValueHandling = NullValueHandling.Ignore - }; - - public ApiBase(SlackConfiguration configuration) - { - this.configuration = configuration; - } - - protected async Task PostAsync(Uri uri, object payload, bool requireAuthToken = true) where T : class - { - using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) - { - if (requireAuthToken) - request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {configuration.AuthToken}"); - - var json = SerializeObject(payload); - request.Content = new StringContent(json, Encoding.UTF8, "application/json"); - var response = await configuration.HttpClient.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); - - return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : DeserializeObject(content); - } - } - - /// - /// Deserialize SlackMessage from a JSON string - /// - /// string containing serialized JSON - /// SlackMessage - public static T DeserializeObject(string json) where T : class - { - return JsonConvert.DeserializeObject(json, _serializerSettings); - } - - /// - /// Serialize SlackMessage to a JSON string - /// - /// An instance of SlackMessage - /// string containing serialized JSON - public static string SerializeObject(object obj) - { - return JsonConvert.SerializeObject(obj, _serializerSettings); - } - - } -} \ No newline at end of file diff --git a/src/Slack.Webhooks/Api/Chat.cs b/src/Slack.Webhooks/Api/Chat.cs deleted file mode 100644 index 21411b7..0000000 --- a/src/Slack.Webhooks/Api/Chat.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Slack.Webhooks.Api -{ - public class Chat : ApiBase - { - public Chat(SlackConfiguration configuration) : base(configuration) - { - } - - public SlackResponse PostMessage(SlackMessage message) - { - var uri = new Uri("https://slack.com/api/chat.postMessage"); - return PostAsync(uri, message).GetAwaiter().GetResult(); - } - - public async Task PostMessageAsync(SlackMessage message) - { - var uri = new Uri("https://slack.com/api/chat.postMessage"); - return await PostAsync(uri, message); - } - - - } -} \ No newline at end of file diff --git a/src/Slack.Webhooks/Api/Webhook.cs b/src/Slack.Webhooks/Api/Webhook.cs deleted file mode 100644 index 1d7eeeb..0000000 --- a/src/Slack.Webhooks/Api/Webhook.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Slack.Webhooks.Api -{ - public class Webhook : ApiBase - { - private const string POST_SUCCESS = "ok"; - public Webhook(SlackConfiguration configuration) : base(configuration) - { - } - - public virtual bool Post(SlackMessage slackMessage) - { - var result = PostWebhook(slackMessage).GetAwaiter().GetResult(); - return result; - } - - public bool PostToChannels(SlackMessage message, IEnumerable channels) - { - return channels.DefaultIfEmpty(message.Channel) - .Select(message.Clone) - .Select(Post).All(r => r); - } - - public IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels) - { - return channels.DefaultIfEmpty(message.Channel) - .Select(message.Clone) - .Select(PostWebhook); - } - - public async Task PostWebhook(SlackMessage slackMessage) - { - var response = await PostAsync(configuration.WebhookUri, slackMessage, false); - return response.Equals(POST_SUCCESS, StringComparison.OrdinalIgnoreCase); - } - } -} \ No newline at end of file diff --git a/src/Slack.Webhooks/Blocks/Actions.cs b/src/Slack.Webhooks/Blocks/ActionsBlock.cs similarity index 83% rename from src/Slack.Webhooks/Blocks/Actions.cs rename to src/Slack.Webhooks/Blocks/ActionsBlock.cs index 7d6dacf..ca7ca74 100644 --- a/src/Slack.Webhooks/Blocks/Actions.cs +++ b/src/Slack.Webhooks/Blocks/ActionsBlock.cs @@ -1,12 +1,14 @@ using System.Collections.Generic; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Blocks { /// /// A block that is used to hold interactive s. /// - public class Actions : Block + public class ActionsBlock : BlockBase { /// /// An array of interactive element objects - , menus, menus, or . @@ -24,9 +26,9 @@ public class Actions : Block public IList Elements { get; set; } /// - /// Create a new instance. + /// Create a new instance. /// - public Actions() : base(BlockType.Actions) + public ActionsBlock() : base(BlockType.Actions) { } } diff --git a/src/Slack.Webhooks/Block.cs b/src/Slack.Webhooks/Blocks/BlockBase.cs similarity index 55% rename from src/Slack.Webhooks/Block.cs rename to src/Slack.Webhooks/Blocks/BlockBase.cs index aed91bb..fb1ceec 100644 --- a/src/Slack.Webhooks/Block.cs +++ b/src/Slack.Webhooks/Blocks/BlockBase.cs @@ -1,4 +1,7 @@ -namespace Slack.Webhooks +using Slack.Webhooks.Classes; +using Slack.Webhooks.Message; + +namespace Slack.Webhooks.Blocks { /// /// Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. @@ -6,20 +9,20 @@ /// Read our guide to composing rich message layouts to learn where and how to use each of these components. You can include up to 50 blocks in each message. /// https://api.slack.com/messaging/composing/layouts /// - /// - /// - /// - /// - /// - /// - /// - public class Block + /// + /// + /// + /// + /// + /// + /// + public class BlockBase { - protected readonly BlockType _blockType; + protected readonly BlockType BlockType; /// - /// The of this instance of . + /// The of this instance of . /// - public BlockType Type { get { return _blockType; } } + public BlockType Type { get { return BlockType; } } /// /// A string acting as a unique identifier for a block. /// You can use this block_id when you receive an interaction payload to identify the source of the action. @@ -27,9 +30,9 @@ public class Block /// public string BlockId { get; set; } - protected Block(BlockType blockType) + protected BlockBase(BlockType blockType) { - _blockType = blockType; + BlockType = blockType; } } } diff --git a/src/Slack.Webhooks/Blocks/Context.cs b/src/Slack.Webhooks/Blocks/ContextBlock.cs similarity index 70% rename from src/Slack.Webhooks/Blocks/Context.cs rename to src/Slack.Webhooks/Blocks/ContextBlock.cs index 5327517..3f2a030 100644 --- a/src/Slack.Webhooks/Blocks/Context.cs +++ b/src/Slack.Webhooks/Blocks/ContextBlock.cs @@ -1,12 +1,14 @@ using System.Collections.Generic; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Blocks { /// /// Displays message context, which can include both images and text. /// - public class Context : Block + public class ContextBlock : BlockBase { /// /// An array of elements and s. Maximum number of items is 10. @@ -14,9 +16,9 @@ public class Context : Block public List Elements { get; set; } /// - /// Create a new instance. + /// Create a new instance. /// - public Context() : base(BlockType.Context) + public ContextBlock() : base(BlockType.Context) { } } diff --git a/src/Slack.Webhooks/Blocks/Divider.cs b/src/Slack.Webhooks/Blocks/Divider.cs deleted file mode 100644 index ec2f17e..0000000 --- a/src/Slack.Webhooks/Blocks/Divider.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Slack.Webhooks.Blocks -{ - /// - /// A content divider, like an <hr>, to split up different blocks inside of a message. The divider block is nice and neat, requiring only a . - /// - public class Divider : Block - { - /// - /// Create a new instance. - /// - public Divider() : base(BlockType.Divider) - { - } - } -} diff --git a/src/Slack.Webhooks/Blocks/DividerBlock.cs b/src/Slack.Webhooks/Blocks/DividerBlock.cs new file mode 100644 index 0000000..022e7fb --- /dev/null +++ b/src/Slack.Webhooks/Blocks/DividerBlock.cs @@ -0,0 +1,18 @@ +using Slack.Webhooks.Classes; +using Slack.Webhooks.Message; + +namespace Slack.Webhooks.Blocks +{ + /// + /// A content divider, like an <hr>, to split up different blocks inside of a message. The divider block is nice and neat, requiring only a . + /// + public class DividerBlock : BlockBase + { + /// + /// Create a new instance. + /// + public DividerBlock() : base(BlockType.Divider) + { + } + } +} diff --git a/src/Slack.Webhooks/Blocks/File.cs b/src/Slack.Webhooks/Blocks/FileBlock.cs similarity index 71% rename from src/Slack.Webhooks/Blocks/File.cs rename to src/Slack.Webhooks/Blocks/FileBlock.cs index 2a7a093..83a97ab 100644 --- a/src/Slack.Webhooks/Blocks/File.cs +++ b/src/Slack.Webhooks/Blocks/FileBlock.cs @@ -1,9 +1,12 @@ +using Slack.Webhooks.Classes; +using Slack.Webhooks.Message; + namespace Slack.Webhooks.Blocks { /// /// Displays a remote file. /// - public class File : Block + public class FileBlock : BlockBase { /// /// The external unique ID for this file. @@ -15,9 +18,9 @@ public class File : Block public string Source { get; set; } = "remote"; /// - /// Create a new instance. + /// Create a new instance. /// - public File() : base(BlockType.File) + public FileBlock() : base(BlockType.File) { } } diff --git a/src/Slack.Webhooks/Blocks/Image.cs b/src/Slack.Webhooks/Blocks/ImageBlock.cs similarity index 79% rename from src/Slack.Webhooks/Blocks/Image.cs rename to src/Slack.Webhooks/Blocks/ImageBlock.cs index 02abcad..3de19b3 100644 --- a/src/Slack.Webhooks/Blocks/Image.cs +++ b/src/Slack.Webhooks/Blocks/ImageBlock.cs @@ -1,12 +1,14 @@ -using Slack.Webhooks.Elements; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Elements; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Blocks { /// /// A simple image block, designed to make those cat photos really pop. /// - public class Image : Block, IContextElement + public class ImageBlock : BlockBase, IContextElement { /// /// The URL of the image to be displayed. Maximum length for this field is 3000 characters. @@ -23,9 +25,9 @@ public class Image : Block, IContextElement public TextObject Title { get; set; } /// - /// Create a new - public Image() : base(BlockType.Image) + public ImageBlock() : base(BlockType.Image) { } diff --git a/src/Slack.Webhooks/Blocks/Input.cs b/src/Slack.Webhooks/Blocks/InputBlock.cs similarity index 85% rename from src/Slack.Webhooks/Blocks/Input.cs rename to src/Slack.Webhooks/Blocks/InputBlock.cs index 69323e7..ade2e62 100644 --- a/src/Slack.Webhooks/Blocks/Input.cs +++ b/src/Slack.Webhooks/Blocks/InputBlock.cs @@ -1,5 +1,7 @@ +using Slack.Webhooks.Classes; using Slack.Webhooks.Elements; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Blocks { @@ -23,10 +25,10 @@ namespace Slack.Webhooks.Blocks /// /// /// - public class Input : Block + public class InputBlock : BlockBase { /// - /// A label that appears above an element in the form of a that can only be of . + /// A label that appears above an element in the form of a that can only be of . /// Maximum length for the text in this field is 2000 characters. /// public TextObject Label { get; set; } @@ -45,9 +47,9 @@ public class Input : Block public IInputElement Element { get; set; } /// - /// Create a new instance. + /// Create a new instance. /// - public Input() : base(BlockType.Input) + public InputBlock() : base(BlockType.Input) { } diff --git a/src/Slack.Webhooks/Blocks/Section.cs b/src/Slack.Webhooks/Blocks/SectionBlock.cs similarity index 76% rename from src/Slack.Webhooks/Blocks/Section.cs rename to src/Slack.Webhooks/Blocks/SectionBlock.cs index c942035..c5898be 100644 --- a/src/Slack.Webhooks/Blocks/Section.cs +++ b/src/Slack.Webhooks/Blocks/SectionBlock.cs @@ -1,13 +1,15 @@ using System.Collections.Generic; +using Slack.Webhooks.Classes; using Slack.Webhooks.Elements; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Blocks { /// - /// A is one of the most flexible blocks available - it can be used as a simple text block, + /// A is one of the most flexible blocks available - it can be used as a simple text block, /// in combination with text fields, or side-by-side with any of the available block elements. /// - public class Section : Block + public class SectionBlock : BlockBase { /// /// The for the block, in the form of a . Maximum length for the text in this field is 3000 characters. @@ -25,9 +27,9 @@ public class Section : Block public Element Accessory { get; set; } /// - /// Create a new instance. + /// Create a new instance. /// - public Section() : base(BlockType.Section) + public SectionBlock() : base(BlockType.Section) { } diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs new file mode 100644 index 0000000..95a06ae --- /dev/null +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Interfaces; + +namespace Slack.Webhooks.Chat +{ + public class ChatClient : ClientBase, IChatClient + { + public ChatClient(ClientConfiguration configuration) : base(configuration) + { + } + + public PostMessageResponse PostMessage(ChatMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postMessage"); + return PostAsync(uri, message).GetAwaiter().GetResult(); + } + + public async Task PostMessageAsync(ChatMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postMessage"); + return await PostAsync(uri, message); + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Chat/ChatMessage.cs b/src/Slack.Webhooks/Chat/ChatMessage.cs new file mode 100644 index 0000000..fb19ff8 --- /dev/null +++ b/src/Slack.Webhooks/Chat/ChatMessage.cs @@ -0,0 +1,16 @@ +using Slack.Webhooks.Messages; + +namespace Slack.Webhooks.Chat +{ + public class ChatMessage : MessageBase + { + public bool AsUser { get; set; } + public bool UnfurlLinks { get; set; } + public bool UnfurlMedia { get; set; } + + public bool ShouldSerializeAsUser() + { + return !string.IsNullOrWhiteSpace(Username); + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/SlackResponse.cs b/src/Slack.Webhooks/Chat/MessageResponse.cs similarity index 54% rename from src/Slack.Webhooks/SlackResponse.cs rename to src/Slack.Webhooks/Chat/MessageResponse.cs index e7efbb3..036ac35 100644 --- a/src/Slack.Webhooks/SlackResponse.cs +++ b/src/Slack.Webhooks/Chat/MessageResponse.cs @@ -1,23 +1,8 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; -namespace Slack.Webhooks +namespace Slack.Webhooks.Chat { - public class SlackResponse - { - [JsonProperty(PropertyName = "ok")] - public bool Ok { get; set; } - - [JsonProperty(PropertyName = "channel")] - public string Channel { get; set; } - - [JsonProperty(PropertyName = "ts")] - public string ThreadId { get; set; } - - [JsonProperty(PropertyName = "message")] - public Message Message { get; set; } - } - - public class Message + public class MessageResponse { [JsonProperty(PropertyName = "type")] public string Type { get; set; } diff --git a/src/Slack.Webhooks/Chat/PostMessageResponse.cs b/src/Slack.Webhooks/Chat/PostMessageResponse.cs new file mode 100644 index 0000000..0ac345e --- /dev/null +++ b/src/Slack.Webhooks/Chat/PostMessageResponse.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using Slack.Webhooks.Classes; + +namespace Slack.Webhooks.Chat +{ + public class PostMessageResponse : ResponseBase + { + [JsonProperty(PropertyName = "channel")] + public string Channel { get; set; } + + [JsonProperty(PropertyName = "ts")] + public string ThreadId { get; set; } + + [JsonProperty(PropertyName = "message")] + public MessageResponse Message { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Classes/ClientBase.cs b/src/Slack.Webhooks/Classes/ClientBase.cs new file mode 100644 index 0000000..43e0f72 --- /dev/null +++ b/src/Slack.Webhooks/Classes/ClientBase.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Slack.Webhooks.Helpers; + +namespace Slack.Webhooks.Classes +{ + public class ClientBase + { + protected readonly ClientConfiguration Configuration; + + protected ClientBase(ClientConfiguration configuration) + { + this.Configuration = configuration; + } + + protected async Task PostAsync(Uri uri, object payload, bool requireAuthToken = true) where T : class + { + using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) + { + if (requireAuthToken) + request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {Configuration.AuthToken}"); + + var json = SerializationHelper.Serialize(payload); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await Configuration.HttpClient.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + + return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : SerializationHelper.Deserialize(content); + } + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/SlackConfiguration.cs b/src/Slack.Webhooks/Classes/ClientConfiguration.cs similarity index 73% rename from src/Slack.Webhooks/SlackConfiguration.cs rename to src/Slack.Webhooks/Classes/ClientConfiguration.cs index 9cfd924..8ec120a 100644 --- a/src/Slack.Webhooks/SlackConfiguration.cs +++ b/src/Slack.Webhooks/Classes/ClientConfiguration.cs @@ -1,9 +1,9 @@ using System; using System.Net.Http; -namespace Slack.Webhooks +namespace Slack.Webhooks.Classes { - public class SlackConfiguration + public class ClientConfiguration { public Uri WebhookUri { get; set; } public string AuthToken { get; set; } diff --git a/src/Slack.Webhooks/Classes/ResponseBase.cs b/src/Slack.Webhooks/Classes/ResponseBase.cs new file mode 100644 index 0000000..c4ea4ab --- /dev/null +++ b/src/Slack.Webhooks/Classes/ResponseBase.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Slack.Webhooks.Classes +{ + public class ResponseBase + { + [JsonProperty(PropertyName = "ok")] + public bool Ok { get; set; } + + [JsonProperty(PropertyName = "error")] + public string Error { get; set; } + + [JsonProperty(PropertyName = "warning")] + public string Warning { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Elements/Button.cs b/src/Slack.Webhooks/Elements/Button.cs index f0cd04f..f86097e 100644 --- a/src/Slack.Webhooks/Elements/Button.cs +++ b/src/Slack.Webhooks/Elements/Button.cs @@ -1,9 +1,12 @@ +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with and blocks. + /// Works with and blocks. /// /// An interactive component that inserts a button. The button can be a trigger for anything from opening a simple link to starting a complex workflow. /// diff --git a/src/Slack.Webhooks/Elements/DatePicker.cs b/src/Slack.Webhooks/Elements/DatePicker.cs index 715aa3d..67ee43b 100644 --- a/src/Slack.Webhooks/Elements/DatePicker.cs +++ b/src/Slack.Webhooks/Elements/DatePicker.cs @@ -1,9 +1,12 @@ +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// An element which lets users easily select a date from a calendar style UI. /// diff --git a/src/Slack.Webhooks/Elements/Element.cs b/src/Slack.Webhooks/Elements/Element.cs index b87c131..129f3d6 100644 --- a/src/Slack.Webhooks/Elements/Element.cs +++ b/src/Slack.Webhooks/Elements/Element.cs @@ -1,3 +1,6 @@ +using Slack.Webhooks.Classes; +using Slack.Webhooks.Message; + namespace Slack.Webhooks.Elements { public class Element diff --git a/src/Slack.Webhooks/Elements/Image.cs b/src/Slack.Webhooks/Elements/Image.cs index 8491890..cb616b0 100644 --- a/src/Slack.Webhooks/Elements/Image.cs +++ b/src/Slack.Webhooks/Elements/Image.cs @@ -1,11 +1,14 @@ +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with and blocks. + /// Works with and blocks. /// - /// An element to insert an image as part of a larger block of content. If you want a block with only an image in it, you're looking for the block. + /// An element to insert an image as part of a larger block of content. If you want a block with only an image in it, you're looking for the block. /// public class Image : Element, IContextElement { diff --git a/src/Slack.Webhooks/Elements/MultiSelect.cs b/src/Slack.Webhooks/Elements/MultiSelect.cs index 6624567..6b170ed 100644 --- a/src/Slack.Webhooks/Elements/MultiSelect.cs +++ b/src/Slack.Webhooks/Elements/MultiSelect.cs @@ -1,10 +1,13 @@ using System.Collections.Generic; +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with and blocks. + /// Works with and blocks. /// /// This is the simplest form of select menu, with a static list of options passed in when defining the element. /// @@ -35,7 +38,7 @@ public MultiSelectStatic() : base(ElementType.MultiSelectStatic) } /// - /// Works with and blocks. + /// Works with and blocks. /// /// This menu will load its options from an external data source, allowing for a dynamic list of options. /// @@ -69,7 +72,7 @@ public MultiSelectExternal() : base(ElementType.MultiSelectExternal) } /// - /// Works with and blocks. + /// Works with and blocks. /// /// This multi-select menu will populate its options with a list of Slack users visible to the current user in the active workspace. /// @@ -86,7 +89,7 @@ public MultiSelectUsers() : base(ElementType.MultiSelectUsers) } /// - /// Works with and blocks. + /// Works with and blocks. /// /// This multi-select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. /// @@ -103,7 +106,7 @@ public MultiSelectConversations() : base(ElementType.MultiSelectConversations) } /// - /// Works with and blocks. + /// Works with and blocks. /// /// This multi-select menu will populate its options with a list of public channels visible to the current user in the active workspace. /// diff --git a/src/Slack.Webhooks/Elements/Overflow.cs b/src/Slack.Webhooks/Elements/Overflow.cs index 31b3a7f..c1f985a 100644 --- a/src/Slack.Webhooks/Elements/Overflow.cs +++ b/src/Slack.Webhooks/Elements/Overflow.cs @@ -1,10 +1,13 @@ using System.Collections.Generic; +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with and blocks. + /// Works with and blocks. /// /// This is like a cross between a button and a select menu - when a user clicks on this overflow button, they will be presented with a list of options to choose from. Unlike the select menu, there is no typeahead field, and the button always appears with an ellipsis ("…") rather than customisable text. /// diff --git a/src/Slack.Webhooks/Elements/PlainTextInput.cs b/src/Slack.Webhooks/Elements/PlainTextInput.cs index 48a6d23..9bd395a 100644 --- a/src/Slack.Webhooks/Elements/PlainTextInput.cs +++ b/src/Slack.Webhooks/Elements/PlainTextInput.cs @@ -1,9 +1,12 @@ +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// A plain-text input, similar to the HTML <input> tag, creates a field where a user can enter freeform data. It can appear as a single-line field or a larger textarea using the > flag. /// diff --git a/src/Slack.Webhooks/Elements/RadioButton.cs b/src/Slack.Webhooks/Elements/RadioButton.cs index de2502a..5585a4f 100644 --- a/src/Slack.Webhooks/Elements/RadioButton.cs +++ b/src/Slack.Webhooks/Elements/RadioButton.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// A radio button group that allows a user to choose one item from a list of possible options. /// diff --git a/src/Slack.Webhooks/Elements/Select.cs b/src/Slack.Webhooks/Elements/Select.cs index c366bbe..fab878b 100644 --- a/src/Slack.Webhooks/Elements/Select.cs +++ b/src/Slack.Webhooks/Elements/Select.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; +using Slack.Webhooks.Blocks; +using Slack.Webhooks.Classes; using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Message; namespace Slack.Webhooks.Elements { @@ -27,7 +30,7 @@ public Select(ElementType elementType) : base(elementType) } /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// This is the simplest form of select menu, with a static list of options passed in when defining the element. /// @@ -58,7 +61,7 @@ public SelectStatic() : base(ElementType.SelectStatic) } /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// This menu will load its options from an external data source, allowing for a dynamic list of options. /// @@ -92,7 +95,7 @@ public SelectExternal() : base(ElementType.SelectExternal) } /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// This select menu will populate its options with a list of Slack users visible to the current user in the active workspace. /// @@ -109,7 +112,7 @@ public SelectUsers() : base(ElementType.SelectUsers) } /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// This select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. /// @@ -126,7 +129,7 @@ public SelectConversations() : base(ElementType.SelectConversations) } /// - /// Works with , and blocks. + /// Works with , and blocks. /// /// This select menu will populate its options with a list of public channels visible to the current user in the active workspace. /// diff --git a/src/Slack.Webhooks/Helpers/SerializationHelper.cs b/src/Slack.Webhooks/Helpers/SerializationHelper.cs new file mode 100644 index 0000000..46686ff --- /dev/null +++ b/src/Slack.Webhooks/Helpers/SerializationHelper.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Slack.Webhooks.Helpers +{ + public static class SerializationHelper + { + private static readonly DefaultContractResolver Resolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }; + private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings + { + ContractResolver = Resolver, + NullValueHandling = NullValueHandling.Ignore + }; + + /// + /// Deserialize SlackMessage from a JSON string + /// + /// string containing serialized JSON + /// SlackMessage + public static T Deserialize(string json) where T : class + { + return JsonConvert.DeserializeObject(json, SerializerSettings); + } + + /// + /// Serialize SlackMessage to a JSON string + /// + /// Instance of an object to serialize + /// string containing serialized JSON + public static string Serialize(object obj) + { + return JsonConvert.SerializeObject(obj, SerializerSettings); + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/ISlackClient.cs b/src/Slack.Webhooks/ISlackClient.cs deleted file mode 100644 index 1123f73..0000000 --- a/src/Slack.Webhooks/ISlackClient.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Slack.Webhooks -{ - public interface ISlackClient - { - bool Post(SlackMessage slackMessage); - bool PostToChannels(SlackMessage message, IEnumerable channels); - IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels); - } -} diff --git a/src/Slack.Webhooks/Interfaces/IActionElement.cs b/src/Slack.Webhooks/Interfaces/IActionElement.cs index a5bd949..b646c25 100644 --- a/src/Slack.Webhooks/Interfaces/IActionElement.cs +++ b/src/Slack.Webhooks/Interfaces/IActionElement.cs @@ -1,7 +1,9 @@ +using Slack.Webhooks.Blocks; + namespace Slack.Webhooks.Interfaces { /// - /// Encapsulates elements compatible with the block. + /// Encapsulates elements compatible with the block. /// public interface IActionElement { diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs new file mode 100644 index 0000000..9ac29a1 --- /dev/null +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Slack.Webhooks.Chat; +using Slack.Webhooks.Messages; + +namespace Slack.Webhooks.Interfaces +{ + public interface IChatClient + { + PostMessageResponse PostMessage(ChatMessage message); + Task PostMessageAsync(ChatMessage message); + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Interfaces/IContextElement.cs b/src/Slack.Webhooks/Interfaces/IContextElement.cs index 6864427..66a4cf7 100644 --- a/src/Slack.Webhooks/Interfaces/IContextElement.cs +++ b/src/Slack.Webhooks/Interfaces/IContextElement.cs @@ -1,7 +1,9 @@ +using Slack.Webhooks.Blocks; + namespace Slack.Webhooks.Interfaces { /// - /// Encapsulates Elements compatible with the block. + /// Encapsulates Elements compatible with the block. /// public interface IContextElement { diff --git a/src/Slack.Webhooks/Interfaces/IInputElement.cs b/src/Slack.Webhooks/Interfaces/IInputElement.cs index a0ea636..6fa0927 100644 --- a/src/Slack.Webhooks/Interfaces/IInputElement.cs +++ b/src/Slack.Webhooks/Interfaces/IInputElement.cs @@ -1,7 +1,9 @@ +using Slack.Webhooks.Blocks; + namespace Slack.Webhooks.Interfaces { /// - /// Encapsulates elements compatible with the block. + /// Encapsulates elements compatible with the block. /// public interface IInputElement { diff --git a/src/Slack.Webhooks/Interfaces/IWebhookClient.cs b/src/Slack.Webhooks/Interfaces/IWebhookClient.cs new file mode 100644 index 0000000..a2975de --- /dev/null +++ b/src/Slack.Webhooks/Interfaces/IWebhookClient.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Slack.Webhooks.Webhook; + +namespace Slack.Webhooks.Interfaces +{ + public interface IWebhookClient + { + bool Post(WebhookMessage webhookMessage); + bool PostToChannels(WebhookMessage webhookMessage, IEnumerable channels); + IEnumerable> PostToChannelsAsync(WebhookMessage webhookMessage, IEnumerable channels); + } +} diff --git a/src/Slack.Webhooks/SlackAttachment.cs b/src/Slack.Webhooks/Messages/Attachment.cs similarity index 95% rename from src/Slack.Webhooks/SlackAttachment.cs rename to src/Slack.Webhooks/Messages/Attachment.cs index 591c60e..e6a7370 100644 --- a/src/Slack.Webhooks/SlackAttachment.cs +++ b/src/Slack.Webhooks/Messages/Attachment.cs @@ -1,13 +1,14 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; +using Newtonsoft.Json; +using Slack.Webhooks.Classes; -namespace Slack.Webhooks +namespace Slack.Webhooks.Message { /// /// Slack message attachment. A message can have zero or more attachments. /// - public class SlackAttachment + public class Attachment { private List _markdownIn; @@ -72,7 +73,7 @@ public class SlackAttachment /// /// Fields are displayed in a table on the message /// - public List Fields { get; set; } + public List Fields { get; set; } /// /// Optional list of proporties where markdown syntax will be parsed /// applicable to fields, title, and pretext @@ -115,6 +116,6 @@ public List MrkdwnIn /// /// The actions you provide will be rendered as message buttons or menus to users. /// - public List Actions { get; set; } + public List Actions { get; set; } } } diff --git a/src/Slack.Webhooks/Emoji.cs b/src/Slack.Webhooks/Messages/Emoji.cs similarity index 99% rename from src/Slack.Webhooks/Emoji.cs rename to src/Slack.Webhooks/Messages/Emoji.cs index f4f3cc0..bc41512 100644 --- a/src/Slack.Webhooks/Emoji.cs +++ b/src/Slack.Webhooks/Messages/Emoji.cs @@ -1,4 +1,4 @@ -namespace Slack.Webhooks +namespace Slack.Webhooks.Message { public class Emoji diff --git a/src/Slack.Webhooks/Enums.cs b/src/Slack.Webhooks/Messages/Enums.cs similarity index 95% rename from src/Slack.Webhooks/Enums.cs rename to src/Slack.Webhooks/Messages/Enums.cs index 25a2a51..317a606 100644 --- a/src/Slack.Webhooks/Enums.cs +++ b/src/Slack.Webhooks/Messages/Enums.cs @@ -1,8 +1,8 @@ +using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using System.Runtime.Serialization; -namespace Slack.Webhooks +namespace Slack.Webhooks.Message { [JsonConverter(typeof(StringEnumConverter))] public enum ParseMode @@ -14,7 +14,7 @@ public enum ParseMode } [JsonConverter(typeof(StringEnumConverter))] - public enum SlackActionDataSource + public enum ActionDataSource { [EnumMember(Value = "static")] Static, @@ -29,7 +29,7 @@ public enum SlackActionDataSource } [JsonConverter(typeof(StringEnumConverter))] - public enum SlackActionStyle + public enum ActionStyle { [EnumMember(Value = "default")] Default, @@ -40,7 +40,7 @@ public enum SlackActionStyle } [JsonConverter(typeof(StringEnumConverter))] - public enum SlackActionType + public enum ActionType { [EnumMember(Value = "button")] Button, diff --git a/src/Slack.Webhooks/SlackField.cs b/src/Slack.Webhooks/Messages/Field.cs similarity index 91% rename from src/Slack.Webhooks/SlackField.cs rename to src/Slack.Webhooks/Messages/Field.cs index 667d561..59aba3f 100644 --- a/src/Slack.Webhooks/SlackField.cs +++ b/src/Slack.Webhooks/Messages/Field.cs @@ -1,9 +1,9 @@ -namespace Slack.Webhooks +namespace Slack.Webhooks.Message { /// /// Slack Field. Fields are displayed in a table on the message /// - public class SlackField + public class Field { /// /// Required Field Title diff --git a/src/Slack.Webhooks/SlackMessage.cs b/src/Slack.Webhooks/Messages/MessageBase.cs similarity index 77% rename from src/Slack.Webhooks/SlackMessage.cs rename to src/Slack.Webhooks/Messages/MessageBase.cs index 7386b06..fa9bcfc 100644 --- a/src/Slack.Webhooks/SlackMessage.cs +++ b/src/Slack.Webhooks/Messages/MessageBase.cs @@ -1,14 +1,17 @@ -using Newtonsoft.Json; -using Slack.Webhooks.Api; using System; using System.Collections.Generic; +using Newtonsoft.Json; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Helpers; +using Slack.Webhooks.Message; +using BlockBase = Slack.Webhooks.Blocks.BlockBase; -namespace Slack.Webhooks +namespace Slack.Webhooks.Messages { /// /// Slack Message /// - public class SlackMessage + public class MessageBase { private bool _markdown = true; /// @@ -78,10 +81,10 @@ public bool Mrkdwn /// /// Optional attachment collection /// - public List Attachments { get; set; } + public List Attachments { get; set; } /// - /// Optional collection of + /// Optional collection of /// /// /// @@ -90,30 +93,7 @@ public bool Mrkdwn /// /// /// - public List Blocks { get; set; } - - /// - /// Create a clone of this overriding the channel if provided - /// - public SlackMessage Clone(string newChannel = null) - { - return new SlackMessage() - { - Attachments = Attachments, - Blocks = Blocks, - Channel = newChannel ?? Channel, - DeleteOriginal = DeleteOriginal, - IconEmoji = IconEmoji, - IconUrl = IconUrl, - LinkNames = LinkNames, - Markdown = Markdown, - Parse = Parse, - ReplaceOriginal = ReplaceOriginal, - ResponseType = ResponseType, - Text = Text, - Username = Username - }; - } + public List Blocks { get; set; } /// /// Conditional serialization of IconEmoji @@ -131,7 +111,7 @@ public bool ShouldSerializeIconEmoji() /// JSON formatted string public string AsJson() { - return ApiBase.SerializeObject(this); + return SerializationHelper.Serialize(this); } } } diff --git a/src/Slack.Webhooks/SlackClient.cs b/src/Slack.Webhooks/SlackClient.cs index ea9828d..32ad73d 100644 --- a/src/Slack.Webhooks/SlackClient.cs +++ b/src/Slack.Webhooks/SlackClient.cs @@ -2,27 +2,31 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; -using Slack.Webhooks.Api; +using Slack.Webhooks.Chat; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Helpers; +using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Messages; +using Slack.Webhooks.Webhook; namespace Slack.Webhooks { - public class SlackClient : ISlackClient, IDisposable + public class SlackClient : IDisposable { - private readonly SlackConfiguration _configuration; - public int TimeoutMs { get { return (int)_configuration.HttpClient.Timeout.TotalMilliseconds; } } + private readonly ClientConfiguration _configuration; + public int TimeoutMs => (int)_configuration.HttpClient.Timeout.TotalMilliseconds; #region Api - public Webhook Webhook { get; private set; } - public Chat Chat { get; private set; } + public WebhookClient Webhook { get; private set; } + public ChatClient Chat { get; private set; } #endregion public SlackClient(string webhookUrl = null, int timeout = 100, HttpClient httpClient = null) { - Uri webhookUri; - if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out webhookUri)) + if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out var webhookUri)) throw new ArgumentException("Please enter a valid webhook url"); - _configuration = new SlackConfiguration + _configuration = new ClientConfiguration { HttpClient = httpClient ?? new HttpClient { Timeout = TimeSpan.FromSeconds(timeout) }, WebhookUri = webhookUri @@ -31,57 +35,16 @@ public SlackClient(string webhookUrl = null, int timeout = 100, HttpClient httpC Configure(); } - public SlackClient(SlackConfiguration configuration) + public SlackClient(ClientConfiguration configuration) { _configuration = configuration; - Configure(); } private void Configure() { - Webhook = new Webhook(_configuration); - Chat = new Chat(_configuration); - } - - [Obsolete("Please use SlackClient.Webhook.Post(SlackMessage) instead.")] - public virtual bool Post(SlackMessage slackMessage) - { - return Webhook.Post(slackMessage); - } - - [Obsolete("Please use SlackClient.Webhook.PostToChannels(SlackMessage, IEnumerable) instead.")] - public bool PostToChannels(SlackMessage message, IEnumerable channels) - { - return Webhook.PostToChannels(message, channels); - } - - [Obsolete("Please use SlackClient.Webhook.PostToChannelsAsync(SlackMessage, IEnumerable) instead.")] - public IEnumerable> PostToChannelsAsync(SlackMessage message, IEnumerable channels) - { - return Webhook.PostToChannelsAsync(message, channels); - } - - /// - /// Deserialize SlackMessage from a JSON string - /// - /// string containing serialized JSON - /// SlackMessage - [Obsolete("Please use ApiBase.DeserializeObject instead.")] - public static SlackMessage DeserializeObject(string json) - { - return ApiBase.DeserializeObject(json); - } - - /// - /// Serialize SlackMessage to a JSON string - /// - /// An instance of SlackMessage - /// string containing serialized JSON - [Obsolete("Please use ApiBase.SerializeObject instead.")] - public static string SerializeObject(object obj) - { - return ApiBase.SerializeObject(obj); + Webhook = new Webhook.WebhookClient(_configuration); + Chat = new ChatClient(_configuration); } public void Dispose() diff --git a/src/Slack.Webhooks/Webhook/WebhookClient.cs b/src/Slack.Webhooks/Webhook/WebhookClient.cs new file mode 100644 index 0000000..1fc698b --- /dev/null +++ b/src/Slack.Webhooks/Webhook/WebhookClient.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Slack.Webhooks.Classes; +using Slack.Webhooks.Interfaces; +using Slack.Webhooks.Messages; + +namespace Slack.Webhooks.Webhook +{ + public class WebhookClient : ClientBase, IWebhookClient + { + private const string PostSuccess = "ok"; + public WebhookClient(ClientConfiguration configuration) : base(configuration) + { + } + + public virtual bool Post(WebhookMessage message) + { + var result = PostWebhook(message).GetAwaiter().GetResult(); + return result; + } + + public bool PostToChannels(WebhookMessage message, IEnumerable channels) + { + return channels.DefaultIfEmpty(message.Channel) + .Select(message.Clone) + .Select(Post).All(r => r); + } + + public IEnumerable> PostToChannelsAsync(WebhookMessage message, IEnumerable channels) + { + return channels.DefaultIfEmpty(message.Channel) + .Select(message.Clone) + .Select(PostWebhook); + } + + public async Task PostWebhook(WebhookMessage message) + { + var response = await PostAsync(Configuration.WebhookUri, message, false); + return response.Equals(PostSuccess, StringComparison.OrdinalIgnoreCase); + } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Webhook/WebhookMessage.cs b/src/Slack.Webhooks/Webhook/WebhookMessage.cs new file mode 100644 index 0000000..21d973e --- /dev/null +++ b/src/Slack.Webhooks/Webhook/WebhookMessage.cs @@ -0,0 +1,30 @@ +using Slack.Webhooks.Messages; + +namespace Slack.Webhooks.Webhook +{ + public class WebhookMessage : MessageBase + { + /// + /// Create a clone of this overriding the channel if provided + /// + public WebhookMessage Clone(string newChannel = null) + { + return new WebhookMessage + { + Attachments = Attachments, + Blocks = Blocks, + Channel = newChannel ?? Channel, + DeleteOriginal = DeleteOriginal, + IconEmoji = IconEmoji, + IconUrl = IconUrl, + LinkNames = LinkNames, + Markdown = Markdown, + Parse = Parse, + ReplaceOriginal = ReplaceOriginal, + ResponseType = ResponseType, + Text = Text, + Username = Username + }; + } + } +} \ No newline at end of file From 38c4dfddfcb4784e19e0b49f0eea7be3af2024a0 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Wed, 4 Mar 2020 14:30:27 +0000 Subject: [PATCH 14/20] ConfigureAwait workaround --- src/Slack.Webhooks/Classes/ClientBase.cs | 8 +++---- .../Interfaces/IWebhookClient.cs | 3 ++- src/Slack.Webhooks/Webhook/WebhookClient.cs | 22 ++++++++++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Slack.Webhooks/Classes/ClientBase.cs b/src/Slack.Webhooks/Classes/ClientBase.cs index 43e0f72..669ff8e 100644 --- a/src/Slack.Webhooks/Classes/ClientBase.cs +++ b/src/Slack.Webhooks/Classes/ClientBase.cs @@ -3,8 +3,6 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using Slack.Webhooks.Helpers; namespace Slack.Webhooks.Classes @@ -18,7 +16,7 @@ protected ClientBase(ClientConfiguration configuration) this.Configuration = configuration; } - protected async Task PostAsync(Uri uri, object payload, bool requireAuthToken = true) where T : class + protected async Task PostAsync(Uri uri, object payload, bool requireAuthToken = true, bool configureAwait = true) where T : class { using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) { @@ -27,8 +25,8 @@ protected async Task PostAsync(Uri uri, object payload, bool requireAuthTo var json = SerializationHelper.Serialize(payload); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); - var response = await Configuration.HttpClient.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); + var response = await Configuration.HttpClient.SendAsync(request).ConfigureAwait(configureAwait); + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(configureAwait); return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : SerializationHelper.Deserialize(content); } diff --git a/src/Slack.Webhooks/Interfaces/IWebhookClient.cs b/src/Slack.Webhooks/Interfaces/IWebhookClient.cs index a2975de..b54af5f 100644 --- a/src/Slack.Webhooks/Interfaces/IWebhookClient.cs +++ b/src/Slack.Webhooks/Interfaces/IWebhookClient.cs @@ -7,7 +7,8 @@ namespace Slack.Webhooks.Interfaces public interface IWebhookClient { bool Post(WebhookMessage webhookMessage); + Task PostAsync(WebhookMessage webhookMessage); bool PostToChannels(WebhookMessage webhookMessage, IEnumerable channels); - IEnumerable> PostToChannelsAsync(WebhookMessage webhookMessage, IEnumerable channels); + Task PostToChannelsAsync(WebhookMessage webhookMessage, IEnumerable channels); } } diff --git a/src/Slack.Webhooks/Webhook/WebhookClient.cs b/src/Slack.Webhooks/Webhook/WebhookClient.cs index 1fc698b..4e97f5e 100644 --- a/src/Slack.Webhooks/Webhook/WebhookClient.cs +++ b/src/Slack.Webhooks/Webhook/WebhookClient.cs @@ -17,10 +17,15 @@ public WebhookClient(ClientConfiguration configuration) : base(configuration) public virtual bool Post(WebhookMessage message) { - var result = PostWebhook(message).GetAwaiter().GetResult(); + var result = PostWebhook(message, false).Result; return result; } + public async Task PostAsync(WebhookMessage webhookMessage) + { + return await PostWebhook(webhookMessage); + } + public bool PostToChannels(WebhookMessage message, IEnumerable channels) { return channels.DefaultIfEmpty(message.Channel) @@ -28,16 +33,23 @@ public bool PostToChannels(WebhookMessage message, IEnumerable channels) .Select(Post).All(r => r); } - public IEnumerable> PostToChannelsAsync(WebhookMessage message, IEnumerable channels) + public async Task PostToChannelsAsync(WebhookMessage message, IEnumerable channels) { - return channels.DefaultIfEmpty(message.Channel) + var tasks = channels.DefaultIfEmpty(message.Channel) .Select(message.Clone) .Select(PostWebhook); + var results = await Task.WhenAll(tasks); + return results.ToList().All(r => r); + } + + private async Task PostWebhook(WebhookMessage message) + { + return await PostWebhook(message, true); } - public async Task PostWebhook(WebhookMessage message) + private async Task PostWebhook(WebhookMessage message, bool configureAwait) { - var response = await PostAsync(Configuration.WebhookUri, message, false); + var response = await PostAsync(Configuration.WebhookUri, message, false, configureAwait); return response.Equals(PostSuccess, StringComparison.OrdinalIgnoreCase); } } From 8fdcd2430f4a2d646a01be1c933ac1c574153970 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Wed, 4 Mar 2020 16:21:23 +0000 Subject: [PATCH 15/20] Implement chat.delete --- src/Slack.Webhooks/Chat/ChatClient.cs | 14 +++++++++++++- src/Slack.Webhooks/Chat/DeleteRequest.cs | 11 +++++++++++ src/Slack.Webhooks/Chat/DeleteResponse.cs | 12 ++++++++++++ src/Slack.Webhooks/Interfaces/IChatClient.cs | 4 ++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/Slack.Webhooks/Chat/DeleteRequest.cs create mode 100644 src/Slack.Webhooks/Chat/DeleteResponse.cs diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs index 95a06ae..9d94b36 100644 --- a/src/Slack.Webhooks/Chat/ChatClient.cs +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -11,10 +11,22 @@ public ChatClient(ClientConfiguration configuration) : base(configuration) { } + public DeleteResponse Delete(DeleteRequest request) + { + var uri = new Uri("https://slack.com/api/chat.delete"); + return PostAsync(uri, request, configureAwait: false).Result; + } + + public Task DeleteAsync(DeleteRequest request) + { + var uri = new Uri("https://slack.com/api/chat.delete"); + return PostAsync(uri, request); + } + public PostMessageResponse PostMessage(ChatMessage message) { var uri = new Uri("https://slack.com/api/chat.postMessage"); - return PostAsync(uri, message).GetAwaiter().GetResult(); + return PostAsync(uri, message, configureAwait: false).Result; } public async Task PostMessageAsync(ChatMessage message) diff --git a/src/Slack.Webhooks/Chat/DeleteRequest.cs b/src/Slack.Webhooks/Chat/DeleteRequest.cs new file mode 100644 index 0000000..40bd684 --- /dev/null +++ b/src/Slack.Webhooks/Chat/DeleteRequest.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; + +namespace Slack.Webhooks.Chat +{ + public class DeleteRequest + { + public string Channel { get; set; } + [JsonProperty(PropertyName = "ts")] + public string ThreadId { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Chat/DeleteResponse.cs b/src/Slack.Webhooks/Chat/DeleteResponse.cs new file mode 100644 index 0000000..a098ca7 --- /dev/null +++ b/src/Slack.Webhooks/Chat/DeleteResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using Slack.Webhooks.Classes; + +namespace Slack.Webhooks.Chat +{ + public class DeleteResponse : ResponseBase + { + public string Channel { get; set; } + [JsonProperty(PropertyName = "ts")] + public string ThreadId { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs index 9ac29a1..2b0deed 100644 --- a/src/Slack.Webhooks/Interfaces/IChatClient.cs +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -6,6 +6,10 @@ namespace Slack.Webhooks.Interfaces { public interface IChatClient { + DeleteResponse Delete(DeleteRequest request); + Task DeleteAsync(DeleteRequest request); + + PostMessageResponse PostMessage(ChatMessage message); Task PostMessageAsync(ChatMessage message); } From 0621705cf0ef5244d1fe56c98ba6cd8673cf83e2 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Wed, 4 Mar 2020 16:29:54 +0000 Subject: [PATCH 16/20] Rename ChatMessage to PostMessage --- src/Slack.Webhooks/Chat/ChatClient.cs | 4 ++-- src/Slack.Webhooks/Chat/{ChatMessage.cs => PostMessage.cs} | 2 +- src/Slack.Webhooks/Interfaces/IChatClient.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Slack.Webhooks/Chat/{ChatMessage.cs => PostMessage.cs} (88%) diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs index 9d94b36..27e269f 100644 --- a/src/Slack.Webhooks/Chat/ChatClient.cs +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -23,13 +23,13 @@ public Task DeleteAsync(DeleteRequest request) return PostAsync(uri, request); } - public PostMessageResponse PostMessage(ChatMessage message) + public PostMessageResponse PostMessage(PostMessage message) { var uri = new Uri("https://slack.com/api/chat.postMessage"); return PostAsync(uri, message, configureAwait: false).Result; } - public async Task PostMessageAsync(ChatMessage message) + public async Task PostMessageAsync(PostMessage message) { var uri = new Uri("https://slack.com/api/chat.postMessage"); return await PostAsync(uri, message); diff --git a/src/Slack.Webhooks/Chat/ChatMessage.cs b/src/Slack.Webhooks/Chat/PostMessage.cs similarity index 88% rename from src/Slack.Webhooks/Chat/ChatMessage.cs rename to src/Slack.Webhooks/Chat/PostMessage.cs index fb19ff8..b1c666d 100644 --- a/src/Slack.Webhooks/Chat/ChatMessage.cs +++ b/src/Slack.Webhooks/Chat/PostMessage.cs @@ -2,7 +2,7 @@ namespace Slack.Webhooks.Chat { - public class ChatMessage : MessageBase + public class PostMessage : MessageBase { public bool AsUser { get; set; } public bool UnfurlLinks { get; set; } diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs index 2b0deed..829df13 100644 --- a/src/Slack.Webhooks/Interfaces/IChatClient.cs +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -10,7 +10,7 @@ public interface IChatClient Task DeleteAsync(DeleteRequest request); - PostMessageResponse PostMessage(ChatMessage message); - Task PostMessageAsync(ChatMessage message); + PostMessageResponse PostMessage(PostMessage message); + Task PostMessageAsync(PostMessage message); } } \ No newline at end of file From d0ba4e0c3a69996d18a29f01c88bb25d941c98a8 Mon Sep 17 00:00:00 2001 From: Benn Lazell Date: Wed, 4 Mar 2020 17:15:03 +0000 Subject: [PATCH 17/20] WIP: chat.getPermalink --- src/Slack.Webhooks/Chat/ChatClient.cs | 12 +++++ src/Slack.Webhooks/Chat/PermalinkRequest.cs | 11 +++++ src/Slack.Webhooks/Chat/PermalinkResponse.cs | 10 ++++ src/Slack.Webhooks/Classes/ClientBase.cs | 17 +++++++ .../Helpers/SerializationHelper.cs | 48 ++++++++++++++++++- src/Slack.Webhooks/Interfaces/IChatClient.cs | 5 +- 6 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 src/Slack.Webhooks/Chat/PermalinkRequest.cs create mode 100644 src/Slack.Webhooks/Chat/PermalinkResponse.cs diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs index 27e269f..3a87b37 100644 --- a/src/Slack.Webhooks/Chat/ChatClient.cs +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -23,6 +23,18 @@ public Task DeleteAsync(DeleteRequest request) return PostAsync(uri, request); } + public PermalinkResponse Permalink(PermalinkRequest request) + { + var uri = new Uri("https://slack.com/api/chat.getPermalink"); + return GetAsync(uri, request, configureAwait: false).Result; + } + + public Task PermalinkAsync(PermalinkRequest request) + { + var uri = new Uri("https://slack.com/api/chat.getPermalink"); + return GetAsync(uri, request); + } + public PostMessageResponse PostMessage(PostMessage message) { var uri = new Uri("https://slack.com/api/chat.postMessage"); diff --git a/src/Slack.Webhooks/Chat/PermalinkRequest.cs b/src/Slack.Webhooks/Chat/PermalinkRequest.cs new file mode 100644 index 0000000..fd893f8 --- /dev/null +++ b/src/Slack.Webhooks/Chat/PermalinkRequest.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; + +namespace Slack.Webhooks.Chat +{ + public class PermalinkRequest + { + public string Channel { get; set; } + [JsonProperty(PropertyName = "message_ts")] + public string ThreadId { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Chat/PermalinkResponse.cs b/src/Slack.Webhooks/Chat/PermalinkResponse.cs new file mode 100644 index 0000000..1b7503d --- /dev/null +++ b/src/Slack.Webhooks/Chat/PermalinkResponse.cs @@ -0,0 +1,10 @@ +using Slack.Webhooks.Classes; + +namespace Slack.Webhooks.Chat +{ + public class PermalinkResponse: ResponseBase + { + public string Channel { get; set; } + public string Permalink { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Classes/ClientBase.cs b/src/Slack.Webhooks/Classes/ClientBase.cs index 669ff8e..58bcf38 100644 --- a/src/Slack.Webhooks/Classes/ClientBase.cs +++ b/src/Slack.Webhooks/Classes/ClientBase.cs @@ -31,5 +31,22 @@ protected async Task PostAsync(Uri uri, object payload, bool requireAuthTo return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : SerializationHelper.Deserialize(content); } } + + protected async Task GetAsync(Uri uri, object payload, bool requireAuthToken = true, + bool configureAwait = true) where T : class + { + using (var request = new HttpRequestMessage(HttpMethod.Get, uri)) + { + if (requireAuthToken) + request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {Configuration.AuthToken}"); + + var dictionary = SerializationHelper.ToDictionary(payload); + request.Content = new FormUrlEncodedContent(dictionary); + var response = await Configuration.HttpClient.SendAsync(request).ConfigureAwait(configureAwait); + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(configureAwait); + + return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : SerializationHelper.Deserialize(content); + } + } } } \ No newline at end of file diff --git a/src/Slack.Webhooks/Helpers/SerializationHelper.cs b/src/Slack.Webhooks/Helpers/SerializationHelper.cs index 46686ff..6630a01 100644 --- a/src/Slack.Webhooks/Helpers/SerializationHelper.cs +++ b/src/Slack.Webhooks/Helpers/SerializationHelper.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; namespace Slack.Webhooks.Helpers @@ -31,5 +35,47 @@ public static string Serialize(object obj) { return JsonConvert.SerializeObject(obj, SerializerSettings); } + + public static IDictionary ToDictionary(object obj) + { + if (obj == null) + { + return null; + } + + var token = obj as JToken; + if (token == null) + { + return ToDictionary(JObject.FromObject(obj, JsonSerializer.Create(SerializerSettings))); + } + + if (token.HasValues) + { + var contentData = new Dictionary(); + foreach (var child in token.Children().ToList()) + { + var childContent = ToDictionary(child); + if (childContent != null) + { + contentData = contentData.Concat(childContent) + .ToDictionary(k => k.Key, v => v.Value); + } + } + + return contentData; + } + + var jValue = token as JValue; + if (jValue?.Value == null) + { + return null; + } + + var value = jValue?.Type == JTokenType.Date ? + jValue?.ToString("o", CultureInfo.InvariantCulture) : + jValue?.ToString(CultureInfo.InvariantCulture); + + return new Dictionary { { token.Path, value } }; + } } } \ No newline at end of file diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs index 829df13..b24bc24 100644 --- a/src/Slack.Webhooks/Interfaces/IChatClient.cs +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -8,8 +8,9 @@ public interface IChatClient { DeleteResponse Delete(DeleteRequest request); Task DeleteAsync(DeleteRequest request); - - + + PermalinkResponse Permalink(PermalinkRequest request); + Task PermalinkAsync(PermalinkRequest request); PostMessageResponse PostMessage(PostMessage message); Task PostMessageAsync(PostMessage message); } From 4287cac1a6dab0cded3d4c2c031c4ae9497bedd4 Mon Sep 17 00:00:00 2001 From: Benn Date: Wed, 4 Mar 2020 20:40:46 +0000 Subject: [PATCH 18/20] Fix GetAsync method to correctly set the query data --- src/Slack.Webhooks/Classes/ClientBase.cs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Slack.Webhooks/Classes/ClientBase.cs b/src/Slack.Webhooks/Classes/ClientBase.cs index 58bcf38..7f21702 100644 --- a/src/Slack.Webhooks/Classes/ClientBase.cs +++ b/src/Slack.Webhooks/Classes/ClientBase.cs @@ -3,6 +3,7 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; +using System.Web; using Slack.Webhooks.Helpers; namespace Slack.Webhooks.Classes @@ -32,21 +33,27 @@ protected async Task PostAsync(Uri uri, object payload, bool requireAuthTo } } - protected async Task GetAsync(Uri uri, object payload, bool requireAuthToken = true, - bool configureAwait = true) where T : class + protected async Task GetAsync(Uri uri, object payload, bool configureAwait = true) where T : class { - using (var request = new HttpRequestMessage(HttpMethod.Get, uri)) + var builder = new UriBuilder(uri) {Query = await GetQueryStringAsync(uri, payload)}; + using (var request = new HttpRequestMessage(HttpMethod.Get, builder.Uri)) { - if (requireAuthToken) - request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {Configuration.AuthToken}"); - - var dictionary = SerializationHelper.ToDictionary(payload); - request.Content = new FormUrlEncodedContent(dictionary); var response = await Configuration.HttpClient.SendAsync(request).ConfigureAwait(configureAwait); var content = await response.Content.ReadAsStringAsync().ConfigureAwait(configureAwait); return typeof(T) == typeof(string) ? (T)Convert.ChangeType(content, typeof(T)) : SerializationHelper.Deserialize(content); } } + + private async Task GetQueryStringAsync(Uri uri, object payload) + { + var dictionary = SerializationHelper.ToDictionary(payload); + dictionary.Add("token", Configuration.AuthToken); + + var existingQuery = HttpUtility.ParseQueryString(uri.Query); + foreach (string key in existingQuery) dictionary.Add(key, existingQuery[key]); + + return await new FormUrlEncodedContent(dictionary).ReadAsStringAsync(); + } } } \ No newline at end of file From d0b9917669a468641e7a9378cea6b0987b5172ed Mon Sep 17 00:00:00 2001 From: Benn Date: Thu, 5 Mar 2020 08:41:49 +0000 Subject: [PATCH 19/20] Rename Permalink methods to match API --- src/Slack.Webhooks/Chat/ChatClient.cs | 4 ++-- src/Slack.Webhooks/Interfaces/IChatClient.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs index 3a87b37..e4ebaf0 100644 --- a/src/Slack.Webhooks/Chat/ChatClient.cs +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -23,13 +23,13 @@ public Task DeleteAsync(DeleteRequest request) return PostAsync(uri, request); } - public PermalinkResponse Permalink(PermalinkRequest request) + public PermalinkResponse GetPermalink(PermalinkRequest request) { var uri = new Uri("https://slack.com/api/chat.getPermalink"); return GetAsync(uri, request, configureAwait: false).Result; } - public Task PermalinkAsync(PermalinkRequest request) + public Task GetPermalinkAsync(PermalinkRequest request) { var uri = new Uri("https://slack.com/api/chat.getPermalink"); return GetAsync(uri, request); diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs index b24bc24..f536267 100644 --- a/src/Slack.Webhooks/Interfaces/IChatClient.cs +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -9,8 +9,8 @@ public interface IChatClient DeleteResponse Delete(DeleteRequest request); Task DeleteAsync(DeleteRequest request); - PermalinkResponse Permalink(PermalinkRequest request); - Task PermalinkAsync(PermalinkRequest request); + PermalinkResponse GetPermalink(PermalinkRequest request); + Task GetPermalinkAsync(PermalinkRequest request); PostMessageResponse PostMessage(PostMessage message); Task PostMessageAsync(PostMessage message); } From 5e6073cd4614f9805fb65b6c93413cacaae008a8 Mon Sep 17 00:00:00 2001 From: Benn Date: Thu, 5 Mar 2020 09:53:29 +0000 Subject: [PATCH 20/20] Implement chat.postEphemeral method --- src/Slack.Webhooks/Chat/ChatClient.cs | 20 ++++++++--- .../Chat/PostEphemeralMessage.cs | 10 ++++++ .../Chat/PostEphemeralResponse.cs | 11 +++++++ src/Slack.Webhooks/Interfaces/IChatClient.cs | 3 +- src/Slack.Webhooks/Messages/MessageBase.cs | 33 +++++-------------- 5 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 src/Slack.Webhooks/Chat/PostEphemeralMessage.cs create mode 100644 src/Slack.Webhooks/Chat/PostEphemeralResponse.cs diff --git a/src/Slack.Webhooks/Chat/ChatClient.cs b/src/Slack.Webhooks/Chat/ChatClient.cs index e4ebaf0..de38689 100644 --- a/src/Slack.Webhooks/Chat/ChatClient.cs +++ b/src/Slack.Webhooks/Chat/ChatClient.cs @@ -17,10 +17,10 @@ public DeleteResponse Delete(DeleteRequest request) return PostAsync(uri, request, configureAwait: false).Result; } - public Task DeleteAsync(DeleteRequest request) + public async Task DeleteAsync(DeleteRequest request) { var uri = new Uri("https://slack.com/api/chat.delete"); - return PostAsync(uri, request); + return await PostAsync(uri, request); } public PermalinkResponse GetPermalink(PermalinkRequest request) @@ -29,10 +29,22 @@ public PermalinkResponse GetPermalink(PermalinkRequest request) return GetAsync(uri, request, configureAwait: false).Result; } - public Task GetPermalinkAsync(PermalinkRequest request) + public async Task GetPermalinkAsync(PermalinkRequest request) { var uri = new Uri("https://slack.com/api/chat.getPermalink"); - return GetAsync(uri, request); + return await GetAsync(uri, request); + } + + public PostEphemeralResponse PostEphemeral(PostEphemeralMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postEphemeral"); + return PostAsync(uri, message, configureAwait: false).Result; + } + + public async Task PostEphemeralAsync(PostEphemeralMessage message) + { + var uri = new Uri("https://slack.com/api/chat.postEphemeral"); + return await PostAsync(uri, message); } public PostMessageResponse PostMessage(PostMessage message) diff --git a/src/Slack.Webhooks/Chat/PostEphemeralMessage.cs b/src/Slack.Webhooks/Chat/PostEphemeralMessage.cs new file mode 100644 index 0000000..9bebdcd --- /dev/null +++ b/src/Slack.Webhooks/Chat/PostEphemeralMessage.cs @@ -0,0 +1,10 @@ +using Slack.Webhooks.Messages; + +namespace Slack.Webhooks.Chat +{ + public class PostEphemeralMessage : MessageBase + { + public string User { get; set; } + public bool? AsUser { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Chat/PostEphemeralResponse.cs b/src/Slack.Webhooks/Chat/PostEphemeralResponse.cs new file mode 100644 index 0000000..d0aa094 --- /dev/null +++ b/src/Slack.Webhooks/Chat/PostEphemeralResponse.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using Slack.Webhooks.Classes; + +namespace Slack.Webhooks.Chat +{ + public class PostEphemeralResponse : ResponseBase + { + [JsonProperty(PropertyName = "thread_ts")] + public string ThreadId { get; set; } + } +} \ No newline at end of file diff --git a/src/Slack.Webhooks/Interfaces/IChatClient.cs b/src/Slack.Webhooks/Interfaces/IChatClient.cs index f536267..64468a7 100644 --- a/src/Slack.Webhooks/Interfaces/IChatClient.cs +++ b/src/Slack.Webhooks/Interfaces/IChatClient.cs @@ -8,9 +8,10 @@ public interface IChatClient { DeleteResponse Delete(DeleteRequest request); Task DeleteAsync(DeleteRequest request); - PermalinkResponse GetPermalink(PermalinkRequest request); Task GetPermalinkAsync(PermalinkRequest request); + PostEphemeralResponse PostEphemeral(PostEphemeralMessage message); + Task PostEphemeralAsync(PostEphemeralMessage message); PostMessageResponse PostMessage(PostMessage message); Task PostMessageAsync(PostMessage message); } diff --git a/src/Slack.Webhooks/Messages/MessageBase.cs b/src/Slack.Webhooks/Messages/MessageBase.cs index fa9bcfc..93d36ac 100644 --- a/src/Slack.Webhooks/Messages/MessageBase.cs +++ b/src/Slack.Webhooks/Messages/MessageBase.cs @@ -13,7 +13,6 @@ namespace Slack.Webhooks.Messages /// public class MessageBase { - private bool _markdown = true; /// /// This is the text that will be posted to the channel /// @@ -50,21 +49,7 @@ public class MessageBase /// Optional override markdown mode. Default: true /// [JsonProperty(PropertyName = "mrkdwn")] - public bool Markdown - { - get { return _markdown; } - set { _markdown = value; } - } - /// - /// Optional override markdown mode. Default: true - /// - [Obsolete("Mrkdwn has been deprecated, please use 'Markdown' instead.")] - [JsonIgnore] - public bool Mrkdwn - { - get { return _markdown; } - set { _markdown = value; } - } + public bool Markdown { get; set; } = true; /// /// Enable linkification of channel and usernames /// @@ -84,15 +69,15 @@ public bool Mrkdwn public List Attachments { get; set; } /// - /// Optional collection of + /// Optional collection of /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// + /// public List Blocks { get; set; } ///