diff --git a/.gitignore b/.gitignore
index 8e281d61..451fd09a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -220,3 +220,14 @@ pip-log.txt
packages
*.private
.vs/config/applicationhost.config
+
+#.Net core
+*.lock.json
+
+# Cake
+tools/*
+!tools/packages.config
+
+
+.vs/
+artifacts/
\ No newline at end of file
diff --git a/Attachment.cs b/Attachment.cs
deleted file mode 100644
index 2274dfd8..00000000
--- a/Attachment.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace SlackAPI
-{
- //See: https://api.slack.com/docs/attachments
- public class Attachment
- {
- public string fallback;
- public string color;
- public string pretext;
- public string author_name;
- public string author_link;
- public string author_icon;
- public string title;
- public string title_link;
- public string text;
- public Field[] fields;
-
- public string image_url;
- public string thumb_url;
- public string[] mrkdwn_in;
- }
-
- public class Field{
- public string title;
- public string value;
- public bool @short;
- }
-}
diff --git a/Bot.cs b/Bot.cs
deleted file mode 100644
index 4c6f9006..00000000
--- a/Bot.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace SlackAPI
-{
- public class Bot
- {
- public string emoji;
- public string image_24;
- public string image_32;
- public string image_48;
- public string image_72;
- public string image_192;
-
- public bool deleted;
- public UserProfile icons;
- public string id;
- public string name;
- public string bot_user_id;
- public string bot_access_token;
- }
-}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..71125298
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,19 @@
+
+
+ A Slack wrapper for direct interaction with their APIs.
+ Inumedia - Copyright © 2018
+ SlackAPI
+ en-US
+ Inumedia
+ Inumedia
+ https://github.com/Inumedia/SlackAPI
+ http://choosealicense.com/licenses/mit/
+ true
+ git
+ https://github.com/Inumedia/SlackAPI
+
+ false
+ false
+ false
+
+
\ No newline at end of file
diff --git a/GlobalAssemblyInfo.cs b/GlobalAssemblyInfo.cs
new file mode 100644
index 00000000..35472f28
--- /dev/null
+++ b/GlobalAssemblyInfo.cs
@@ -0,0 +1,5 @@
+using System.Reflection;
+
+[assembly: AssemblyVersion("1.1.0.0")]
+[assembly: AssemblyFileVersion("1.1.0.0")]
+[assembly: AssemblyInformationalVersion("1.1.0.0")]
diff --git a/IntegrationTest/Configuration/Config.cs b/IntegrationTest/Configuration/Config.cs
deleted file mode 100644
index 1fb3ab0e..00000000
--- a/IntegrationTest/Configuration/Config.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.IO;
-using Newtonsoft.Json;
-
-namespace IntegrationTest.Configuration
-{
- public class Config
- {
- public SlackConfig Slack { get; set; }
-
- public static Config GetConfig()
- {
- string fileName = Path.Combine(Environment.CurrentDirectory, @"configuration\config.json");
- string json = File.ReadAllText(fileName);
- return JsonConvert.DeserializeObject(json);
- }
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/Configuration/config.default.json b/IntegrationTest/Configuration/config.default.json
deleted file mode 100644
index bec134dd..00000000
--- a/IntegrationTest/Configuration/config.default.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "slack": {
- "userAuthToken": "token-tokentoken-tokentoken-tokentoken-token",
- "botAuthToken": "bottoken-bottoken-bottoken-bottoken",
- "testChannel": "SuperSecretChannel",
- "directMessageUser": "someUserId",
- "authCode": "some-super-secret-code-from-Slack-specially-created-for-you-=)",
- "clientId": "your-special-id",
- "clientSecret": "such-special-secret-key"
- }
-}
diff --git a/IntegrationTest/Connect.cs b/IntegrationTest/Connect.cs
deleted file mode 100644
index e4aa7c20..00000000
--- a/IntegrationTest/Connect.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using SlackAPI;
-using System;
-using System.Threading;
-using IntegrationTest.Configuration;
-using IntegrationTest.Helpers;
-using Polly;
-using SlackAPI.WebSocketMessages;
-
-namespace IntegrationTest
-{
- [TestClass]
- public class Connect
- {
- const string TestText = "Test :D";
- private readonly Config _config;
-
- public Connect()
- {
- _config = Config.GetConfig();
- }
-
- [TestMethod]
- public void TestConnectAsUser()
- {
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
- Assert.IsTrue(client.IsConnected, "Invalid, doesn't think it's connected.");
- }
-
- [TestMethod]
- public void TestGetAccessToken()
- {
- // assemble
- var clientId = _config.Slack.ClientId;
- var clientSecret = _config.Slack.ClientSecret;
- var authCode = _config.Slack.AuthCode;
-
- // act
- var accessTokenResponse = GetAccessToken(clientId, clientSecret, "", authCode);
-
- // assert
- Assert.IsNotNull(accessTokenResponse, "accessTokenResponse != null");
- Assert.IsNotNull(accessTokenResponse.bot, "bot != null");
- Assert.IsNotNull(accessTokenResponse.bot.bot_user_id, "bot.user_id != null");
- Assert.IsNotNull(accessTokenResponse.bot.bot_access_token, "bot.bot_access_token != null");
- }
-
- private AccessTokenResponse GetAccessToken(string clientId, string clientSecret, string redirectUri, string authCode)
- {
- var waiter = new EventWaitHandle(false, EventResetMode.ManualReset);
- AccessTokenResponse accessTokenResponse = null;
-
- SlackClient.GetAccessToken(response =>
- {
- accessTokenResponse = response;
- waiter.Set();
-
- }, clientId, clientSecret, redirectUri, authCode);
-
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() => { Assert.IsTrue(waiter.WaitOne(), "Still waiting for things to happen..."); });
-
- return accessTokenResponse;
- }
-
- [TestMethod]
- public void TestConnectAsBot()
- {
- var client = ClientHelper.GetClient(_config.Slack.BotAuthToken);
- Assert.IsTrue(client.IsConnected, "Invalid, doesn't think it's connected.");
- }
-
- [TestMethod]
- public void TestConnectPostAndDelete()
- {
- // given
- SlackSocketClient client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
- string channel = _config.Slack.TestChannel;
-
- // when
- DateTime messageTimestamp = PostMessage(client, channel);
- DeletedResponse deletedResponse = DeleteMessage(client, channel, messageTimestamp);
-
- // then
- Assert.IsNotNull(deletedResponse, "No response was found");
- Assert.IsTrue(deletedResponse.ok, "Message not deleted!");
- Assert.AreEqual(channel, deletedResponse.channel, "Got invalid channel? Something's not right here...");
- Assert.AreEqual(messageTimestamp, deletedResponse.ts, "Got invalid time stamp? Something's not right here...");
- }
-
- private static DateTime PostMessage(SlackSocketClient client, string channel)
- {
- var waiter = new EventWaitHandle(false, EventResetMode.ManualReset);
- MessageReceived sendMessageResponse = null;
-
- client.SendMessage(response =>
- {
- sendMessageResponse = response;
- waiter.Set();
- }, channel, TestText);
-
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(waiter.WaitOne(), "Still waiting for things to happen...");
- });
-
- Assert.IsNotNull(sendMessageResponse, "sendMessageResponse != null");
- Assert.AreEqual(TestText, sendMessageResponse.text, "Got invalid returned text, something's not right here...");
-
- return sendMessageResponse.ts;
- }
-
- private static DeletedResponse DeleteMessage(SlackSocketClient client, string channel, DateTime messageTimestamp)
- {
- DeletedResponse deletedResponse = null;
- var waiter = new EventWaitHandle(false, EventResetMode.ManualReset);
-
- client.DeleteMessage(response =>
- {
- deletedResponse = response;
- waiter.Set();
- }, channel, messageTimestamp);
-
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(waiter.WaitOne(), "Still waiting for things to happen...");
- });
-
- return deletedResponse;
- }
- }
-}
diff --git a/IntegrationTest/Helpers/ClientHelper.cs b/IntegrationTest/Helpers/ClientHelper.cs
deleted file mode 100644
index 662c2702..00000000
--- a/IntegrationTest/Helpers/ClientHelper.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Threading;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Polly;
-using SlackAPI;
-
-namespace IntegrationTest.Helpers
-{
- public static class ClientHelper
- {
- public static SlackSocketClient GetClient(string authToken)
- {
- var wait = new EventWaitHandle(false, EventResetMode.ManualReset);
-
- var client = new SlackSocketClient(authToken);
- client.Connect(x =>
- {
- Console.WriteLine("RTM Start");
- }, () =>
- {
- Console.WriteLine("Connected");
- wait.Set();
- });
-
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(wait.WaitOne(), "Still waiting for things to happen...");
- });
-
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(client.IsConnected, "Doh, still isn't connected");
- });
-
- return client;
- }
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/Helpers/InSync.cs b/IntegrationTest/Helpers/InSync.cs
deleted file mode 100644
index 0d292038..00000000
--- a/IntegrationTest/Helpers/InSync.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace IntegrationTest.Helpers
-{
- using System;
- using System.Threading;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Polly;
-
- public class InSync : IDisposable
- {
- private readonly EventWaitHandle wait;
- private readonly string AttemptingToDo;
-
- public InSync(string attemptingToDo = "something")
- {
- wait = new EventWaitHandle(false, EventResetMode.ManualReset);
- this.AttemptingToDo = attemptingToDo;
- }
-
- public void Proceed()
- {
- wait.Set();
- }
-
- public void Dispose()
- {
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(wait.WaitOne(), $"Took too long to do '{AttemptingToDo}'");
- });
- }
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/Helpers/SlackMother.cs b/IntegrationTest/Helpers/SlackMother.cs
deleted file mode 100644
index bd108708..00000000
--- a/IntegrationTest/Helpers/SlackMother.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace IntegrationTest.Helpers
-{
- using SlackAPI;
-
- public class SlackMother
- {
- public static Attachment[] SomeAttachments => new[]
- {
- new Attachment()
- {
- fallback = "Required plain-text summary of the attachment.",
- color = "#36a64f",
- pretext = "Optional text that appears above the attachment block",
- author_name = "Bobby Tables",
- author_link = "http://flickr.com/bobby/",
- author_icon = "http://flickr.com/icons/bobby.jpg",
- title = "Slack API Documentation",
- title_link = "https://api.slack.com/",
- text = "Optional text that appears within the attachment",
- fields = new[]
- {
- new Field() { title = "Priority", value = "High", @short = false },
- new Field() { title = "Priority", value = "High", @short = true },
- new Field() { title = "Priority", value = "High", @short = true }
- }
- }
- };
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/IntegrationTest.csproj b/IntegrationTest/IntegrationTest.csproj
deleted file mode 100644
index 2af68da5..00000000
--- a/IntegrationTest/IntegrationTest.csproj
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
-
- Debug
- AnyCPU
- {C254F6FF-81D4-46DF-AA21-3D1A6456253B}
- Library
- Properties
- IntegrationTest
- IntegrationTest
- v4.5
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 10.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
- False
- UnitTest
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
- ..\packages\Polly.2.2.3\lib\net45\Polly.dll
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {0c0a58a8-174e-4a4c-907b-c3569144d15d}
- SlackAPI
-
-
-
-
- Always
-
-
-
-
-
-
-
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/IntegrationTest/JoinDirectMessageChannel.cs b/IntegrationTest/JoinDirectMessageChannel.cs
deleted file mode 100644
index 4b14d246..00000000
--- a/IntegrationTest/JoinDirectMessageChannel.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using IntegrationTest.Configuration;
-using IntegrationTest.Helpers;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Polly;
-
-namespace IntegrationTest
-{
- [TestClass]
- public class JoinDirectMessageChannel
- {
- private readonly Config _config;
-
- public JoinDirectMessageChannel()
- {
- _config = Config.GetConfig();
- }
-
- [TestMethod]
- public void ShouldJoinDirectMessageChannel()
- {
- // given
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
-
- string userName = _config.Slack.DirectMessageUser;
- string user = client.Users.First(x => x.name.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).id;
-
- // when
- EventWaitHandle wait = new EventWaitHandle(false, EventResetMode.ManualReset);
- client.JoinDirectMessageChannel(response =>
- {
- Assert.IsTrue(response.ok, "Error while joining user channel");
- Assert.IsTrue(!string.IsNullOrEmpty(response.channel.id), "We expected a channel id to be returned");
- wait.Set();
- }, user);
-
- // then
- Policy
- .Handle()
- .WaitAndRetry(15, x => TimeSpan.FromSeconds(0.2), (exception, span) => Console.WriteLine("Retrying in {0} seconds", span.TotalSeconds))
- .Execute(() =>
- {
- Assert.IsTrue(wait.WaitOne(), "Took too long to do the THING");
- });
- }
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/PostMessage.cs b/IntegrationTest/PostMessage.cs
deleted file mode 100644
index 4d76c63d..00000000
--- a/IntegrationTest/PostMessage.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using IntegrationTest.Configuration;
-using IntegrationTest.Helpers;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Polly;
-
-namespace IntegrationTest
-{
- using SlackAPI;
-
- [TestClass]
- public class PostMessage
- {
- private readonly Config _config;
-
- public PostMessage()
- {
- _config = Config.GetConfig();
- }
-
- [TestMethod]
- public void SimpleMessageDelivery()
- {
- // given
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
- PostMessageResponse actual = null;
-
- // when
- using (var sync = new InSync())
- {
- client.PostMessage(
- response =>
- {
- actual = response;
- sync.Proceed();
- },
- _config.Slack.TestChannel,
- "Hi there!");
- }
-
- // then
- Assert.IsTrue(actual.ok, "Error while posting message to channel. ");
- Assert.AreEqual(actual.message.text, "Hi there!");
- Assert.AreEqual(actual.message.type, "message");
- }
-
- [TestMethod]
- public void Attachments()
- {
- // given
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
- PostMessageResponse actual = null;
-
- // when
- using (var sync = new InSync())
- {
- client.PostMessage(
- response =>
- {
- actual = response;
- sync.Proceed();
- },
- _config.Slack.TestChannel,
- string.Empty,
- attachments: SlackMother.SomeAttachments);
- }
-
- // then
- Assert.IsTrue(actual.ok, "Error while posting message to channel. ");
- }
- }
-}
\ No newline at end of file
diff --git a/IntegrationTest/Properties/AssemblyInfo.cs b/IntegrationTest/Properties/AssemblyInfo.cs
deleted file mode 100644
index a45fc50c..00000000
--- a/IntegrationTest/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +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("IntegrationTest")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("IntegrationTest")]
-[assembly: AssemblyCopyright("Copyright © 2015")]
-[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("c254f6ff-81d4-46df-aa21-3d1a6456253b")]
-
-// 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.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/IntegrationTest/packages.config b/IntegrationTest/packages.config
deleted file mode 100644
index 808121a2..00000000
--- a/IntegrationTest/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/JavascriptBotsToArray.cs b/JavascriptBotsToArray.cs
deleted file mode 100644
index c8d0a38a..00000000
--- a/JavascriptBotsToArray.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SlackAPI
-{
- public class JavascriptBotsToArray : Newtonsoft.Json.JsonConverter
- {
- public override bool CanConvert(Type objectType)
- {
- return true;
- }
-
- public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
- {
- List bots = new List();
- int d = reader.Depth;
-
- while (reader.Read() && reader.Depth > d)
- {
- Bot current = new Bot();
- int depth = reader.Depth;
-
- current.name = reader.Value.ToString();
-
- reader.Read();
- while (reader.Read() && reader.Depth > depth)
- {
- if (reader.Value == null) break;
- switch (reader.Value.ToString())
- {
- case "image_48":
- reader.Read();
- current.image_48 = reader.Value.ToString();
- break;
-
- case "image_64":
- reader.Read();
- current.image_48 = reader.Value.ToString();
- break;
-
- case "emoji":
- reader.Read();
- current.emoji = reader.Value.ToString();
- break;
- }
- }
-
- bots.Add(current);
- }
-
- return bots.ToArray();
- }
-
- public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
- {
- //Not sure if this is correct :D
- throw new NotSupportedException("Too hackish for this shi.");
- }
- }
-}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
deleted file mode 100644
index 8b2c719f..00000000
--- a/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +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("SlackAPI")]
-[assembly: AssemblyDescription("A Slack wrapper for direct interaction with their APIs.")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Inumedia")]
-[assembly: AssemblyProduct("SlackAPI")]
-[assembly: AssemblyCopyright("Copyright © 2014")]
-[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("80fe3ab4-f0d5-4fee-a6ae-524b523cebcc")]
-
-// 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.0.*")]
-[assembly: AssemblyVersion("1.0.4.1")]
-[assembly: AssemblyFileVersion("1.0.4.1")]
diff --git a/README.md b/README.md
index 8b010776..ebae38ab 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
[](https://ci.appveyor.com/project/Inumedia/slackapi/branch/master)
+[](https://www.nuget.org/packages/SlackAPI/)
+[](https://www.myget.org/feed/slackapi/package/nuget/SlackAPI)
# SlackAPI
-This is a third party implementation of Slack's API written in C#. This supports their WebAPI aswell as their Real Time Messaging API.
+This is a third party implementation of Slack's API written in C#. This supports their WebAPI as well as their Real Time Messaging API.
# Examples
@@ -22,8 +24,25 @@ Want committer access? Feel like I'm too lazy to keep up with Slack's ever chang
Create some pull requests, give me a reason to give you access.
-# Creating NuGet package
-
-Example:
-
-```nuget pack SlackAPI.csproj -version 1.2.3```
+# How to build the solution
+###### (aka where is the config.json file?)
+The project **SlackAPI.Tests** requires a valid `config.json` file for tests. You have two options to build the solution:
+- Unload SlackAPI.Tests project and you're able to build SlackAPI solution.
+- Create your own config.json file to be able to run tests and validate your changes.
+ - Copy/paste `config.default.json` to `config.json`
+ - Update `config.json` file with your settings
+ - *userAuthToken* : Visit https://api.slack.com/docs/oauth-test-tokens to generate a token for your user
+ - *botAuthToken* : Visit https://my.slack.com/services/new/bot to create a bot for your Slack team and retrieve associated token
+ - *testChannel* : A channel ID (user associated to *userAuthToken* must be member of the channel)
+ - *directMessageUser* : A Slack member username
+ - *clientId*/*clientSecret*/*authCode* : Not used
+
+# NuGet package
+SlackAPI NuGet package is build with following platforms support:
+- .NET Framework 4.5
+- .NET Standard 1.3 (UWP support).
+ - The version cannot detect SlackSocketRouting attributes in loaded assemblies (used to extend SlackAPI to handle custom messages).
+- .NET Standard 1.6
+- .NET Standard 2.0
+
+[(.NET implementation compatibility table)](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)
diff --git a/RPCMessages/AccessTokenResponse.cs b/RPCMessages/AccessTokenResponse.cs
deleted file mode 100644
index ebf9fb9d..00000000
--- a/RPCMessages/AccessTokenResponse.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SlackAPI
-{
- [RequestPath("oauth.access")]
- public class AccessTokenResponse : Response
- {
- public string access_token;
- public string scope;
- public string team_name;
- public Bot bot;
- }
-}
diff --git a/SlackAPI.Tests/BlockMessage.cs b/SlackAPI.Tests/BlockMessage.cs
new file mode 100644
index 00000000..0f1d451d
--- /dev/null
+++ b/SlackAPI.Tests/BlockMessage.cs
@@ -0,0 +1,98 @@
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class BlockMessage
+ {
+ private readonly IntegrationFixture fixture;
+
+ public BlockMessage(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+
+ [Fact]
+ public void Blocks()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.PostMessage)))
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ string.Empty,
+ blocks: SlackMother.SomeBlocks);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ }
+
+ [Fact]
+ public void BlocksWithActions()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync())
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ string.Empty,
+ blocks: SlackMother.SomeBlocksWithActions);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ }
+
+ [Fact]
+ public void BlocksInAttachment()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.PostMessage)))
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ "These blocks are in an attachment",
+ attachments: new []
+ {
+ new Attachment
+ {
+ blocks = SlackMother.SomeBlocks
+ }
+ });
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/Configuration/IntegrationCollection.cs b/SlackAPI.Tests/Configuration/IntegrationCollection.cs
new file mode 100644
index 00000000..2b8c8e0c
--- /dev/null
+++ b/SlackAPI.Tests/Configuration/IntegrationCollection.cs
@@ -0,0 +1,9 @@
+using Xunit;
+
+namespace SlackAPI.Tests.Configuration
+{
+ [CollectionDefinition("Integration tests")]
+ public class IntegrationCollection : ICollectionFixture
+ {
+ }
+}
diff --git a/SlackAPI.Tests/Configuration/IntegrationFixture.cs b/SlackAPI.Tests/Configuration/IntegrationFixture.cs
new file mode 100755
index 00000000..cc542af7
--- /dev/null
+++ b/SlackAPI.Tests/Configuration/IntegrationFixture.cs
@@ -0,0 +1,148 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using Newtonsoft.Json;
+using Polly;
+using SlackAPI.Tests.Helpers;
+using SlackAPI.WebSocketMessages;
+using Xunit;
+
+namespace SlackAPI.Tests.Configuration
+{
+ public class IntegrationFixture : IDisposable
+ {
+ private const int MaxConnectionAttempts = 5;
+
+ private readonly Lazy userClient;
+ private readonly Lazy botClient;
+ private readonly Lazy userClientAsync;
+ private readonly Lazy botClientAsync;
+
+ private readonly Policy connectRetryPolicy;
+
+ public IntegrationFixture()
+ {
+ this.Config = this.GetConfig();
+
+ this.connectRetryPolicy = Policy
+ .Handle(exception => exception.Message.Contains("ratelimited"))
+ .WaitAndRetry(MaxConnectionAttempts, retryAttempt => TimeSpan.FromSeconds(ComputeExponentialBackoff(retryAttempt)),
+ (exception, timeSpan, retryCount, context) => Console.WriteLine($"Connection failed ({exception.Message}). Retrying after {timeSpan.TotalSeconds}s ({retryCount}/5)"));
+
+ this.userClient = new Lazy(() => connectRetryPolicy.Execute(() => this.CreateClient(this.Config.UserAuthToken)));
+ this.botClient = new Lazy(() => connectRetryPolicy.Execute(() => this.CreateClient(this.Config.BotAuthToken)));
+ this.userClientAsync = new Lazy(() => new SlackTaskClient(this.Config.UserAuthToken));
+ this.botClientAsync = new Lazy(() => new SlackTaskClient(this.Config.BotAuthToken));
+ }
+
+ public SlackConfig Config { get; }
+
+ public TimeSpan ConnectionTimeout => TimeSpan.FromSeconds(Enumerable.Range(1, MaxConnectionAttempts).Sum(ComputeExponentialBackoff)) + TimeSpan.FromSeconds(10); // Maximum exponential backoff + 10 seconds for connections attemps
+
+ public SlackSocketClient UserClient
+ {
+ get
+ {
+ Assert.True(userClient.Value.IsReady);
+ return userClient.Value;
+ }
+ }
+
+ public SlackSocketClient BotClient
+ {
+ get
+ {
+ Assert.True(botClient.Value.IsReady);
+ return botClient.Value;
+ }
+ }
+
+ public SlackSocketClient CreateUserClient(IWebProxy proxySettings = null, bool maintainPresenceChangesStatus = false, Action presenceChanged = null)
+ {
+ return this.connectRetryPolicy.Execute(() => this.CreateClient(this.Config.UserAuthToken, proxySettings, maintainPresenceChangesStatus, presenceChanged));
+ }
+
+ public SlackSocketClient CreateBotClient(IWebProxy proxySettings = null)
+ {
+ return this.connectRetryPolicy.Execute(() => this.CreateClient(this.Config.BotAuthToken, proxySettings));
+ }
+
+ public SlackTaskClient UserClientAsync => userClientAsync.Value;
+
+ public SlackTaskClient BotClientAsync => botClientAsync.Value;
+
+ public void Dispose()
+ {
+ if (this.userClient.IsValueCreated)
+ {
+ this.UserClient.CloseSocket();
+ }
+
+ if (this.botClient.IsValueCreated)
+ {
+ this.BotClient.CloseSocket();
+ }
+ }
+
+ private SlackConfig GetConfig()
+ {
+ var currentAssembly = this.GetType().GetTypeInfo().Assembly.Location;
+ var assemblyDirectory = Path.GetDirectoryName(currentAssembly);
+ string fileName = Path.Combine(assemblyDirectory, @"configuration\config.json");
+ string json = System.IO.File.ReadAllText(fileName);
+
+ var jsonObject = new {slack = (SlackConfig)null };
+ return JsonConvert.DeserializeAnonymousType(json, jsonObject).slack;
+ }
+
+ private SlackSocketClient CreateClient(string authToken, IWebProxy proxySettings = null, bool maintainPresenceChanges = false, Action presenceChanged = null)
+ {
+ SlackSocketClient client;
+
+ LoginResponse loginResponse = null;
+ using (var syncClient = new InSync($"{nameof(SlackClient.Connect)} - Connected callback"))
+ using (var syncClientSocket = new InSync($"{nameof(SlackClient.Connect)} - SocketConnected callback"))
+ using (var syncClientSocketHello = new InSync($"{nameof(SlackClient.Connect)} - SocketConnected hello callback"))
+ {
+ client = new SlackSocketClient(authToken, proxySettings, maintainPresenceChanges);
+
+ void OnPresenceChanged(PresenceChange x)
+ {
+ presenceChanged?.Invoke(client, x);
+ }
+
+ client.OnPresenceChanged += OnPresenceChanged;
+ client.OnHello += () => syncClientSocketHello.Proceed();
+ client.Connect(x =>
+ {
+ loginResponse = x;
+
+ Console.WriteLine($"Connected {x.ok}");
+ syncClient.Proceed();
+ if (!x.ok)
+ {
+ // If connect fails, socket connect callback is not called
+ syncClientSocket.Proceed();
+ syncClientSocketHello.Proceed();
+ }
+ }, () =>
+ {
+ Console.WriteLine("Socket Connected");
+ syncClientSocket.Proceed();
+ });
+ }
+
+ loginResponse.AssertOk();
+
+ return client;
+ }
+
+ private int ComputeExponentialBackoff(int retryAttempt)
+ {
+ // Retries after 4, 8, 16, 32, 64... seconds
+ return 2 * (int)Math.Pow(2, retryAttempt);
+ }
+ }
+}
diff --git a/IntegrationTest/Configuration/SlackConfig.cs b/SlackAPI.Tests/Configuration/SlackConfig.cs
similarity index 58%
rename from IntegrationTest/Configuration/SlackConfig.cs
rename to SlackAPI.Tests/Configuration/SlackConfig.cs
index 15d7e635..6f45de62 100644
--- a/IntegrationTest/Configuration/SlackConfig.cs
+++ b/SlackAPI.Tests/Configuration/SlackConfig.cs
@@ -1,4 +1,4 @@
-namespace IntegrationTest.Configuration
+namespace SlackAPI.Tests.Configuration
{
public class SlackConfig
{
@@ -6,8 +6,12 @@ public class SlackConfig
public string BotAuthToken { get; set; }
public string TestChannel { get; set; }
public string DirectMessageUser { get; set; }
- public string AuthCode { get; set; }
+
public string ClientId { get; set; }
public string ClientSecret { get; set; }
+ public string RedirectUrl { get; set; }
+ public string AuthUsername { get; set; }
+ public string AuthPassword { get; set; }
+ public string AuthWorkspace { get; set; }
}
}
\ No newline at end of file
diff --git a/SlackAPI.Tests/Configuration/config.default.json b/SlackAPI.Tests/Configuration/config.default.json
new file mode 100644
index 00000000..01d4e493
--- /dev/null
+++ b/SlackAPI.Tests/Configuration/config.default.json
@@ -0,0 +1,16 @@
+{
+ "slack": {
+ "userAuthToken": "token-tokentoken-tokentoken-tokentoken-token",
+ "botAuthToken": "bottoken-bottoken-bottoken-bottoken",
+ "testChannel": "SuperSecretChannel",
+ "directMessageUser": "someUserId",
+
+ // Application GetAccessToken
+ "clientId": "your-special-id",
+ "clientSecret": "such-special-secret-key",
+ "redirectUrl": "http://redirecturl",
+ "authUsername": "user@domain.com",
+ "authPassword": "userpassword",
+ "authWorkspace": "workspace"
+ }
+}
diff --git a/SlackAPI.Tests/Connect.cs b/SlackAPI.Tests/Connect.cs
new file mode 100644
index 00000000..3aa193f8
--- /dev/null
+++ b/SlackAPI.Tests/Connect.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using SlackAPI.WebSocketMessages;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class Connect : IDisposable
+ {
+ const string TestText = "Test :D";
+ private readonly IntegrationFixture fixture;
+
+ private SlackSocketClient slackClient;
+
+ public Connect(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+
+ // Extra wait to mitigate Slack throttling
+ Thread.Sleep(2000);
+ }
+
+ public void Dispose()
+ {
+ slackClient?.CloseSocket();
+ }
+
+ [Fact]
+ public void TestConnectAsUser()
+ {
+ slackClient = this.fixture.CreateUserClient();
+ Assert.True(slackClient.IsConnected, "Invalid, doesn't think it's connected.");
+ }
+
+ [Fact]
+ public void TestConnectAsBot()
+ {
+ slackClient = this.fixture.CreateBotClient();
+ Assert.True(slackClient.IsConnected, "Invalid, doesn't think it's connected.");
+ }
+
+ [Fact]
+ public void TestConnectWithWrongProxySettings()
+ {
+ var proxySettings = new WebProxy { Address = new Uri("http://127.0.0.1:8080")};
+ Assert.Throws(() => this.fixture.CreateUserClient(proxySettings));
+ Assert.Throws(() => this.fixture.CreateBotClient(proxySettings));
+ }
+
+ [Fact]
+ public void TestConnectPostAndDelete()
+ {
+ // given
+ slackClient = this.fixture.CreateUserClient();
+ string channel = this.fixture.Config.TestChannel;
+
+ // when
+ DateTime messageTimestamp = PostMessage(slackClient, channel);
+ DeletedResponse deletedResponse = DeleteMessage(slackClient, channel, messageTimestamp);
+
+ // then
+ Assert.NotNull(deletedResponse);
+ Assert.True(deletedResponse.ok);
+ Assert.Equal(channel, deletedResponse.channel);
+ Assert.Equal(messageTimestamp, deletedResponse.ts);
+ }
+
+ [Fact]
+ public void TestConnectGetPresenceChanges()
+ {
+ // Arrange
+ int presenceChangesRaisedCount = 0;
+ using (var sync = new InSync(nameof(TestConnectGetPresenceChanges), this.fixture.ConnectionTimeout))
+ {
+ void OnPresenceChanged(SlackSocketClient sender, PresenceChange e)
+ {
+ if (++presenceChangesRaisedCount == sender.Users.Count)
+ {
+ sync.Proceed();
+ }
+ }
+
+ // Act
+ slackClient = this.fixture.CreateUserClient(maintainPresenceChangesStatus: true, presenceChanged: OnPresenceChanged);
+ }
+
+ // Assert
+ Assert.True(slackClient.Users.All(x => x.presence != null));
+ }
+
+ [Fact(Skip = "Not stable on AppVeyor")]
+ public void TestManualSubscribePresenceChangeAndManualPresenceChange()
+ {
+ // Arrange
+ slackClient = this.fixture.CreateUserClient();
+ using (var sync = new InSync())
+ {
+ slackClient.OnPresenceChanged += x =>
+ {
+ if (x.user == slackClient.MySelf.id)
+ {
+ // Assert
+ sync.Proceed();
+ }
+ };
+
+ slackClient.SubscribePresenceChange(slackClient.MySelf.id);
+ }
+
+ using (var sync = new InSync())
+ {
+ slackClient.OnPresenceChanged += x =>
+ {
+ if (x is ManualPresenceChange && x.user == slackClient.MySelf.id)
+ {
+ // Assert
+ sync.Proceed();
+ }
+ };
+
+ // Act
+ slackClient.EmitPresence(x => x.AssertOk(), Presence.away);
+ slackClient.EmitPresence(x => x.AssertOk(), Presence.auto);
+ }
+ }
+
+ private static DateTime PostMessage(SlackSocketClient client, string channel)
+ {
+ MessageReceived sendMessageResponse = null;
+
+ using (var sync = new InSync(nameof(SlackSocketClient.SendMessage)))
+ {
+ client.SendMessage(response =>
+ {
+ sendMessageResponse = response;
+ sync.Proceed();
+ }, channel, TestText);
+ }
+
+ Assert.NotNull(sendMessageResponse);
+ Assert.Equal(TestText, sendMessageResponse.text);
+
+ return sendMessageResponse.ts;
+ }
+
+ private static DeletedResponse DeleteMessage(SlackSocketClient client, string channel, DateTime messageTimestamp)
+ {
+ DeletedResponse deletedResponse = null;
+
+ using (var sync = new InSync(nameof(SlackClient.DeleteMessage)))
+ {
+ client.DeleteMessage(response =>
+ {
+ deletedResponse = response;
+ sync.Proceed();
+ }, channel, messageTimestamp);
+ }
+
+ return deletedResponse;
+ }
+ }
+}
diff --git a/SlackAPI.Tests/Conversations.cs b/SlackAPI.Tests/Conversations.cs
new file mode 100644
index 00000000..3c8b74dc
--- /dev/null
+++ b/SlackAPI.Tests/Conversations.cs
@@ -0,0 +1,41 @@
+using System.Linq;
+using SlackAPI.RPCMessages;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class Conversations
+ {
+ private readonly IntegrationFixture fixture;
+
+ public Conversations(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+ [Fact]
+ public void ConversationList()
+ {
+ var client = this.fixture.UserClient;
+ ConversationsListResponse actual = null;
+ using (var sync = new InSync(nameof(SlackClient.ChannelLookup)))
+ {
+ client.GetConversationsList(response =>
+ {
+ actual = response;
+ sync.Proceed();
+ });
+ }
+
+ Assert.True(actual.ok, "Error while fetching conversation list.");
+ Assert.True(actual.channels.Any());
+
+ // check to null
+ var someChannel = actual.channels.First();
+ Assert.NotNull(someChannel.id);
+ Assert.NotNull(someChannel.name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/Helpers/InSync.cs b/SlackAPI.Tests/Helpers/InSync.cs
new file mode 100644
index 00000000..83a77ec8
--- /dev/null
+++ b/SlackAPI.Tests/Helpers/InSync.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+namespace SlackAPI.Tests.Helpers
+{
+ public class InSync : IDisposable
+ {
+ private readonly TimeSpan DefaultWaitTimeout = TimeSpan.FromSeconds(15);
+
+ private readonly ManualResetEventSlim waiter;
+ private readonly string message;
+ private readonly TimeSpan waitTimeout;
+
+ public InSync([CallerMemberName] string message = null, TimeSpan? waitTimeout = null)
+ {
+ this.message = message;
+ this.waitTimeout = waitTimeout.GetValueOrDefault(DefaultWaitTimeout);
+ this.waiter = new ManualResetEventSlim();
+ }
+
+ public void Proceed()
+ {
+ this.waiter.Set();
+ }
+
+ public void Dispose()
+ {
+ Assert.True(this.waiter.Wait(Debugger.IsAttached ? Timeout.InfiniteTimeSpan : this.waitTimeout), $"Took too long to do '{this.message}'");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/Helpers/SlackMother.cs b/SlackAPI.Tests/Helpers/SlackMother.cs
new file mode 100644
index 00000000..70f750ef
--- /dev/null
+++ b/SlackAPI.Tests/Helpers/SlackMother.cs
@@ -0,0 +1,253 @@
+namespace SlackAPI.Tests.Helpers
+{
+ public class SlackMother
+ {
+ public static IBlock[] SomeBlocks => new IBlock[]
+ {
+ new ContextBlock
+ {
+ elements = new IElement[]{
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = ""
+
+ }
+ }
+ },
+ new SectionBlock
+ {
+ text = new Text
+ {
+ type = TextTypes.Markdown,
+ text = ""
+ },
+ accessory = new ImageElement()
+ {
+ image_url = "https://imgs.xkcd.com/comics/exploits_of_a_mom.png",
+ alt_text = "Required for image elements"
+ }
+ },
+ new DividerBlock(),
+ new SectionBlock
+ {
+ fields = new []
+ {
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.PlainText,
+ text = "*Priority*\nHigh"
+ }
+ }
+ }
+ };
+
+ public static IBlock[] SomeBlocksWithActions => new IBlock[]
+ {
+ new ContextBlock
+ {
+ elements = new IElement[]{
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = ""
+ }
+ },
+
+ },
+ new SectionBlock
+ {
+ text = new Text
+ {
+ type = TextTypes.Markdown,
+ text = ""
+ },
+ accessory = new OverflowElement
+ {
+ options = new []
+ {
+ new Option
+ {
+ text = new Text
+ {
+ type = TextTypes.PlainText,
+ text = "Option 1 Text"
+ },
+ value = "option 1"
+ },
+ new Option
+ {
+ text = new Text
+ {
+ type = TextTypes.PlainText,
+ text = "Option 2 Text"
+ },
+ value = "option 2"
+ },
+ }
+ }
+ },
+ new DividerBlock(),
+ new SectionBlock
+ {
+ fields = new []
+ {
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.Markdown,
+ text = "*Priority*\nHigh"
+ },
+ new Text
+ {
+ type = TextTypes.PlainText,
+ text = "*Priority*\nHigh"
+ }
+ }
+ },
+ new SectionBlock
+ {
+ text = new Text
+ {
+ text = "Pick a date"
+ },
+ accessory = new Element
+ {
+ type = ElementTypes.DatePicker,
+ initial_date = "1977-05-25",
+ placeholder = new Text
+ {
+ text = "Select a date"
+ }
+ }
+ },
+ new ActionsBlock
+ {
+ block_id = "Optional unique identifier for a block",
+ elements = new IElement[]
+ {
+ new ButtonElement
+ {
+ text = new Text
+ {
+ text = "Button 1 Text"
+ },
+ value = "Button 1",
+ style = ButtonStyles.Danger,
+ confirm = new Confirm
+ {
+ title = new Text
+ {
+ text = "Are you sure?"
+ },
+ text = new Text
+ {
+ text = "Did you press Button 1?"
+ },
+ confirm = new Text
+ {
+ text = "I did"
+ },
+ deny = new Text
+ {
+ text = "I didn't"
+ }
+ }
+ },
+ new ButtonElement
+ {
+ text = new Text
+ {
+ text = "Button 2 Text"
+ },
+ value = "Button 2",
+ },
+ new ButtonElement
+ {
+ text = new Text
+ {
+ text = "Button 3 Text"
+ },
+ value = "Button 3",
+ style = ButtonStyles.Primary,
+ }
+ }
+ }
+ };
+ public static Attachment[] SomeAttachments => new[]
+ {
+ new Attachment()
+ {
+ fallback = "Required plain-text summary of the attachment.",
+ color = "#36a64f",
+ pretext = "Optional text that appears above the attachment block",
+ author_name = "Bobby Tables",
+ author_link = "http://flickr.com/bobby/",
+ author_icon = "http://flickr.com/icons/bobby.jpg",
+ title = "Slack API Documentation",
+ title_link = "https://api.slack.com/",
+ text = "Optional text that appears within the attachment",
+ fields = new[]
+ {
+ new Field() { title = "Priority", value = "High", @short = false },
+ new Field() { title = "Priority", value = "High", @short = true },
+ new Field() { title = "Priority", value = "High", @short = true }
+ }
+ }
+ };
+
+ public static Attachment[] SomeAttachmentsWithActions => new[]
+ {
+ new Attachment()
+ {
+ fallback = "Required plain-text summary of the attachment.",
+ color = "#36a64f",
+ pretext = "Optional text that appears above the attachment block",
+ author_name = "Bobby Tables",
+ author_link = "http://flickr.com/bobby/",
+ author_icon = "http://flickr.com/icons/bobby.jpg",
+ title = "Slack API Documentation",
+ title_link = "https://api.slack.com/",
+ text = "Optional text that appears within the attachment",
+ fields = new[]
+ {
+ new Field() { title = "Priority", value = "High", @short = false },
+ new Field() { title = "Priority", value = "High", @short = true },
+ new Field() { title = "Priority", value = "High", @short = true }
+ },
+ actions = new []
+ {
+ new AttachmentAction("Button 1", "Button 1 Text") ,
+ new AttachmentAction("Button 2", "Button 2 Text") {style = "primary"},
+ new AttachmentAction("Button 3", "Button 3 Text") {style = "danger"},
+ new AttachmentAction("Button 4", "Button 4 Text") {style = "danger", confirm = new ActionConfirm {text = "Are you sure?????"} },
+ new AttachmentAction("Button 5", "Button 5 Text") {style = "danger", confirm = new ActionConfirm {text = "Do you really want to do this", dismiss_text = "No I don't", ok_text = "Sure I do", title = "Just checking"} }
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/JoinDirectMessageChannel.cs b/SlackAPI.Tests/JoinDirectMessageChannel.cs
new file mode 100644
index 00000000..237ede30
--- /dev/null
+++ b/SlackAPI.Tests/JoinDirectMessageChannel.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Linq;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class JoinDirectMessageChannel
+ {
+ private readonly IntegrationFixture fixture;
+
+ public JoinDirectMessageChannel(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+
+ [Fact]
+ public void ShouldJoinDirectMessageChannel()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ JoinDirectMessageChannelResponse actual = null;
+
+ string userName = this.fixture.Config.DirectMessageUser;
+ string user = client.Users.First(x => x.name.Equals(userName, StringComparison.OrdinalIgnoreCase)).id;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.JoinDirectMessageChannel)))
+ {
+ client.JoinDirectMessageChannel(response =>
+ {
+ actual = response;
+ sync.Proceed();;
+ }, user);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while joining user channel");
+ Assert.NotEmpty(actual.channel.id);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/PostMessage.cs b/SlackAPI.Tests/PostMessage.cs
new file mode 100644
index 00000000..9766660a
--- /dev/null
+++ b/SlackAPI.Tests/PostMessage.cs
@@ -0,0 +1,125 @@
+using System;
+using SlackAPI.RPCMessages;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using System.Linq;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class PostMessage
+ {
+ private readonly IntegrationFixture fixture;
+
+ public PostMessage(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+
+ [Fact]
+ public void SimpleMessageDelivery()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.PostMessage)))
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ "Hi there!");
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ Assert.Equal("Hi there!", actual.message.text);
+ Assert.Equal("message", actual.message.type);
+ }
+
+ [Fact]
+ public void Attachments()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.PostMessage)))
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ string.Empty,
+ attachments: SlackMother.SomeAttachments);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ }
+
+ [Fact]
+ public void AttachmentsWithActions()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostMessageResponse actual = null;
+
+ // when
+ using (var sync = new InSync())
+ {
+ client.PostMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ string.Empty,
+ attachments: SlackMother.SomeAttachmentsWithActions);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ }
+
+ [Fact]
+ public void PostEphemeralMessage()
+ {
+ // given
+ var client = this.fixture.UserClient;
+ PostEphemeralResponse actual = null;
+
+ string userName = this.fixture.Config.DirectMessageUser;
+ string userId = client.Users.First(x => x.name.Equals(userName, StringComparison.OrdinalIgnoreCase)).id;
+
+ // when
+ using (var sync = new InSync(nameof(SlackClient.PostEphemeralMessage)))
+ {
+ client.PostEphemeralMessage(
+ response =>
+ {
+ actual = response;
+ sync.Proceed();
+ },
+ this.fixture.Config.TestChannel,
+ "Hi there!",
+ userId);
+ }
+
+ // then
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ Assert.Null(actual.error);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/SlackAPI.Tests.csproj b/SlackAPI.Tests/SlackAPI.Tests.csproj
new file mode 100644
index 00000000..bc4dc3b4
--- /dev/null
+++ b/SlackAPI.Tests/SlackAPI.Tests.csproj
@@ -0,0 +1,43 @@
+
+
+
+ net452;netcoreapp2.1
+ Library
+ Full
+ SlackAPI.Tests
+ SlackAPI.Tests
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ Properties\GlobalAssemblyInfo.cs
+
+
+
+
+ $(DefineConstants);RELEASE
+
+
+
diff --git a/SlackAPI.Tests/Timestamp.cs b/SlackAPI.Tests/Timestamp.cs
new file mode 100644
index 00000000..8f9e2ef0
--- /dev/null
+++ b/SlackAPI.Tests/Timestamp.cs
@@ -0,0 +1,34 @@
+using Newtonsoft.Json;
+using System;
+using System.IO;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Unit tests")]
+ public class Timestamp
+ {
+ [Theory]
+ [InlineData("12345.000000")]
+ [InlineData("12345.900000")]
+ [InlineData("12345.123456")]
+ [InlineData("12345.123450")]
+ [InlineData("12345.12345")]
+ [InlineData("12345")]
+ public void TestTimestampConversion(string originalTimestamp)
+ {
+ // Arrange
+ JavascriptDateTimeConverter converter = new JavascriptDateTimeConverter();
+ var jsonReader = new JsonTextReader(new StringReader($"\"{originalTimestamp}\""));
+ jsonReader.Read();
+
+ // Act
+ DateTime timestampDateTime = (DateTime)converter.ReadJson(jsonReader, null, null, null);
+ var newTimestamp = timestampDateTime.ToProperTimeStamp();
+
+ // Assert
+ Assert.Equal(double.Parse(originalTimestamp), double.Parse(newTimestamp));
+ Assert.Equal(6, newTimestamp.Substring(newTimestamp.IndexOf(".") + 1).Length);
+ }
+ }
+}
diff --git a/IntegrationTest/Update.cs b/SlackAPI.Tests/Update.cs
similarity index 56%
rename from IntegrationTest/Update.cs
rename to SlackAPI.Tests/Update.cs
index 742ad928..a5ed7151 100644
--- a/IntegrationTest/Update.cs
+++ b/SlackAPI.Tests/Update.cs
@@ -1,32 +1,29 @@
-using System.Linq;
-using IntegrationTest.Configuration;
-using IntegrationTest.Helpers;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
-namespace IntegrationTest
+namespace SlackAPI.Tests
{
- using SlackAPI;
-
- [TestClass]
+ [Collection("Integration tests")]
public class Update
{
- private readonly Config _config;
+ private readonly IntegrationFixture fixture;
- public Update()
+ public Update(IntegrationFixture fixture)
{
- _config = Config.GetConfig();
+ this.fixture = fixture;
}
- [TestMethod]
+ [Fact]
public void SimpleUpdate()
{
// given
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
+ var client = this.fixture.UserClient;
var messageId = PostedMessage(client);
UpdateResponse actual = null;
// when
- using (var sync = new InSync())
+ using (var sync = new InSync(nameof(SlackClient.Update)))
{
client.Update(
response =>
@@ -35,42 +32,42 @@ public void SimpleUpdate()
sync.Proceed();
},
messageId,
- _config.Slack.TestChannel,
+ this.fixture.Config.TestChannel,
"[changed]",
attachments: SlackMother.SomeAttachments,
as_user: true);
}
// then
- Assert.IsTrue(actual.ok, "Error while posting message to channel. ");
- Assert.AreEqual(actual.message.text, "[changed]");
- Assert.AreEqual(actual.message.type, "message");
+ Assert.True(actual.ok, "Error while posting message to channel. ");
+ Assert.Equal("[changed]", actual.message.text);
+ Assert.Equal("message", actual.message.type);
}
private string PostedMessage(SlackSocketClient client)
{
string messageId = null;
- using (var sync = new InSync())
+ using (var sync = new InSync(nameof(SlackClient.PostMessage)))
{
client.PostMessage(
response =>
{
messageId = response.ts;
- Assert.IsTrue(response.ok, "Error while posting message to channel. ");
+ Assert.True(response.ok, "Error while posting message to channel. ");
sync.Proceed();
},
- _config.Slack.TestChannel,
+ this.fixture.Config.TestChannel,
"Hi there!",
as_user: true);
}
return messageId;
}
- [TestMethod()]
+ [Fact]
public void UpdatePresence()
{
- var client = ClientHelper.GetClient(_config.Slack.UserAuthToken);
- using (var sync = new InSync())
+ var client = this.fixture.UserClient;
+ using (var sync = new InSync(nameof(SlackClient.EmitPresence)))
{
client.EmitPresence((presence) =>
{
diff --git a/SlackAPI.Tests/UploadFile.cs b/SlackAPI.Tests/UploadFile.cs
new file mode 100644
index 00000000..28a5b943
--- /dev/null
+++ b/SlackAPI.Tests/UploadFile.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class UploadFile
+ {
+ private readonly IntegrationFixture fixture;
+
+ public UploadFile(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+
+ [Fact]
+ public void UploadFile_Succeeds()
+ {
+ // Arrange
+ const int FileSize = 500;
+ const string FileName = "MyFile.bin";
+ byte[] data = new byte[FileSize];
+
+ // Act
+ FileUploadResponse fileUploadResponse = null;
+ using (var sync = new InSync(nameof(SlackClient.UploadFile)))
+ {
+ this.fixture.UserClient.UploadFile(c =>
+ {
+ fileUploadResponse = c;
+ Assert.True(c.ok);
+ sync.Proceed();
+ },
+ data, FileName, new[] {this.fixture.Config.TestChannel});
+ }
+
+ // Assert
+ using (var sync = new InSync(nameof(SlackClient.UploadFile)))
+ {
+ this.fixture.UserClient.GetFileInfo(c =>
+ {
+ Assert.True(c.ok);
+ Assert.Equal(FileSize, c.file.size);
+ Assert.Equal(FileName, c.file.name);
+ Assert.Equal(new[] { this.fixture.Config.TestChannel}, c.file.channels);
+ sync.Proceed();
+ }, fileUploadResponse.file.id);
+ }
+ }
+
+ [Fact]
+ public async Task UploadFileAsync_Succeeds()
+ {
+ // Arrange
+ const int FileSize = 500;
+ const string FileName = "MyFile.bin";
+ byte[] data = new byte[FileSize];
+
+ // Act
+ var fileUploadResponse = await this.fixture.UserClientAsync.UploadFileAsync(data, FileName, new[] { this.fixture.Config.TestChannel });
+
+ // Assert
+ Assert.True(fileUploadResponse.ok);
+ var fileInfoResponse = await this.fixture.UserClientAsync.GetFileInfoAsync(fileUploadResponse.file.id);
+ Assert.True(fileInfoResponse.ok);
+ Assert.Equal(FileSize, fileInfoResponse.file.size);
+ Assert.Equal(FileName, fileInfoResponse.file.name);
+ Assert.Equal(new[] { this.fixture.Config.TestChannel}, fileInfoResponse.file.channels);
+ }
+ }
+}
diff --git a/SlackAPI.Tests/UserUIInteraction.cs b/SlackAPI.Tests/UserUIInteraction.cs
new file mode 100644
index 00000000..0ece63cf
--- /dev/null
+++ b/SlackAPI.Tests/UserUIInteraction.cs
@@ -0,0 +1,77 @@
+using System.IO;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Chrome;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+// Run UI tests on a single plateform to avoid Slack Captcha
+// (captcha is displayed when trying to login too often)
+#if NETFRAMEWORK
+ [Collection("Integration tests")]
+ public class UserUIInteraction
+ {
+ private readonly IntegrationFixture fixture;
+
+ public UserUIInteraction(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+
+ [Fact]
+ public void TestGetAccessToken()
+ {
+ var clientId = this.fixture.Config.ClientId;
+ var clientSecret = this.fixture.Config.ClientSecret;
+ var redirectUrl = this.fixture.Config.RedirectUrl;
+ var authUsername = this.fixture.Config.AuthUsername;
+ var authPassword = this.fixture.Config.AuthPassword;
+ var authWorkspace = this.fixture.Config.AuthWorkspace;
+
+ using (var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))
+ {
+ driver.Navigate().GoToUrl($"https://{authWorkspace}.slack.com");
+
+ // Wait a bit to ensure we can properly fill username and password fields
+ Thread.Sleep(1000);
+ driver.FindElement(By.Id("email")).SendKeys(authUsername);
+ driver.FindElement(By.Id("password")).SendKeys(authPassword);
+ driver.FindElement(By.Id("signin_btn")).Click();
+
+ var slackClientHelpers = new SlackClientHelpers();
+ var uri = slackClientHelpers.GetAuthorizeUri(clientId, SlackScope.Identify);
+ driver.Navigate().GoToUrl(uri);
+ driver.FindElement(By.CssSelector("button[type='submit']")).Click();
+
+ var code = Regex.Match(driver.Url, "code=(?[^&]+)&state").Groups["code"].Value;
+
+ var accessTokenResponse = GetAccessToken(slackClientHelpers, clientId, clientSecret, redirectUrl, code);
+ Assert.True(accessTokenResponse.ok);
+ Assert.Contains("identify", accessTokenResponse.scope);
+ }
+ }
+
+ private AccessTokenResponse GetAccessToken(SlackClientHelpers slackClientHelpers, string clientId, string clientSecret, string redirectUri, string authCode)
+ {
+ AccessTokenResponse accessTokenResponse = null;
+
+ using (var sync = new InSync(nameof(slackClientHelpers.GetAccessToken)))
+ {
+ slackClientHelpers.GetAccessToken(response =>
+ {
+ accessTokenResponse = response;
+ sync.Proceed();
+
+ }, clientId, clientSecret, redirectUri, authCode);
+ }
+
+ return accessTokenResponse;
+ }
+ }
+#endif
+}
diff --git a/SlackAPI.Tests/Users.cs b/SlackAPI.Tests/Users.cs
new file mode 100644
index 00000000..3970f6aa
--- /dev/null
+++ b/SlackAPI.Tests/Users.cs
@@ -0,0 +1,45 @@
+using System.Linq;
+using SlackAPI.Tests.Configuration;
+using SlackAPI.Tests.Helpers;
+using Xunit;
+
+namespace SlackAPI.Tests
+{
+ [Collection("Integration tests")]
+ public class Users
+ {
+ private readonly IntegrationFixture fixture;
+
+ public Users(IntegrationFixture fixture)
+ {
+ this.fixture = fixture;
+ }
+ [Fact]
+ public void UserList()
+ {
+ var client = this.fixture.UserClient;
+ UserListResponse actual = null;
+ using (var sync = new InSync(nameof(SlackClient.UserLookup)))
+ {
+ client.GetUserList(response =>
+ {
+ actual = response;
+ sync.Proceed();
+ });
+ }
+
+ Assert.True(actual.ok, "Error while fetching user list.");
+ Assert.True(actual.members.Any());
+
+ // apparently deleted users do indeed have null values, so if the first user returned is deleted then there are failures.
+ var someMember = actual.members.Where(x => !x.deleted).First();
+ Assert.NotNull(someMember.id);
+ Assert.NotNull(someMember.color);
+ Assert.NotNull(someMember.real_name);
+ Assert.NotNull(someMember.name);
+ Assert.NotNull(someMember.team_id);
+ Assert.NotNull(someMember.tz);
+ Assert.NotNull(someMember.tz_label);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI.Tests/app.config b/SlackAPI.Tests/app.config
new file mode 100755
index 00000000..21f024a7
--- /dev/null
+++ b/SlackAPI.Tests/app.config
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SlackAPI.csproj b/SlackAPI.csproj
deleted file mode 100644
index aa3aaac4..00000000
--- a/SlackAPI.csproj
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {0C0A58A8-174E-4A4C-907B-C3569144D15D}
- Library
- Properties
- SlackAPI
- SlackAPI
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SlackAPI.nuspec b/SlackAPI.nuspec
deleted file mode 100644
index 43fde8c9..00000000
--- a/SlackAPI.nuspec
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
- SlackAPI
- 1.0.4.1
- SlackAPI
- Inumedia
- Inumedia
- http://choosealicense.com/licenses/mit/
- true
- C# implementation of the Slack team communication platform API.
- The MIT License (MIT)
-
- en-US
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SlackAPI.sln b/SlackAPI.sln
index 762a9965..f3f74ac0 100644
--- a/SlackAPI.sln
+++ b/SlackAPI.sln
@@ -1,17 +1,18 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.23107.0
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30320.27
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlackAPI", "SlackAPI.csproj", "{0C0A58A8-174E-4A4C-907B-C3569144D15D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlackAPI", "SlackAPI\SlackAPI.csproj", "{7EED3D9B-9B7A-49A4-AFBF-599153A47DDA}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTest", "IntegrationTest\IntegrationTest.csproj", "{C254F6FF-81D4-46DF-AA21-3D1A6456253B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlackAPI.Tests", "SlackAPI.Tests\SlackAPI.Tests.csproj", "{DEFA9559-0F8F-4C38-9644-67A080EDC46D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlackApi.Console", "SlackApi.Console\SlackApi.Console.csproj", "{19140E48-E1A9-421D-86DB-5AF18EECFEF2}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{9FC74E78-2D91-407E-B4C8-7C89D6CECB5B}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{532C9828-A2CC-4281-950A-248B06D42E9C}"
ProjectSection(SolutionItems) = preProject
- LICENSE = LICENSE
+ appveyor.yml = appveyor.yml
+ build.cake = build.cake
+ Directory.Build.props = Directory.Build.props
+ GlobalAssemblyInfo.cs = GlobalAssemblyInfo.cs
README.md = README.md
EndProjectSection
EndProject
@@ -21,20 +22,19 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {0C0A58A8-174E-4A4C-907B-C3569144D15D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0C0A58A8-174E-4A4C-907B-C3569144D15D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0C0A58A8-174E-4A4C-907B-C3569144D15D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0C0A58A8-174E-4A4C-907B-C3569144D15D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C254F6FF-81D4-46DF-AA21-3D1A6456253B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C254F6FF-81D4-46DF-AA21-3D1A6456253B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C254F6FF-81D4-46DF-AA21-3D1A6456253B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C254F6FF-81D4-46DF-AA21-3D1A6456253B}.Release|Any CPU.Build.0 = Release|Any CPU
- {19140E48-E1A9-421D-86DB-5AF18EECFEF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19140E48-E1A9-421D-86DB-5AF18EECFEF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19140E48-E1A9-421D-86DB-5AF18EECFEF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19140E48-E1A9-421D-86DB-5AF18EECFEF2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7EED3D9B-9B7A-49A4-AFBF-599153A47DDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7EED3D9B-9B7A-49A4-AFBF-599153A47DDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7EED3D9B-9B7A-49A4-AFBF-599153A47DDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7EED3D9B-9B7A-49A4-AFBF-599153A47DDA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DEFA9559-0F8F-4C38-9644-67A080EDC46D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DEFA9559-0F8F-4C38-9644-67A080EDC46D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DEFA9559-0F8F-4C38-9644-67A080EDC46D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DEFA9559-0F8F-4C38-9644-67A080EDC46D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {367D3666-0DC5-4B8A-832F-57279BE4DCB0}
+ EndGlobalSection
EndGlobal
diff --git a/SlackAPI/Attachment.cs b/SlackAPI/Attachment.cs
new file mode 100644
index 00000000..64b28e1d
--- /dev/null
+++ b/SlackAPI/Attachment.cs
@@ -0,0 +1,59 @@
+namespace SlackAPI
+{
+ //See: https://api.slack.com/docs/attachments
+ public class Attachment
+ {
+ public string callback_id;
+ public string fallback;
+ public string color;
+ public string pretext;
+ public string author_name;
+ public string author_link;
+ public string author_icon;
+ public string title;
+ public string title_link;
+ public string text;
+ public Field[] fields;
+ public IBlock[] blocks;
+
+ public string image_url;
+ public string thumb_url;
+ public string[] mrkdwn_in;
+ public AttachmentAction[] actions;
+
+ public string footer;
+ public string footer_icon;
+ }
+
+ public class Field{
+ public string title;
+ public string value;
+ public bool @short;
+ }
+
+ //See: https://api.slack.com/docs/message-buttons#action_fields
+ public class AttachmentAction
+ {
+ public AttachmentAction(string name, string text)
+ {
+ this.name = name;
+ this.text = text;
+ }
+ public string name { get; }
+ public string text { get; }
+ public string style;
+ public string type = "button";
+ public string value;
+ public ActionConfirm confirm;
+ public string url;
+ }
+
+ //see: https://api.slack.com/docs/message-buttons#confirmation_fields
+ public class ActionConfirm
+ {
+ public string title;
+ public string text;
+ public string ok_text;
+ public string dismiss_text;
+ }
+}
diff --git a/SlackAPI/Block.cs b/SlackAPI/Block.cs
new file mode 100644
index 00000000..5b05099f
--- /dev/null
+++ b/SlackAPI/Block.cs
@@ -0,0 +1,236 @@
+namespace SlackAPI
+{
+ //see https://api.slack.com/reference/messaging/blocks
+ public class Block : IBlock
+ {
+ public string type { get; set; }
+ public string block_id { get; set; }
+ public Text text { get; set; }
+ public Element accessory { get; set; }
+ public Element[] elements { get; set; }
+ public Text title { get; set; }
+ public string image_url { get; set; }
+ public string alt_text { get; set; }
+ public Text[] fields { get; set; }
+ }
+ public class SectionBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Section;
+ public string block_id { get; set; }
+ public Text text { get; set; }
+ public IElement accessory { get; set; }
+ public Text[] fields { get; set; }
+ }
+ public class DividerBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Divider;
+ public string block_id { get; set; }
+ }
+ public class ImageBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Image;
+ public string block_id { get; set; }
+ public Text title { get; set; }
+ public string image_url { get; set; }
+ public string alt_text { get; set; }
+ }
+ public class ActionsBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Actions;
+ public string block_id { get; set; }
+ public IElement[] elements { get; set; }
+ }
+ public class ContextBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Context;
+ public string block_id { get; set; }
+ public IElement[] elements { get; set; }
+ }
+ public class HeaderBlock : IBlock
+ {
+ public string type { get; } = BlockTypes.Header;
+ public Text text { get; set; }
+ public string block_id { get; set; }
+ }
+ public class Text : IElement
+ {
+ public string type { get; set; } = TextTypes.PlainText;
+ public string text { get; set; }
+ public bool? emoji { get; set; }
+ public bool? verbatim { get; set; }
+ }
+
+ public class Option
+ {
+ public Text text { get; set; }
+ public string value { get; set; }
+ }
+
+ public class OptionGroups
+ {
+ public Text label { get; set; }
+ public Option[] options { get; set; }
+ }
+
+ public class Confirm
+ {
+ public Text title { get; set; }
+ public Text text { get; set; }
+ public Text confirm { get; set; }
+ public Text deny { get; set; }
+ }
+
+ public class Element : IElement
+ {
+ public string type { get; set; }
+ public string action_id { get; set; }
+ public Text text { get; set; }
+ public string value { get; set; }
+ public Text placeholder { get; set; }
+ public Option[] options { get; set; }
+ public OptionGroups[] option_groups { get; set; }
+ public string image_url { get; set; }
+ public string alt_text { get; set; }
+ public string url { get; set; }
+ public string initial_date { get; set; }
+ public string initial_user { get; set; }
+ public string initial_channel { get; set; }
+ public string initial_conversation { get; set; }
+ public string initial_option { get; set; }
+ public int? min_query_length { get; set; }
+ public Confirm confirm { get; set; }
+ public string style { get; set; }
+ }
+ public class ImageElement : IElement
+ {
+ public string type { get; } = ElementTypes.Image;
+ public string image_url { get; set; }
+ public string alt_text { get; set; }
+ }
+ public class ButtonElement : IElement
+ {
+ public string type { get; } = ElementTypes.Button;
+ public string action_id { get; set; }
+ public Text text { get; set; }
+ public string value { get; set; }
+ public Text placeholder { get; set; }
+ public Option[] options { get; set; }
+ public OptionGroups[] option_groups { get; set; }
+ public string url { get; set; }
+ public Confirm confirm { get; set; }
+ public string style { get; set; }
+ }
+ public class StaticSelectElement : IElement
+ {
+ public string type { get; } = ElementTypes.StaticSelect;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public Option[] options { get; set; }
+ public OptionGroups[] option_groups { get; set; }
+ public string initial_option { get; set; }
+ public Confirm confirm { get; set; }
+ }
+ public class ExternalSelectElement : IElement
+ {
+ public string type { get; } = ElementTypes.ExternalSelect;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public string initial_option { get; set; }
+ public int min_query_length { get; set; }
+ public Confirm confirm { get; set; }
+ }
+
+
+ public class UserSelectElement : IElement
+ {
+ public string type { get; } = ElementTypes.UserSelect;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public string initial_user { get; set; }
+ public Confirm confirm { get; set; }
+ }
+ public class ConversationSelectElement : IElement
+ {
+ public string type { get; } = ElementTypes.ChannelSelect;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public string initial_conversation { get; set; }
+ public Confirm confirm { get; set; }
+ }
+ public class ChannelSelectElement : IElement
+ {
+ public string type { get; } = ElementTypes.ChannelSelect;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public string initial_channel { get; set; }
+ public Confirm confirm { get; set; }
+ }
+ public class OverflowElement : IElement
+ {
+ public string type { get; } = ElementTypes.Overflow;
+ public string action_id { get; set; }
+ public Option[] options { get; set; }
+ public Confirm confirm { get; set; }
+ }
+
+ public class DatePickerElement : IElement
+ {
+ public string type { get; } = ElementTypes.DatePicker;
+ public string action_id { get; set; }
+ public Text placeholder { get; set; }
+ public string initial_date { get; set; }
+ public Confirm confirm { get; set; }
+ }
+
+ public class View
+ {
+ public string type { get; set; }
+ public IBlock[] blocks { get; set; }
+ }
+
+ public static class ButtonStyles
+ {
+ public const string Primary = "primary";
+ public const string Danger = "danger";
+ }
+
+ public static class BlockTypes
+ {
+ public const string Section = "section";
+ public const string Divider = "divider";
+ public const string Actions = "actions";
+ public const string Context = "context";
+ public const string Image = "image";
+ public const string Header = "header";
+ }
+
+ public static class ViewTypes
+ {
+ public const string Home = "home";
+ public const string Modal = "modal";
+ }
+
+ public static class TextTypes
+ {
+ public const string Markdown = "mrkdwn";
+ public const string PlainText = "plain_text";
+ }
+
+ public static class ElementTypes
+ {
+ public const string Image = "image";
+ public const string Button = "button";
+ public const string StaticSelect = "static_select";
+ public const string ExternalSelect = "external_select";
+ public const string UserSelect = "users_select";
+ public const string ChannelSelect = "channel_select";
+ public const string ConversationSelect = "conversation_select";
+ public const string Overflow = "overflow";
+ public const string DatePicker = "datepicker";
+ }
+
+ public interface IElement { }
+
+ public interface IBlock { }
+
+}
diff --git a/SlackAPI/Bot.cs b/SlackAPI/Bot.cs
new file mode 100644
index 00000000..c11ae22d
--- /dev/null
+++ b/SlackAPI/Bot.cs
@@ -0,0 +1,12 @@
+namespace SlackAPI
+{
+ public class Bot
+ {
+ public string id;
+ public bool deleted;
+ public string name;
+ public string updated;
+ public string app_id;
+ public ProfileIcons icons;
+ }
+}
diff --git a/Channel.cs b/SlackAPI/Channel.cs
similarity index 92%
rename from Channel.cs
rename to SlackAPI/Channel.cs
index 7335e8bd..c45d1c23 100644
--- a/Channel.cs
+++ b/SlackAPI/Channel.cs
@@ -10,12 +10,14 @@ public class Channel : Conversation
{
public string name;
public string creator;
+ public string user;
public bool is_archived;
public bool is_member;
public bool is_general;
public bool is_channel;
public bool is_group;
+ public bool is_im;
//Is this deprecated by is_open?
public bool IsPrivateGroup { get { return id != null && id[0] == 'G'; } }
diff --git a/ContextMessage.cs b/SlackAPI/ContextMessage.cs
similarity index 100%
rename from ContextMessage.cs
rename to SlackAPI/ContextMessage.cs
diff --git a/Conversation.cs b/SlackAPI/Conversation.cs
similarity index 100%
rename from Conversation.cs
rename to SlackAPI/Conversation.cs
diff --git a/SlackAPI/Dialog.cs b/SlackAPI/Dialog.cs
new file mode 100644
index 00000000..020f9dcf
--- /dev/null
+++ b/SlackAPI/Dialog.cs
@@ -0,0 +1,80 @@
+namespace SlackAPI
+{
+ //see https://api.slack.com/dialogs
+
+ public class Dialog
+ {
+ public string callback_id { get; set; }
+ public string title { get; set; }
+ public string submit_label { get; set; }
+ public bool? notify_on_cancel { get; set; }
+ public string state { get; set; }
+ public Element[] elements { get; set; }
+
+ public abstract class Element
+ {
+ public string label { get; set; }
+ public string name { get; set; }
+ public string placeholder { get; set; }
+ public bool? optional { get; set; }
+ public string value { get; set; }
+ }
+
+ public class TextElement : Element
+ {
+ public string type { get; } = "text";
+ public string subtype { get; set; }
+ public int? max_length { get; set; }
+ public int? min_length { get; set; }
+ public string hint { get; set; }
+
+ }
+
+ public class TextAreaElement : Element
+ {
+ public string type { get; } = "textarea";
+ public int? max_length { get; set; }
+ public int? min_length { get; set; }
+ public string hint { get; set; }
+
+ }
+
+ public class SelectElement : Element
+ {
+ public string type { get; } = "select";
+ public string data_source { get; set; }
+ public Option[] options { get; set; }
+ public OptionGroup[] option_groups { get; set; }
+ public Option[] selected_options { get; set; }
+ public int? min_query_length { get; set; }
+ }
+
+ public class Option
+ {
+ public string label { get; set; }
+ public string value { get; set; }
+ }
+
+ public class OptionGroup
+ {
+ public string label { get; set; }
+ public Option[] options { get; set; }
+ }
+
+ public static class DataSourceTypes
+ {
+ public static string Users = "users";
+ public static string Channels = "channels";
+ public static string Conversations = "conversations";
+ public static string External = "external";
+ }
+
+ public static class TextElementSubTypes
+ {
+ public static string Email = "email";
+ public static string Number = "number";
+ public static string Telephone = "telephone";
+ public static string Url = "url";
+ }
+ }
+}
\ No newline at end of file
diff --git a/DirectMessageConversation.cs b/SlackAPI/DirectMessageConversation.cs
similarity index 100%
rename from DirectMessageConversation.cs
rename to SlackAPI/DirectMessageConversation.cs
diff --git a/Extensions.cs b/SlackAPI/Extensions.cs
similarity index 70%
rename from Extensions.cs
rename to SlackAPI/Extensions.cs
index 1e67071f..02d8d6e4 100644
--- a/Extensions.cs
+++ b/SlackAPI/Extensions.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using System.Globalization;
using Newtonsoft.Json;
namespace SlackAPI
@@ -18,30 +18,26 @@ public static string ToProperTimeStamp(this DateTime that, bool toUTC = true)
{
if (toUTC)
{
- string result = ((that.ToUniversalTime().Ticks - 621355968000000000m) / 10000000m).ToString("G17");
- if (result.Contains("."))
- result = result.TrimEnd('0');
- return result;
+ return ((that.ToUniversalTime().Ticks - 621355968000000000m) / 10000000m).ToString("F6", CultureInfo.InvariantCulture);
}
else
- return that.Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString();
+ return that.Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString(CultureInfo.InvariantCulture);
}
public static K Deserialize(this string data)
where K : class
{
- return JsonConvert.DeserializeObject(data, CreateSettings(data));
+ return JsonConvert.DeserializeObject(data, CreateSettings());
}
public static object Deserialize(this string data, Type type)
{
- return JsonConvert.DeserializeObject(data, type, CreateSettings(data));
+ return JsonConvert.DeserializeObject(data, type, CreateSettings());
}
- private static JsonSerializerSettings CreateSettings(object contextData)
+ private static JsonSerializerSettings CreateSettings()
{
JsonSerializerSettings settings = new JsonSerializerSettings();
- settings.Context = new StreamingContext(StreamingContextStates.Other, contextData);
settings.Converters = Converters;
return settings;
diff --git a/File.cs b/SlackAPI/File.cs
similarity index 100%
rename from File.cs
rename to SlackAPI/File.cs
diff --git a/JavascriptDateTimeConverter.cs b/SlackAPI/JavascriptDateTimeConverter.cs
similarity index 81%
rename from JavascriptDateTimeConverter.cs
rename to SlackAPI/JavascriptDateTimeConverter.cs
index babfa5d6..438df1a8 100644
--- a/JavascriptDateTimeConverter.cs
+++ b/SlackAPI/JavascriptDateTimeConverter.cs
@@ -7,11 +7,11 @@
namespace SlackAPI
{
- class JavascriptDateTimeConverter : Newtonsoft.Json.JsonConverter
+ internal class JavascriptDateTimeConverter : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
- return objectType == typeof(DateTime);
+ return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
@@ -20,7 +20,7 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectTy
DateTime res = new DateTime(621355968000000000 + (long)(value * 10000000m)).ToLocalTime();
System.Diagnostics.Debug.Assert(
Decimal.Equals(
- Decimal.Parse(res.ToProperTimeStamp()),
+ Decimal.Parse(res.ToProperTimeStamp(), CultureInfo.InvariantCulture),
Decimal.Parse(reader.Value.ToString(), CultureInfo.InvariantCulture)),
"Precision loss :(");
return res;
diff --git a/Message.cs b/SlackAPI/Message.cs
similarity index 88%
rename from Message.cs
rename to SlackAPI/Message.cs
index d829245a..6077ec6c 100644
--- a/Message.cs
+++ b/SlackAPI/Message.cs
@@ -16,9 +16,12 @@ public class Message : SlackSocketMessage
///
public string username;
public string text;
+ public Attachment[] attachments;
public bool is_starred;
public string permalink;
public Reaction[] reactions;
//Wibblr? Not really sure what this applies to. :<
+
+ public DateTime? thread_ts;
}
}
diff --git a/MimeTypes.cs b/SlackAPI/MimeTypes.cs
similarity index 100%
rename from MimeTypes.cs
rename to SlackAPI/MimeTypes.cs
diff --git a/OwnedStampedMessage.cs b/SlackAPI/OwnedStampedMessage.cs
similarity index 100%
rename from OwnedStampedMessage.cs
rename to SlackAPI/OwnedStampedMessage.cs
diff --git a/Preferences.cs b/SlackAPI/Preferences.cs
similarity index 97%
rename from Preferences.cs
rename to SlackAPI/Preferences.cs
index 92f48a58..f0931eec 100644
--- a/Preferences.cs
+++ b/SlackAPI/Preferences.cs
@@ -55,7 +55,7 @@ public class Preferences
public string emoji_mode;
public string highlight_words;
//public string newxp_slackbot_step; //I don't even...
- public SearchSort search_sort;
+ public string search_sort;
public string push_loud_channels;
public string push_mention_channels;
public string push_loud_channels_set;
@@ -73,5 +73,6 @@ public class Preferences
public string mac_ssb_bounce;
public string last_snippet_type;
public int display_real_names_override;
+ public string muted_channels;
}
}
\ No newline at end of file
diff --git a/SlackAPI/ProfileIcons.cs b/SlackAPI/ProfileIcons.cs
new file mode 100644
index 00000000..db849a48
--- /dev/null
+++ b/SlackAPI/ProfileIcons.cs
@@ -0,0 +1,12 @@
+namespace SlackAPI
+{
+ public class ProfileIcons
+ {
+ public string image_24;
+ public string image_32;
+ public string image_48;
+ public string image_72;
+ public string image_192;
+ public string image_512;
+ }
+}
diff --git a/SlackAPI/Properties/InternalsVisibleTo.cs b/SlackAPI/Properties/InternalsVisibleTo.cs
new file mode 100644
index 00000000..d747d594
--- /dev/null
+++ b/SlackAPI/Properties/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly:InternalsVisibleTo("SlackAPI.Tests")]
\ No newline at end of file
diff --git a/SlackAPI/RPCMessages/AccessTokenResponse.cs b/SlackAPI/RPCMessages/AccessTokenResponse.cs
new file mode 100644
index 00000000..9c4266bb
--- /dev/null
+++ b/SlackAPI/RPCMessages/AccessTokenResponse.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("oauth.access")]
+ public class AccessTokenResponse : Response
+ {
+ public string access_token;
+ public string scope;
+ public string team_name;
+ public string team_id { get; set; }
+ public BotTokenResponse bot;
+ public IncomingWebhook incoming_webhook { get; set; }
+ }
+
+ public class BotTokenResponse
+ {
+ public string emoji;
+ public string image_24;
+ public string image_32;
+ public string image_48;
+ public string image_72;
+ public string image_192;
+
+ public bool deleted;
+ public UserProfile icons;
+ public string id;
+ public string name;
+ public string bot_user_id;
+ public string bot_access_token;
+ }
+
+ public class IncomingWebhook
+ {
+ public string channel { get; set; }
+ public string channel_id { get; set; }
+ public string configuration_url { get; set; }
+ public string url { get; set; }
+ }
+}
diff --git a/SlackAPI/RPCMessages/AppHomeTabResponse.cs b/SlackAPI/RPCMessages/AppHomeTabResponse.cs
new file mode 100644
index 00000000..e0b8db02
--- /dev/null
+++ b/SlackAPI/RPCMessages/AppHomeTabResponse.cs
@@ -0,0 +1,38 @@
+namespace SlackAPI
+{
+ [RequestPath("views.publish")]
+ public class AppHomeTabResponse : Response
+ {
+ public AppHomeTabView view;
+
+ public class AppHomeTabView
+ {
+ public string id;
+ public string team_id;
+ public string type;
+ public object close;
+ public object submit;
+ public Block[] blocks;
+ public string private_metadata;
+ public string callback_id;
+ public State state;
+ public string hash;
+ public bool clear_on_close;
+ public bool notify_on_close;
+ public string root_view_id;
+ public object previous_view_id;
+ public string app_id;
+ public string external_id;
+ public string bot_id;
+ }
+
+ public class State
+ {
+ public Values values;
+ }
+
+ public class Values
+ {
+ }
+ }
+}
diff --git a/RPCMessages/AuthSigninResponse.cs b/SlackAPI/RPCMessages/AuthSigninResponse.cs
similarity index 100%
rename from RPCMessages/AuthSigninResponse.cs
rename to SlackAPI/RPCMessages/AuthSigninResponse.cs
diff --git a/RPCMessages/AuthStartResponse.cs b/SlackAPI/RPCMessages/AuthStartResponse.cs
similarity index 100%
rename from RPCMessages/AuthStartResponse.cs
rename to SlackAPI/RPCMessages/AuthStartResponse.cs
diff --git a/RPCMessages/AuthTestResponse.cs b/SlackAPI/RPCMessages/AuthTestResponse.cs
similarity index 100%
rename from RPCMessages/AuthTestResponse.cs
rename to SlackAPI/RPCMessages/AuthTestResponse.cs
diff --git a/RPCMessages/ChannelCreateResponse.cs b/SlackAPI/RPCMessages/ChannelCreateResponse.cs
similarity index 100%
rename from RPCMessages/ChannelCreateResponse.cs
rename to SlackAPI/RPCMessages/ChannelCreateResponse.cs
diff --git a/RPCMessages/PresenseResponse.cs b/SlackAPI/RPCMessages/ChannelInviteResponse.cs
similarity index 50%
rename from RPCMessages/PresenseResponse.cs
rename to SlackAPI/RPCMessages/ChannelInviteResponse.cs
index 5c3c333f..41b2108b 100644
--- a/RPCMessages/PresenseResponse.cs
+++ b/SlackAPI/RPCMessages/ChannelInviteResponse.cs
@@ -6,13 +6,9 @@
namespace SlackAPI
{
- [RequestPath("users.setPresence")]
- public class PresenceResponse : Response
+ [RequestPath("channels.invite")]
+ public class ChannelInviteResponse : Response
{
- }
- public enum Presence
- {
- active,
- away
+ public Channel channel;
}
}
diff --git a/RPCMessages/ChannelListResponse.cs b/SlackAPI/RPCMessages/ChannelListResponse.cs
similarity index 100%
rename from RPCMessages/ChannelListResponse.cs
rename to SlackAPI/RPCMessages/ChannelListResponse.cs
diff --git a/RPCMessages/ChannelMessageHistory.cs b/SlackAPI/RPCMessages/ChannelMessageHistory.cs
similarity index 100%
rename from RPCMessages/ChannelMessageHistory.cs
rename to SlackAPI/RPCMessages/ChannelMessageHistory.cs
diff --git a/RPCMessages/ChannelSetTopicResponse.cs b/SlackAPI/RPCMessages/ChannelSetTopicResponse.cs
similarity index 100%
rename from RPCMessages/ChannelSetTopicResponse.cs
rename to SlackAPI/RPCMessages/ChannelSetTopicResponse.cs
diff --git a/SlackAPI/RPCMessages/ConversationsArchiveResponse.cs b/SlackAPI/RPCMessages/ConversationsArchiveResponse.cs
new file mode 100644
index 00000000..d3203251
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsArchiveResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.archive")]
+ public class ConversationsArchiveResponse : Response
+ {
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsCloseResponse.cs b/SlackAPI/RPCMessages/ConversationsCloseResponse.cs
new file mode 100644
index 00000000..d8a7fca8
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsCloseResponse.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.close")]
+ public class ConversationsCloseResponse : Response
+ {
+ public string no_op;
+ public string already_closed;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsCreateResponse.cs b/SlackAPI/RPCMessages/ConversationsCreateResponse.cs
new file mode 100644
index 00000000..ec71d2e3
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsCreateResponse.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.create")]
+ public class ConversationsCreateResponse : Response
+ {
+ public Channel channel;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsInviteResponse.cs b/SlackAPI/RPCMessages/ConversationsInviteResponse.cs
new file mode 100644
index 00000000..95f04de4
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsInviteResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.invite")]
+ public class ConversationsInviteResponse : Response
+ {
+ public Channel channel;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsJoinResponse.cs b/SlackAPI/RPCMessages/ConversationsJoinResponse.cs
new file mode 100644
index 00000000..873818de
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsJoinResponse.cs
@@ -0,0 +1,9 @@
+namespace SlackAPI.RPCMessages
+{
+
+ [RequestPath("conversations.join")]
+ public class ConversationsJoinResponse : Response
+ {
+ public Channel channel;
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI/RPCMessages/ConversationsKickResponse.cs b/SlackAPI/RPCMessages/ConversationsKickResponse.cs
new file mode 100644
index 00000000..daa081e3
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsKickResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.kick")]
+ public class ConversationsKickResponse : Response
+ {
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsLeaveResponse.cs b/SlackAPI/RPCMessages/ConversationsLeaveResponse.cs
new file mode 100644
index 00000000..0f66be8f
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsLeaveResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.leave")]
+ public class ConversationsLeaveResponse : Response
+ {
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsListResponse.cs b/SlackAPI/RPCMessages/ConversationsListResponse.cs
new file mode 100644
index 00000000..005938f6
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsListResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("conversations.list")]
+ public class ConversationsListResponse : Response
+ {
+ public Channel[] channels;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsMarkResponse.cs b/SlackAPI/RPCMessages/ConversationsMarkResponse.cs
new file mode 100644
index 00000000..fe7d6665
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsMarkResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.mark")]
+ public class ConversationsMarkResponse : Response
+ {
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsMembersResponse.cs b/SlackAPI/RPCMessages/ConversationsMembersResponse.cs
new file mode 100644
index 00000000..bd53a648
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsMembersResponse.cs
@@ -0,0 +1,8 @@
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("conversations.members")]
+ public class ConversationsMembersResponse : Response
+ {
+ public string[] members;
+ }
+}
\ No newline at end of file
diff --git a/SlackAPI/RPCMessages/ConversationsMessageHistory.cs b/SlackAPI/RPCMessages/ConversationsMessageHistory.cs
new file mode 100644
index 00000000..ffff5abc
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsMessageHistory.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("conversations.history")]
+ public class ConversationsMessageHistory : MessageHistory
+ {
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsOpenResponse.cs b/SlackAPI/RPCMessages/ConversationsOpenResponse.cs
new file mode 100644
index 00000000..527f8f16
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsOpenResponse.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.open")]
+ public class ConversationsOpenResponse : Response
+ {
+ public string no_op;
+ public string already_open;
+ public Channel channel;
+ public string error;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsRenameResponse.cs b/SlackAPI/RPCMessages/ConversationsRenameResponse.cs
new file mode 100644
index 00000000..e633eddd
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsRenameResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.rename")]
+ public class ConversationsRenameResponse : Response
+ {
+ public Channel channel;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsSetPurposeResponse.cs b/SlackAPI/RPCMessages/ConversationsSetPurposeResponse.cs
new file mode 100644
index 00000000..20d4fafe
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsSetPurposeResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.setPurpose")]
+ public class ConversationsSetPurposeResponse : Response
+ {
+ public string purpose;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsSetTopicResponse.cs b/SlackAPI/RPCMessages/ConversationsSetTopicResponse.cs
new file mode 100644
index 00000000..61ead8db
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsSetTopicResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.setTopic")]
+ public class ConversationsSetTopicResponse : Response
+ {
+ public string topic;
+ }
+}
diff --git a/SlackAPI/RPCMessages/ConversationsUnarchiveResponse.cs b/SlackAPI/RPCMessages/ConversationsUnarchiveResponse.cs
new file mode 100644
index 00000000..1d894e01
--- /dev/null
+++ b/SlackAPI/RPCMessages/ConversationsUnarchiveResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("conversations.unarchive")]
+ public class ConversationsUnarchiveResponse : Response
+ {
+ }
+}
diff --git a/RPCMessages/DeletedResponse.cs b/SlackAPI/RPCMessages/DeletedResponse.cs
similarity index 100%
rename from RPCMessages/DeletedResponse.cs
rename to SlackAPI/RPCMessages/DeletedResponse.cs
diff --git a/SlackAPI/RPCMessages/DialogOpenResponse.cs b/SlackAPI/RPCMessages/DialogOpenResponse.cs
new file mode 100644
index 00000000..926a339a
--- /dev/null
+++ b/SlackAPI/RPCMessages/DialogOpenResponse.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("dialog.open")]
+ public class DialogOpenResponse : Response
+ {
+ public ResponseMetadata response_metadata { get; set; }
+
+ public class ResponseMetadata
+ {
+ public string[] messages { get; set; }
+ }
+ }
+}
diff --git a/RPCMessages/DirectMessageConversationListResponse.cs b/SlackAPI/RPCMessages/DirectMessageConversationListResponse.cs
similarity index 100%
rename from RPCMessages/DirectMessageConversationListResponse.cs
rename to SlackAPI/RPCMessages/DirectMessageConversationListResponse.cs
diff --git a/SlackAPI/RPCMessages/FileDeleteResponse.cs b/SlackAPI/RPCMessages/FileDeleteResponse.cs
new file mode 100644
index 00000000..81c637a7
--- /dev/null
+++ b/SlackAPI/RPCMessages/FileDeleteResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SlackAPI
+{
+ [RequestPath("files.delete")]
+ public class FileDeleteResponse : Response
+ {
+ }
+}
diff --git a/RPCMessages/FileInfoResponse.cs b/SlackAPI/RPCMessages/FileInfoResponse.cs
similarity index 100%
rename from RPCMessages/FileInfoResponse.cs
rename to SlackAPI/RPCMessages/FileInfoResponse.cs
diff --git a/RPCMessages/FileListResponse.cs b/SlackAPI/RPCMessages/FileListResponse.cs
similarity index 100%
rename from RPCMessages/FileListResponse.cs
rename to SlackAPI/RPCMessages/FileListResponse.cs
diff --git a/RPCMessages/FileUploadResponse.cs b/SlackAPI/RPCMessages/FileUploadResponse.cs
similarity index 100%
rename from RPCMessages/FileUploadResponse.cs
rename to SlackAPI/RPCMessages/FileUploadResponse.cs
diff --git a/RPCMessages/FindTeamResponse.cs b/SlackAPI/RPCMessages/FindTeamResponse.cs
similarity index 100%
rename from RPCMessages/FindTeamResponse.cs
rename to SlackAPI/RPCMessages/FindTeamResponse.cs
diff --git a/RPCMessages/GroupArchiveResponse.cs b/SlackAPI/RPCMessages/GroupArchiveResponse.cs
similarity index 100%
rename from RPCMessages/GroupArchiveResponse.cs
rename to SlackAPI/RPCMessages/GroupArchiveResponse.cs
diff --git a/RPCMessages/GroupCloseResponse.cs b/SlackAPI/RPCMessages/GroupCloseResponse.cs
similarity index 100%
rename from RPCMessages/GroupCloseResponse.cs
rename to SlackAPI/RPCMessages/GroupCloseResponse.cs
diff --git a/RPCMessages/GroupCreateChildResponse.cs b/SlackAPI/RPCMessages/GroupCreateChildResponse.cs
similarity index 100%
rename from RPCMessages/GroupCreateChildResponse.cs
rename to SlackAPI/RPCMessages/GroupCreateChildResponse.cs
diff --git a/RPCMessages/GroupCreateResponse.cs b/SlackAPI/RPCMessages/GroupCreateResponse.cs
similarity index 100%
rename from RPCMessages/GroupCreateResponse.cs
rename to SlackAPI/RPCMessages/GroupCreateResponse.cs
diff --git a/RPCMessages/GroupInviteResponse.cs b/SlackAPI/RPCMessages/GroupInviteResponse.cs
similarity index 100%
rename from RPCMessages/GroupInviteResponse.cs
rename to SlackAPI/RPCMessages/GroupInviteResponse.cs
diff --git a/RPCMessages/GroupKickResponse.cs b/SlackAPI/RPCMessages/GroupKickResponse.cs
similarity index 100%
rename from RPCMessages/GroupKickResponse.cs
rename to SlackAPI/RPCMessages/GroupKickResponse.cs
diff --git a/RPCMessages/GroupLeaveResponse.cs b/SlackAPI/RPCMessages/GroupLeaveResponse.cs
similarity index 100%
rename from RPCMessages/GroupLeaveResponse.cs
rename to SlackAPI/RPCMessages/GroupLeaveResponse.cs
diff --git a/RPCMessages/GroupListResponse.cs b/SlackAPI/RPCMessages/GroupListResponse.cs
similarity index 100%
rename from RPCMessages/GroupListResponse.cs
rename to SlackAPI/RPCMessages/GroupListResponse.cs
diff --git a/RPCMessages/GroupMarkResponse.cs b/SlackAPI/RPCMessages/GroupMarkResponse.cs
similarity index 100%
rename from RPCMessages/GroupMarkResponse.cs
rename to SlackAPI/RPCMessages/GroupMarkResponse.cs
diff --git a/RPCMessages/GroupMessageHistory.cs b/SlackAPI/RPCMessages/GroupMessageHistory.cs
similarity index 100%
rename from RPCMessages/GroupMessageHistory.cs
rename to SlackAPI/RPCMessages/GroupMessageHistory.cs
diff --git a/RPCMessages/GroupOpenResponse.cs b/SlackAPI/RPCMessages/GroupOpenResponse.cs
similarity index 100%
rename from RPCMessages/GroupOpenResponse.cs
rename to SlackAPI/RPCMessages/GroupOpenResponse.cs
diff --git a/RPCMessages/GroupRenameResponse.cs b/SlackAPI/RPCMessages/GroupRenameResponse.cs
similarity index 100%
rename from RPCMessages/GroupRenameResponse.cs
rename to SlackAPI/RPCMessages/GroupRenameResponse.cs
diff --git a/RPCMessages/GroupResponse.cs b/SlackAPI/RPCMessages/GroupResponse.cs
similarity index 100%
rename from RPCMessages/GroupResponse.cs
rename to SlackAPI/RPCMessages/GroupResponse.cs
diff --git a/RPCMessages/GroupSetPurposeResponse.cs b/SlackAPI/RPCMessages/GroupSetPurposeResponse.cs
similarity index 100%
rename from RPCMessages/GroupSetPurposeResponse.cs
rename to SlackAPI/RPCMessages/GroupSetPurposeResponse.cs
diff --git a/RPCMessages/GroupSetTopicResponse.cs b/SlackAPI/RPCMessages/GroupSetTopicResponse.cs
similarity index 100%
rename from RPCMessages/GroupSetTopicResponse.cs
rename to SlackAPI/RPCMessages/GroupSetTopicResponse.cs
diff --git a/RPCMessages/GroupUnarchiveResponse.cs b/SlackAPI/RPCMessages/GroupUnarchiveResponse.cs
similarity index 100%
rename from RPCMessages/GroupUnarchiveResponse.cs
rename to SlackAPI/RPCMessages/GroupUnarchiveResponse.cs
diff --git a/RPCMessages/JoinDirectMessageChannelResponse.cs b/SlackAPI/RPCMessages/JoinDirectMessageChannelResponse.cs
similarity index 76%
rename from RPCMessages/JoinDirectMessageChannelResponse.cs
rename to SlackAPI/RPCMessages/JoinDirectMessageChannelResponse.cs
index 2e9d54bb..a7095398 100644
--- a/RPCMessages/JoinDirectMessageChannelResponse.cs
+++ b/SlackAPI/RPCMessages/JoinDirectMessageChannelResponse.cs
@@ -1,6 +1,6 @@
namespace SlackAPI
{
- [RequestPath("im.open")]
+ [RequestPath("conversations.open")]
public class JoinDirectMessageChannelResponse : Response
{
public Channel channel;
diff --git a/RPCMessages/LoginResponse.cs b/SlackAPI/RPCMessages/LoginResponse.cs
similarity index 100%
rename from RPCMessages/LoginResponse.cs
rename to SlackAPI/RPCMessages/LoginResponse.cs
diff --git a/RPCMessages/MarkResponse.cs b/SlackAPI/RPCMessages/MarkResponse.cs
similarity index 100%
rename from RPCMessages/MarkResponse.cs
rename to SlackAPI/RPCMessages/MarkResponse.cs
diff --git a/RPCMessages/MessageHistory.cs b/SlackAPI/RPCMessages/MessageHistory.cs
similarity index 92%
rename from RPCMessages/MessageHistory.cs
rename to SlackAPI/RPCMessages/MessageHistory.cs
index ba2541ca..8fb797a1 100644
--- a/RPCMessages/MessageHistory.cs
+++ b/SlackAPI/RPCMessages/MessageHistory.cs
@@ -12,6 +12,7 @@ public class MessageHistory : Response
public DateTime latest;
public Message[] messages;
public bool has_more;
+ public int unread_count_display;
public bool channel_not_found;
public bool invalid_ts_latest;
diff --git a/SlackAPI/RPCMessages/PostEphemeralResponse.cs b/SlackAPI/RPCMessages/PostEphemeralResponse.cs
new file mode 100644
index 00000000..bf48a50e
--- /dev/null
+++ b/SlackAPI/RPCMessages/PostEphemeralResponse.cs
@@ -0,0 +1,8 @@
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("chat.postEphemeral")]
+ public class PostEphemeralResponse : Response
+ {
+ public string message_ts;
+ }
+}
diff --git a/RPCMessages/PostMessageResponse.cs b/SlackAPI/RPCMessages/PostMessageResponse.cs
similarity index 100%
rename from RPCMessages/PostMessageResponse.cs
rename to SlackAPI/RPCMessages/PostMessageResponse.cs
diff --git a/SlackAPI/RPCMessages/PresenseResponse.cs b/SlackAPI/RPCMessages/PresenseResponse.cs
new file mode 100644
index 00000000..7b3cd5de
--- /dev/null
+++ b/SlackAPI/RPCMessages/PresenseResponse.cs
@@ -0,0 +1,14 @@
+namespace SlackAPI
+{
+ [RequestPath("users.setPresence")]
+ public class PresenceResponse : Response
+ {
+ }
+
+ public enum Presence
+ {
+ active,
+ away,
+ auto
+ }
+}
diff --git a/SlackAPI/RPCMessages/ScheduleMessageResponse.cs b/SlackAPI/RPCMessages/ScheduleMessageResponse.cs
new file mode 100644
index 00000000..70a4b5c9
--- /dev/null
+++ b/SlackAPI/RPCMessages/ScheduleMessageResponse.cs
@@ -0,0 +1,21 @@
+namespace SlackAPI
+{
+ [RequestPath("chat.scheduleMessage")]
+ public class ScheduleMessageResponse : Response
+ {
+ public string ts;
+ public string channel;
+ public string scheduled_message_id;
+ public int post_at;
+ public Message message;
+
+ public class Message
+ {
+ public string text;
+ public string user;
+ public string username;
+ public string type;
+ public string subtype;
+ }
+ }
+}
diff --git a/RPCMessages/SearchResponseAll.cs b/SlackAPI/RPCMessages/SearchResponseAll.cs
similarity index 100%
rename from RPCMessages/SearchResponseAll.cs
rename to SlackAPI/RPCMessages/SearchResponseAll.cs
diff --git a/RPCMessages/SearchResponseFiles.cs b/SlackAPI/RPCMessages/SearchResponseFiles.cs
similarity index 91%
rename from RPCMessages/SearchResponseFiles.cs
rename to SlackAPI/RPCMessages/SearchResponseFiles.cs
index 9b8b284a..4cf8ebe1 100644
--- a/RPCMessages/SearchResponseFiles.cs
+++ b/SlackAPI/RPCMessages/SearchResponseFiles.cs
@@ -10,7 +10,7 @@ namespace SlackAPI
public class SearchResponseFiles : Response
{
public string query;
- SearchResponseMessagesContainer messages;
+ public SearchResponseFilesContainer files;
}
public class SearchResponseFilesContainer
diff --git a/RPCMessages/SearchResponseMessages.cs b/SlackAPI/RPCMessages/SearchResponseMessages.cs
similarity index 95%
rename from RPCMessages/SearchResponseMessages.cs
rename to SlackAPI/RPCMessages/SearchResponseMessages.cs
index 6131f949..8b1eef70 100644
--- a/RPCMessages/SearchResponseMessages.cs
+++ b/SlackAPI/RPCMessages/SearchResponseMessages.cs
@@ -10,7 +10,6 @@ namespace SlackAPI
public class SearchResponseMessages : Response
{
public string query;
- SearchResponseMessagesContainer messages;
}
public class SearchResponseMessagesContainer
@@ -56,8 +55,10 @@ public class PaginationInformation
public enum SearchSort
{
+ not_set,
score,
- timestamp
+ timestamp,
+ not_set_new_user
}
public enum SearchSortDirection
diff --git a/RPCMessages/StarListResponse.cs b/SlackAPI/RPCMessages/StarListResponse.cs
similarity index 100%
rename from RPCMessages/StarListResponse.cs
rename to SlackAPI/RPCMessages/StarListResponse.cs
diff --git a/RPCMessages/UpdateResponse.cs b/SlackAPI/RPCMessages/UpdateResponse.cs
similarity index 100%
rename from RPCMessages/UpdateResponse.cs
rename to SlackAPI/RPCMessages/UpdateResponse.cs
diff --git a/RPCMessages/UserCountsResponse.cs b/SlackAPI/RPCMessages/UserCountsResponse.cs
similarity index 100%
rename from RPCMessages/UserCountsResponse.cs
rename to SlackAPI/RPCMessages/UserCountsResponse.cs
diff --git a/SlackAPI/RPCMessages/UserEmailLookupResponse.cs b/SlackAPI/RPCMessages/UserEmailLookupResponse.cs
new file mode 100644
index 00000000..487adcda
--- /dev/null
+++ b/SlackAPI/RPCMessages/UserEmailLookupResponse.cs
@@ -0,0 +1,8 @@
+namespace SlackAPI.RPCMessages
+{
+ [RequestPath("users.lookupByEmail")]
+ public class UserEmailLookupResponse : Response
+ {
+ public User user;
+ }
+}
\ No newline at end of file
diff --git a/RPCMessages/UserGetPresenceResponse.cs b/SlackAPI/RPCMessages/UserGetPresenceResponse.cs
similarity index 100%
rename from RPCMessages/UserGetPresenceResponse.cs
rename to SlackAPI/RPCMessages/UserGetPresenceResponse.cs
diff --git a/RPCMessages/UserInfoResponse.cs b/SlackAPI/RPCMessages/UserInfoResponse.cs
similarity index 100%
rename from RPCMessages/UserInfoResponse.cs
rename to SlackAPI/RPCMessages/UserInfoResponse.cs
diff --git a/RPCMessages/UserListResponse.cs b/SlackAPI/RPCMessages/UserListResponse.cs
similarity index 100%
rename from RPCMessages/UserListResponse.cs
rename to SlackAPI/RPCMessages/UserListResponse.cs
diff --git a/RPCMessages/UserPreferencesResponse.cs b/SlackAPI/RPCMessages/UserPreferencesResponse.cs
similarity index 100%
rename from RPCMessages/UserPreferencesResponse.cs
rename to SlackAPI/RPCMessages/UserPreferencesResponse.cs
diff --git a/Reaction.cs b/SlackAPI/Reaction.cs
similarity index 100%
rename from Reaction.cs
rename to SlackAPI/Reaction.cs
diff --git a/ReactionAddedResponse.cs b/SlackAPI/ReactionAddedResponse.cs
similarity index 100%
rename from ReactionAddedResponse.cs
rename to SlackAPI/ReactionAddedResponse.cs
diff --git a/Request.cs b/SlackAPI/Request.cs
similarity index 52%
rename from Request.cs
rename to SlackAPI/Request.cs
index 34b82794..e7ed01a5 100644
--- a/Request.cs
+++ b/SlackAPI/Request.cs
@@ -1,6 +1,7 @@
using Newtonsoft.Json;
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
@@ -9,6 +10,8 @@
namespace SlackAPI
{
+
+
class RequestState
where K : Response
{
@@ -21,6 +24,7 @@ class RequestState
public RequestState(HttpWebRequest requestData, Tuple[] postParameters, Action toCallback)
{
+ if (requestData == null) throw new ArgumentNullException("requestData can not be null");
request = requestData;
Post = postParameters;
callback = toCallback;
@@ -68,27 +72,61 @@ internal void GotResponse(IAsyncResult result)
{
try
{
- response = (HttpWebResponse)request.EndGetResponse(result);
+ response = (HttpWebResponse)request?.EndGetResponse(result);
Success = true;
}
catch (WebException we)
{
+ // If we don't get a response, let the exception bubble up as we can't do anything
+ if (we.Response == null)
+ {
+ var defaultResponse = CreateDefaultResponseForError(we);
+ callback?.Invoke(defaultResponse);
+ return;
+ }
+
//Anything that doesn't return error 200 throws an exception. Sucks. :l
response = (HttpWebResponse)we.Response;
//TODO: Handle timeouts, etc?
}
+ catch (Exception e)
+ {
+ var defaultResponse = CreateDefaultResponseForError(e);
+ callback?.Invoke(defaultResponse);
+ return;
+ }
K responseObj;
+ if (response == null)
+ {
+ responseObj = CreateDefaultResponseForError(new Exception("Empty response"));
+ callback?.Invoke(responseObj);
+ return;
+ }
- using(Stream responseReading = response.GetResponseStream())
- using (StreamReader reader = new StreamReader(responseReading))
+ try
+ {
+ using (Stream responseReading = response.GetResponseStream())
+ using (StreamReader reader = new StreamReader(responseReading))
+ {
+ string responseData = reader.ReadToEnd();
+ responseObj = responseData.Deserialize();
+ }
+ }
+ catch (Exception e)
{
- string responseData = reader.ReadToEnd();
- responseObj = responseData.Deserialize();
+ responseObj = CreateDefaultResponseForError(e);
}
- if(callback != null)
- callback(responseObj);
+ callback?.Invoke(responseObj);
+ }
+
+ private K CreateDefaultResponseForError(Exception e)
+ {
+ var defaultResponse = (K)Activator.CreateInstance();
+ defaultResponse.ok = false;
+ defaultResponse.error = e.ToString();
+ return defaultResponse;
}
}
@@ -104,20 +142,36 @@ public RequestPath(string requestPath, bool isPrimaryAPI = true)
UsePrimaryAPI = isPrimaryAPI;
}
- static Dictionary paths = new Dictionary();
+ static ConcurrentDictionary paths = new ConcurrentDictionary();
public static RequestPath GetRequestPath()
{
Type t = typeof(K);
- if (paths.ContainsKey(t))
- return paths[t];
+ if (paths.TryGetValue(t, out var path))
+ return path;
TypeInfo info = t.GetTypeInfo();
- RequestPath path = info.GetCustomAttribute();
+ path = info.GetCustomAttribute();
if (path == null) throw new InvalidOperationException(string.Format("No valid request path for {0}", t.Name));
- paths.Add(t, path);
+ try
+ {
+ // Some other thread may have already placed the path to the dictionary,
+ // the original one will remain there and current one will be GCed once it
+ // gets out of scope of this request.
+ paths.TryAdd(t, path);
+ }
+ catch (Exception e)
+ {
+ // There is a slight chance of TryAdd throwing, we want to be extra safe
+ // so we just consume it and leave the dictionary as-is, next call of
+ // the same function will try to add the path again.
+ // This may be removed in the future if TryAdd is verified as safe.
+ // See #190.
+ Trace.TraceError(e.ToString());
+ }
+
return path;
}
}
diff --git a/RequestStateForTask.cs b/SlackAPI/RequestStateForTask.cs
similarity index 93%
rename from RequestStateForTask.cs
rename to SlackAPI/RequestStateForTask.cs
index 63075b73..f535f5a4 100644
--- a/RequestStateForTask.cs
+++ b/SlackAPI/RequestStateForTask.cs
@@ -40,7 +40,7 @@ private async Task ExecuteResult()
HttpWebResponse response = null;
try
{
- response = (HttpWebResponse)await this.request.GetResponseAsync();
+ response = (HttpWebResponse)await this.request.GetResponseAsync().ConfigureAwait(false);
Success = true;
}
catch (WebException we)
@@ -68,7 +68,7 @@ private async Task ExecutePost()
{
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
- using (Stream requestStream = await request.GetRequestStreamAsync())
+ using (Stream requestStream = await request.GetRequestStreamAsync().ConfigureAwait(false))
{
if (Post.Length > 0)
{
@@ -80,7 +80,7 @@ private async Task ExecutePost()
if (!first)
writer.Write('&');
- await writer.WriteAsync(string.Format("{0}={1}", Uri.EscapeDataString(postEntry.Item1), Uri.EscapeDataString(postEntry.Item2)));
+ await writer.WriteAsync(string.Format("{0}={1}", Uri.EscapeDataString(postEntry.Item1), Uri.EscapeDataString(postEntry.Item2))).ConfigureAwait(false);
first = false;
}
@@ -88,7 +88,7 @@ private async Task ExecutePost()
}
}
- return await this.ExecuteResult();
+ return await this.ExecuteResult().ConfigureAwait(false);
}
}
}
diff --git a/Response.cs b/SlackAPI/Response.cs
similarity index 71%
rename from Response.cs
rename to SlackAPI/Response.cs
index 6c60a0dc..8cd5e2c7 100644
--- a/Response.cs
+++ b/SlackAPI/Response.cs
@@ -17,11 +17,22 @@ public abstract class Response
/// if ok is false, then this is the reason-code
///
public string error;
+ public string needed;
+ public string provided;
+ public string warning;
public void AssertOk()
{
if (!(ok))
throw new InvalidOperationException(string.Format("An error occurred: {0}", this.error));
}
+
+ public ResponseMetaData response_metadata;
+ }
+
+ public class ResponseMetaData
+ {
+ public string next_cursor;
+ public string[] messages;
}
}
diff --git a/SlackAPI/SlackAPI.csproj b/SlackAPI/SlackAPI.csproj
new file mode 100644
index 00000000..934c5356
--- /dev/null
+++ b/SlackAPI/SlackAPI.csproj
@@ -0,0 +1,40 @@
+
+
+
+ net45;netstandard2.0
+ Full
+ SlackAPI
+ SlackAPI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties\GlobalAssemblyInfo.cs
+
+
+
+
+ $(DefineConstants);RELEASE
+
+
+
diff --git a/SlackClient.cs b/SlackAPI/SlackClient.cs
similarity index 57%
rename from SlackClient.cs
rename to SlackAPI/SlackClient.cs
index 69b65aa4..e81a55c3 100644
--- a/SlackClient.cs
+++ b/SlackAPI/SlackClient.cs
@@ -6,34 +6,18 @@
using System.Net;
using System.Net.Http;
using System.Text;
-using System.Threading;
using SlackAPI.RPCMessages;
namespace SlackAPI
{
///
/// SlackClient is intended to solely handle RPC (HTTP-based) functionality. Does not handle WebSocket connectivity.
- ///
+ ///
/// For WebSocket connectivity, refer to
///
- public class SlackClient
+ public class SlackClient : SlackClientBase
{
- readonly string APIToken;
- bool authWorks = false;
-
- const string APIBaseLocation = "https://slack.com/api/";
- const int Timeout = 5000;
-
- const char StartHighlight = '\uE001';
- const char EndHightlight = '\uE001';
-
- static List> replacers = new List>(){
- new Tuple("&", "&"),
- new Tuple("<", "<"),
- new Tuple(">", ">")
- };
-
- //Dictionary> socketCallbacks;
+ private readonly string APIToken;
public Self MySelf;
public User MyData;
@@ -42,6 +26,7 @@ public class SlackClient
public List starredChannels;
public List Users;
+ public List Bots;
public List Channels;
public List Groups;
public List DirectMessages;
@@ -52,22 +37,24 @@ public class SlackClient
public Dictionary DirectMessageLookup;
public Dictionary ConversationLookup;
- //public event Action OnUserTyping;
- //public event Action OnMessageReceived;
- //public event Action OnPresenceChanged;
- //public event Action OnHello;
-
public SlackClient(string token)
{
APIToken = token;
}
+ public SlackClient(string token, IWebProxy proxySettings)
+ : base(proxySettings)
+ {
+ APIToken = token;
+ }
+
public virtual void Connect(Action onConnected = null, Action onSocketConnected = null)
{
EmitLogin((loginDetails) =>
{
- if(loginDetails.ok)
- Connected(loginDetails);
+ if (loginDetails.ok)
+ Connected(loginDetails);
+
if (onConnected != null)
onConnected(loginDetails);
});
@@ -80,6 +67,7 @@ protected virtual void Connected(LoginResponse loginDetails)
MyTeam = loginDetails.team;
Users = new List(loginDetails.users.Where((c) => !c.deleted));
+ Bots = new List(loginDetails.bots.Where((c) => !c.deleted));
Channels = new List(loginDetails.channels);
Groups = new List(loginDetails.groups);
DirectMessages = new List(loginDetails.ims.Where((c) => Users.Exists((a) => a.id == c.user) && c.id != MySelf.id));
@@ -117,103 +105,72 @@ protected virtual void Connected(LoginResponse loginDetails)
}
}
- internal static Uri GetSlackUri(string path, Tuple[] getParameters)
+ public void APIRequestWithToken(Action callback, params Tuple[] getParameters)
+ where K : Response
{
- string parameters = getParameters
- .Where(x => x.Item2 != null)
- .Select(new Func, string>(a =>
- {
- try
- {
- return string.Format("{0}={1}", Uri.EscapeDataString(a.Item1), Uri.EscapeDataString(a.Item2));
- }
- catch (Exception ex)
- {
- throw new InvalidOperationException(string.Format("Failed when processing '{0}'.", a), ex);
- }
- }))
- .Aggregate((a, b) =>
- {
- if (string.IsNullOrEmpty(a))
- return b;
- else
- return string.Format("{0}&{1}", a, b);
- });
-
- Uri requestUri = new Uri(string.Format("{0}?{1}", path, parameters));
- return requestUri;
+ APIRequest(callback, getParameters, new Tuple[0], APIToken);
}
- public static void APIRequest(Action callback, Tuple[] getParameters, Tuple[] postParameters)
- where K : Response
+ public void TestAuth(Action callback)
{
- RequestPath path = RequestPath.GetRequestPath();
- //TODO: Custom paths? Appropriate subdomain paths? Not sure.
- //Maybe store custom path in the requestpath.path itself?
-
- Uri requestUri = GetSlackUri(Path.Combine(APIBaseLocation, path.Path), getParameters);
- HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestUri);
+ APIRequestWithToken(callback);
+ }
- //This will handle all of the processing.
- RequestState state = new RequestState(request, postParameters, callback);
- state.Begin();
+ public void GetUserList(Action callback)
+ {
+ APIRequestWithToken(callback);
}
- public static void APIGetRequest(Action callback, params Tuple[] getParameters)
- where K : Response
+ public void GetUserByEmail(Action callback, string email)
{
- APIRequest(callback, getParameters, new Tuple[0]);
+ APIRequestWithToken(callback, new Tuple("email", email));
}
- public void APIRequestWithToken(Action callback, params Tuple[] getParameters)
- where K : Response
+ public void ChannelsCreate(Action callback, string name) {
+ APIRequestWithToken(callback, new Tuple("name", name));
+ }
+
+ public void ChannelsInvite(Action callback, string userId, string channelId)
{
- Tuple[] tokenArray = new Tuple[]{
- new Tuple("token", APIToken)
- };
+ List> parameters = new List>();
- if (getParameters != null && getParameters.Length > 0)
- tokenArray = tokenArray.Concat(getParameters).ToArray();
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("user", userId));
- APIRequest(callback, tokenArray, new Tuple[0]);
+ APIRequestWithToken(callback, parameters.ToArray());
}
- [Obsolete("Please use the OAuth method for authenticating users")]
- public static void StartAuth(Action callback, string email)
+ public void GetConversationsList(Action callback, string cursor = "", bool ExcludeArchived = true, int limit = 100, string[] types = null)
{
- APIRequest(callback, new Tuple[] { new Tuple("email", email) }, new Tuple[0]);
- }
+ List> parameters = new List>()
+ {
+ Tuple.Create("exclude_archived", ExcludeArchived ? "1" : "0")
+ };
+ if (limit > 0)
+ parameters.Add(Tuple.Create("limit", limit.ToString()));
+ if ((types != null) && types.Any())
+ parameters.Add(Tuple.Create("types", string.Join(",", types)));
+ if (!string.IsNullOrEmpty(cursor))
+ parameters.Add(Tuple.Create("cursor", cursor));
- public static void FindTeam(Action callback, string team)
- {
- //This seems to accept both 'team.slack.com' and just plain 'team'.
- //Going to go with the latter.
- Tuple domainName = new Tuple("domain", team);
- APIRequest(callback, new Tuple[] { domainName }, new Tuple[0]);
+ APIRequestWithToken(callback, parameters.ToArray());
}
-
- public static void AuthSignin(Action callback, string userId, string teamId, string password)
+
+ public void GetConversationsMembers(Action callback, string channelId, string cursor = "", int limit = 100)
{
- APIRequest(callback, new Tuple[] {
- new Tuple("user", userId),
- new Tuple("team", teamId),
- new Tuple("password", password)
- }, new Tuple[0]);
- }
+ List> parameters = new List>
+ {
+ new Tuple("channel", channelId)
+ };
+ if (limit > 0)
+ parameters.Add(Tuple.Create("limit", limit.ToString()));
+ if (!string.IsNullOrEmpty(cursor))
+ parameters.Add(Tuple.Create("cursor", cursor));
- public void TestAuth(Action callback)
- {
- APIRequestWithToken(callback);
+ APIRequestWithToken(callback, parameters.ToArray());
}
- public void GetUserList(Action callback)
- {
- APIRequestWithToken(callback);
- }
- public void ChannelsCreate(Action callback, string name) {
- APIRequestWithToken(callback, new Tuple("name", name));
- }
- public void GetChannelList(Action callback, bool ExcludeArchived = true)
+ public void GetChannelList(Action callback, bool ExcludeArchived = true)
{
APIRequestWithToken(callback, new Tuple("exclude_archived", ExcludeArchived ? "1" : "0"));
}
@@ -228,7 +185,7 @@ public void GetDirectMessageList(Action c
APIRequestWithToken(callback);
}
- public void GetFiles(Action callback, string userId = null, DateTime? from = null, DateTime? to = null, int? count = null, int? page = null, FileTypes types = FileTypes.all)
+ public void GetFiles(Action callback, string userId = null, DateTime? from = null, DateTime? to = null, int? count = null, int? page = null, FileTypes types = FileTypes.all, string channel = null)
{
List> parameters = new List>();
@@ -244,7 +201,7 @@ public void GetFiles(Action callback, string userId = null, Da
if (!types.HasFlag(FileTypes.all))
{
FileTypes[] values = (FileTypes[])Enum.GetValues(typeof(FileTypes));
-
+
StringBuilder building = new StringBuilder();
bool first = true;
for (int i = 0; i < values.Length; ++i)
@@ -269,38 +226,48 @@ public void GetFiles(Action callback, string userId = null, Da
if (page.HasValue)
parameters.Add(new Tuple("page", page.Value.ToString()));
+ if (!string.IsNullOrEmpty(channel))
+ parameters.Add(new Tuple("channel", channel));
+
APIRequestWithToken(callback, parameters.ToArray());
}
- void GetHistory(Action historyCallback, string channel, DateTime? latest = null, DateTime? oldest = null, int? count = null)
+ void GetHistory(Action historyCallback, string channel, DateTime? latest = null, DateTime? oldest = null, int? count = null, bool? unreads = false)
where K : MessageHistory
{
List> parameters = new List>();
parameters.Add(new Tuple("channel", channel));
-
+
if(latest.HasValue)
parameters.Add(new Tuple("latest", latest.Value.ToProperTimeStamp()));
if(oldest.HasValue)
parameters.Add(new Tuple("oldest", oldest.Value.ToProperTimeStamp()));
if(count.HasValue)
parameters.Add(new Tuple("count", count.Value.ToString()));
+ if (unreads.HasValue)
+ parameters.Add(new Tuple("unreads", unreads.Value ? "1" : "0"));
APIRequestWithToken(historyCallback, parameters.ToArray());
}
- public void GetChannelHistory(Action callback, Channel channelInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null)
+ public void GetChannelHistory(Action callback, Channel channelInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null, bool? unreads = false)
{
- GetHistory(callback, channelInfo.id, latest, oldest, count);
+ GetHistory(callback, channelInfo.id, latest, oldest, count, unreads);
}
- public void GetDirectMessageHistory(Action callback, DirectMessageConversation conversationInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null)
+ public void GetDirectMessageHistory(Action callback, DirectMessageConversation conversationInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null, bool? unreads = false)
{
- GetHistory(callback, conversationInfo.id, latest, oldest, count);
+ GetHistory(callback, conversationInfo.id, latest, oldest, count, unreads);
}
- public void GetGroupHistory(Action callback, Channel groupInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null)
+ public void GetGroupHistory(Action callback, Channel groupInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null, bool? unreads = false)
{
- GetHistory(callback, groupInfo.id, latest, oldest, count);
+ GetHistory(callback, groupInfo.id, latest, oldest, count, unreads);
+ }
+
+ public void GetConversationsHistory(Action callback, Channel conversationInfo, DateTime? latest = null, DateTime? oldest = null, int? count = null, bool? unreads = false)
+ {
+ GetHistory(callback, conversationInfo.id, latest, oldest, count, unreads);
}
public void MarkChannel(Action callback, string channelId, DateTime ts)
@@ -316,7 +283,7 @@ public void GetFileInfo(Action callback, string fileId, int? p
List> parameters = new List>();
parameters.Add(new Tuple("file", fileId));
-
+
if(count.HasValue)
parameters.Add(new Tuple("count", count.Value.ToString()));
@@ -418,13 +385,112 @@ public void GroupsUnarchive(Action callback, string chan
#endregion
- public void SearchAll(Action callback, string query, SearchSort? sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
+ #region Conversations
+ public void ConversationsArchive(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ public void ConversationsClose(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ public void ConversationsCreate(Action callback, string name)
+ {
+ APIRequestWithToken(callback, new Tuple("name", name));
+ }
+
+ public void ConversationsInvite(Action callback, string channelId, string[] userIds)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("users", string.Join(",", userIds)));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsJoin(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ public void ConversationsKick(Action callback, string channelId, string userId)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("user", userId));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsLeave(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ public void ConversationsMark(Action callback, string channelId, DateTime ts)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("ts", ts.ToProperTimeStamp()));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsOpen(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ public void ConversationsRename(Action callback, string channelId, string name)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("name", name));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsSetPurpose(Action callback, string channelId, string purpose)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("purpose", purpose));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsSetTopic(Action callback, string channelId, string topic)
+ {
+ List> parameters = new List>();
+
+ parameters.Add(new Tuple("channel", channelId));
+ parameters.Add(new Tuple("topic", topic));
+
+ APIRequestWithToken(callback, parameters.ToArray());
+ }
+
+ public void ConversationsUnarchive(Action callback, string channelId)
+ {
+ APIRequestWithToken(callback, new Tuple("channel", channelId));
+ }
+
+ #endregion
+
+
+ public void SearchAll(Action callback, string query, string sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
{
List> parameters = new List>();
parameters.Add(new Tuple("query", query));
- if (sorting.HasValue)
- parameters.Add(new Tuple("sort", sorting.Value.ToString()));
+ if (sorting != null)
+ parameters.Add(new Tuple("sort", sorting));
if (direction.HasValue)
parameters.Add(new Tuple("sort_dir", direction.Value.ToString()));
@@ -441,13 +507,13 @@ public void SearchAll(Action callback, string query, SearchSo
APIRequestWithToken(callback, parameters.ToArray());
}
- public void SearchMessages(Action callback, string query, SearchSort? sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
+ public void SearchMessages(Action callback, string query, string sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
{
List> parameters = new List>();
parameters.Add(new Tuple("query", query));
- if (sorting.HasValue)
- parameters.Add(new Tuple("sort", sorting.Value.ToString()));
+ if (sorting != null)
+ parameters.Add(new Tuple("sort", sorting));
if (direction.HasValue)
parameters.Add(new Tuple("sort_dir", direction.Value.ToString()));
@@ -464,13 +530,13 @@ public void SearchMessages(Action callback, string query
APIRequestWithToken(callback, parameters.ToArray());
}
- public void SearchFiles(Action callback, string query, SearchSort? sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
+ public void SearchFiles(Action callback, string query, string sorting = null, SearchSortDirection? direction = null, bool enableHighlights = false, int? count = null, int? page = null)
{
List> parameters = new List>();
parameters.Add(new Tuple("query", query));
- if (sorting.HasValue)
- parameters.Add(new Tuple("sort", sorting.Value.ToString()));
+ if (sorting != null)
+ parameters.Add(new Tuple("sort", sorting));
if (direction.HasValue)
parameters.Add(new Tuple("sort_dir", direction.Value.ToString()));
@@ -489,7 +555,7 @@ public void SearchFiles(Action callback, string query, Sear
public void GetStars(Action callback, string userId = null, int? count = null, int? page = null){
List> parameters = new List>();
-
+
if(!string.IsNullOrEmpty(userId))
parameters.Add(new Tuple("user", userId));
@@ -540,7 +606,7 @@ public void GetInfo(Action callback, string user)
APIRequestWithToken(callback, new Tuple("user", user));
}
- #endregion
+ #endregion
public void EmitLogin(Action callback, string agent = "Inumedia.SlackAPI")
{
@@ -555,8 +621,9 @@ public void Update(
string botName = null,
string parse = null,
bool linkNames = false,
+ IBlock[] blocks = null,
Attachment[] attachments = null,
- bool as_user = false)
+ bool? as_user = null)
{
List> parameters = new List>();
@@ -573,17 +640,29 @@ public void Update(
if (linkNames)
parameters.Add(new Tuple("link_names", "1"));
- if (attachments != null && attachments.Length > 0)
- parameters.Add(new Tuple