Skip to content

Commit 0c4a28d

Browse files
committed
Adds support for file uploads
1 parent 3c58d5c commit 0c4a28d

File tree

9 files changed

+181
-1
lines changed

9 files changed

+181
-1
lines changed

Samples/HelloWorld/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class DoStuff : IHandleAppMentions
2929
public Task<EventHandledResponse> Handle(EventMetaData eventMetadata, AppMentionEvent slackEvent)
3030
{
3131
Console.WriteLine("Doing stuff!");
32+
Console.WriteLine(JsonSerializer.Serialize(slackEvent));
3233
return Task.FromResult(new EventHandledResponse("yolo"));
3334
}
3435
}

source/src/Slackbot.Net.SlackClients.Http/Extensions/HttpClientExtensions.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,40 @@ public static async Task<T> PostParametersAsForm<T>(this HttpClient httpClient,
8484

8585
return resObj;
8686
}
87+
88+
public static async Task<T> PostParametersAsMultiPartFormData<T>(this HttpClient httpClient, IEnumerable<KeyValuePair<string, string>> parameters, Byte[] bytes, string api, Action<string> logger = null) where T: Response
89+
{
90+
var request = new HttpRequestMessage(HttpMethod.Post, api);
91+
92+
if (parameters != null && parameters.Any())
93+
{
94+
var formData = new MultipartFormDataContent();
95+
foreach (KeyValuePair<string,string> param in parameters)
96+
{
97+
formData.Add(new StringContent(param.Value), param.Key);
98+
}
99+
formData.Add(new ByteArrayContent(bytes), "file", parameters.First(p => p.Key == "filename").Value);
100+
101+
request.Content = formData;
102+
}
103+
104+
var response = await httpClient.SendAsync(request);
105+
var responseContent = await response.Content.ReadAsStringAsync();
106+
107+
if (!response.IsSuccessStatusCode)
108+
{
109+
logger?.Invoke($"{response.StatusCode} \n {responseContent}");
110+
}
111+
112+
response.EnsureSuccessStatusCode();
113+
114+
var resObj = JsonSerializer.Deserialize<T>(responseContent, JsonSerializerSettings);
115+
116+
if(!resObj.Ok)
117+
throw new WellKnownSlackApiException(error: $"{resObj.Error}", responseContent:responseContent);
118+
119+
return resObj;
120+
}
87121
}
88122

89123
internal class LowerCaseNaming : JsonNamingPolicy

source/src/Slackbot.Net.SlackClients.Http/ISlackClient.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using Slackbot.Net.SlackClients.Http.Models.Requests.ChatPostMessage;
2+
using Slackbot.Net.SlackClients.Http.Models.Requests.FileUpload;
23
using Slackbot.Net.SlackClients.Http.Models.Requests.ViewPublish;
34
using Slackbot.Net.SlackClients.Http.Models.Responses;
45
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatGetPermalink;
56
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatPostMessage;
67
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsList;
78
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsRepliesResponse;
9+
using Slackbot.Net.SlackClients.Http.Models.Responses.FileUpload;
810
using Slackbot.Net.SlackClients.Http.Models.Responses.UserProfile;
911
using Slackbot.Net.SlackClients.Http.Models.Responses.UsersList;
1012
using Slackbot.Net.SlackClients.Http.Models.Responses.ViewPublish;
@@ -88,4 +90,13 @@ public interface ISlackClient
8890
/// </summary>
8991
/// <remarks>https://api.slack.com/methods/users.profile.get</remarks>
9092
Task<UserProfileResponse> UserProfile(string user);
93+
94+
/// <summary>
95+
/// Scopes required: files.write
96+
/// Uploads a file
97+
/// </summary>
98+
/// <remarks>https://api.slack.com/methods/files.upload</remarks>
99+
Task<FileUploadResponse> FilesUpload(FileUploadRequest fileupload);
100+
101+
Task<FileUploadResponse> FilesUpload(FileUploadMultiPartRequest req);
91102
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Slackbot.Net.SlackClients.Http.Models.Requests.FileUpload;
2+
3+
public class FileUploadRequest
4+
{
5+
public string Channels { get; set; }
6+
public string Content { get; set; }
7+
public string Filename { get; set; }
8+
public string Filetype { get; set; }
9+
public string Initial_Comment { get; set; }
10+
public string Thread_Ts { get; set; }
11+
public string Title { get; set; }
12+
}
13+
14+
public class FileUploadMultiPartRequest
15+
{
16+
public string Channels { get; set; }
17+
public Byte[] File { get; set; }
18+
public string Filename { get; set; }
19+
public string Filetype { get; set; }
20+
public string Initial_Comment { get; set; }
21+
public string Thread_Ts { get; set; }
22+
public string Title { get; set; }
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace Slackbot.Net.SlackClients.Http.Models.Responses.FileUpload;
2+
3+
public class FileUploadResponse : Response
4+
{
5+
public FileUploadFile File { get; set; }
6+
}
7+
8+
public class FileUploadFile
9+
{
10+
public string Id { get; set; }
11+
public int Created { get; set; }
12+
public string Name { get; set; }
13+
public string Title { get; set; }
14+
public string FileType { get; set; }
15+
public string Pretty_Type { get; set; }
16+
public string User { get; set; }
17+
public bool Is_Public { get; set; }
18+
}

source/src/Slackbot.Net.SlackClients.Http/SlackClient.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using Microsoft.Extensions.Logging;
22
using Slackbot.Net.SlackClients.Http.Extensions;
33
using Slackbot.Net.SlackClients.Http.Models.Requests.ChatPostMessage;
4+
using Slackbot.Net.SlackClients.Http.Models.Requests.FileUpload;
45
using Slackbot.Net.SlackClients.Http.Models.Requests.ViewPublish;
56
using Slackbot.Net.SlackClients.Http.Models.Responses;
67
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatGetPermalink;
78
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatPostMessage;
89
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsList;
910
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsRepliesResponse;
11+
using Slackbot.Net.SlackClients.Http.Models.Responses.FileUpload;
1012
using Slackbot.Net.SlackClients.Http.Models.Responses.UserProfile;
1113
using Slackbot.Net.SlackClients.Http.Models.Responses.UsersList;
1214
using Slackbot.Net.SlackClients.Http.Models.Responses.ViewPublish;
@@ -141,4 +143,43 @@ public async Task<UserProfileResponse> UserProfile(string user)
141143
};
142144
return await _client.PostParametersAsForm<UserProfileResponse>(parameters,"users.profile.get", s => _logger.LogTrace(s));
143145
}
146+
147+
/// <inheritdoc/>
148+
public async Task<FileUploadResponse> FilesUpload(FileUploadRequest req)
149+
{
150+
var parameters = new List<KeyValuePair<string, string>>
151+
{
152+
new KeyValuePair<string, string>("channels", req.Channels),
153+
new KeyValuePair<string, string>("title", req.Title),
154+
new KeyValuePair<string, string>("content", req.Content),
155+
new KeyValuePair<string, string>("filename", req.Filename),
156+
new KeyValuePair<string, string>("filetype", req.Filetype),
157+
new KeyValuePair<string, string>("initial_comment", req.Initial_Comment),
158+
new KeyValuePair<string, string>("thread_ts", req.Thread_Ts),
159+
};
160+
return await _client.PostParametersAsForm<FileUploadResponse>(parameters, "files.upload", s => _logger.LogTrace(s));
161+
}
162+
163+
/// <inheritdoc/>
164+
public async Task<FileUploadResponse> FilesUpload(FileUploadMultiPartRequest req)
165+
{
166+
var parameters = new List<KeyValuePair<string, string>>
167+
{
168+
new KeyValuePair<string, string>("channels", req.Channels),
169+
new KeyValuePair<string, string>("title", req.Title),
170+
new KeyValuePair<string, string>("filename", req.Filename),
171+
};
172+
173+
if (req.Initial_Comment is { })
174+
{
175+
parameters.Add(new KeyValuePair<string, string>("initial_comment", req.Initial_Comment));
176+
}
177+
178+
if (req.Thread_Ts is { })
179+
{
180+
parameters.Add(new KeyValuePair<string, string>("thread_ts", req.Thread_Ts));
181+
}
182+
183+
return await _client.PostParametersAsMultiPartFormData<FileUploadResponse>(parameters, req.File, "files.upload", s => _logger.LogTrace(s));
184+
}
144185
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Slackbot.Net.SlackClients.Http.Models.Requests.FileUpload;
2+
using Slackbot.Net.Tests.Helpers;
3+
4+
namespace Slackbot.Net.Tests
5+
{
6+
public class FileUploadTests : Setup
7+
{
8+
public FileUploadTests(ITestOutputHelper helper) : base(helper)
9+
{
10+
}
11+
12+
[Fact]
13+
public async Task FilesUploadTests()
14+
{
15+
var response = await SlackClient.FilesUpload(new FileUploadRequest
16+
{
17+
Channels = $"{Channel}",
18+
Title = "Man in field",
19+
Initial_Comment = "My initial comment!",
20+
Content = "https://assets3.thrillist.com/v1/image/1682388/size/tl-horizontal_main.jpg",
21+
Filename = "heisann.jpg",
22+
Filetype = "jpg"
23+
});
24+
25+
Assert.True(response.Ok);
26+
}
27+
28+
[Fact]
29+
public async Task FilesUploadFileTests()
30+
{
31+
var bytes = Convert.FromBase64String(File.ReadAllText("./Helpers/ImageBase64Encoded.txt"));
32+
var response = await SlackClient.FilesUpload(new FileUploadMultiPartRequest
33+
{
34+
Channels = $"{Channel}",
35+
Title = "Man holding beer",
36+
File = bytes,
37+
Filename = "beer.png",
38+
Filetype = "png"
39+
});
40+
41+
Assert.True(response.Ok);
42+
}
43+
}
44+
}

source/test/Slackbot.Net.SlackClients.Http.Tests/Helpers/ImageBase64Encoded.txt

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

source/test/Slackbot.Net.SlackClients.Http.Tests/Slackbot.Net.SlackClients.Http.Tests.csproj

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<IsPackable>false</IsPackable>
66
<RootNamespace>Slackbot.Net.Tests</RootNamespace>
77
<ImplicitUsings>enable</ImplicitUsings>
8-
<LangVersion>latest</LangVersion>
8+
<LangVersion>Preview</LangVersion>
99
</PropertyGroup>
1010

1111
<ItemGroup>
@@ -25,5 +25,12 @@
2525
<Using Include="Xunit" />
2626
<Using Include="Xunit.Abstractions" />
2727
</ItemGroup>
28+
29+
<ItemGroup>
30+
<None Remove="Helpers\ImageBase64Encoded.txt" />
31+
<Content Include="Helpers\ImageBase64Encoded.txt">
32+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
33+
</Content>
34+
</ItemGroup>
2835

2936
</Project>

0 commit comments

Comments
 (0)