Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions source/src/Slackbot.Net.SlackClients.Http/ISlackClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatGetPermalink;
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatPostMessage;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsHistoryResponse;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsInfo;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsList;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsMembers;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsRepliesResponse;
using Slackbot.Net.SlackClients.Http.Models.Responses.FileUpload;
using Slackbot.Net.SlackClients.Http.Models.Responses.UserProfile;
Expand Down Expand Up @@ -75,12 +77,18 @@ public interface ISlackClient

/// <summary>
/// Scopes required: channels:read | groups:read | im:read
/// Only requests `public_channel` types of conversations
/// Returns the member user ids of a conversation.
/// </summary>
/// <remarks>https://api.slack.com/methods/conversations.members</remarks>
Task<ConversationsListResponse> ConversationsMembers(string channel);


Task<ConversationsMembersResponse> ConversationsMembers(string channel);

/// <summary>
/// Scopes required: channels:read | groups:read | im:read | mpim:read
/// </summary>
/// <remarks>https://api.slack.com/methods/conversations.info</remarks>
Task<ConversationsInfoResponse> ConversationsInfo(string channel, bool includeNumMembers = false);


/// <summary>
/// Scopes required: channels:history/groups:history/im:history or mpim:history
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsInfo;

public class ConversationsInfoResponse : Response
{
public ConversationInfo Channel { get; set; }
}

public class ConversationInfo
{
public string Id { get; set; }
public string Name { get; set; }
public bool Is_Channel { get; set; }
public bool Is_Archived { get; set; }

// Only populated when include_num_members=true is passed.
public int? Num_Members { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsList;

namespace Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsMembers;

public class ConversationsMembersResponse : Response
{
public IEnumerable<string> Members { get; set; }

public ResponseMetadata Response_Metadata { get; set; }
}
17 changes: 15 additions & 2 deletions source/src/Slackbot.Net.SlackClients.Http/SlackClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatGetPermalink;
using Slackbot.Net.SlackClients.Http.Models.Responses.ChatPostMessage;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsHistoryResponse;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsInfo;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsList;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsMembers;
using Slackbot.Net.SlackClients.Http.Models.Responses.ConversationsRepliesResponse;
using Slackbot.Net.SlackClients.Http.Models.Responses.FileUpload;
using Slackbot.Net.SlackClients.Http.Models.Responses.UserProfile;
Expand Down Expand Up @@ -113,13 +115,24 @@ public async Task<ConversationsListResponse> ConversationsListPublicChannels(int
}

/// <inheritdoc/>
public async Task<ConversationsListResponse> ConversationsMembers(string channel)
public async Task<ConversationsMembersResponse> ConversationsMembers(string channel)
{
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("channel", channel)
};
return await _client.PostParametersAsForm<ConversationsListResponse>(parameters, "conversations.members", s => _logger.LogTrace(s));
return await _client.PostParametersAsForm<ConversationsMembersResponse>(parameters, "conversations.members", s => _logger.LogTrace(s));
}

/// <inheritdoc/>
public async Task<ConversationsInfoResponse> ConversationsInfo(string channel, bool includeNumMembers = false)
{
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("channel", channel),
new KeyValuePair<string, string>("include_num_members", includeNumMembers ? "true" : "false")
};
return await _client.PostParametersAsForm<ConversationsInfoResponse>(parameters, "conversations.info", s => _logger.LogTrace(s));
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Microsoft.Extensions.Logging;
using Slackbot.Net.SlackClients.Http;
using Slackbot.Net.Tests.Helpers;

namespace Slackbot.Net.Tests;

// Self-contained: deserializes realistic Slack payloads through the real SlackClient
// path (no network, no credentials).
public class ConversationsInfoAndMembersDeserializationTests(ITestOutputHelper helper)
{
private ISlackClient ClientReturning(string json) =>
new SlackClient(
new HttpClient(new StubHttpMessageHandler(json)) { BaseAddress = new Uri("https://slack.com/api/") },
new XUnitLogger<ISlackClient>(helper));

// Based on https://docs.slack.dev/reference/methods/conversations.info
[Fact]
public async Task ConversationsInfo_PopulatesNumMembers_WhenRequested()
{
var json = """
{
"ok": true,
"channel": {
"id": "C012AB3CD",
"name": "general",
"is_channel": true,
"is_archived": false,
"num_members": 6
}
}
""";
var response = await ClientReturning(json).ConversationsInfo("C012AB3CD", includeNumMembers: true);

Assert.True(response.Ok);
Assert.Equal("C012AB3CD", response.Channel.Id);
Assert.Equal("general", response.Channel.Name);
Assert.True(response.Channel.Is_Channel);
Assert.False(response.Channel.Is_Archived);
Assert.Equal(6, response.Channel.Num_Members);
}

// num_members is only present when include_num_members=true was passed on the request.
[Fact]
public async Task ConversationsInfo_NumMembersIsNull_WhenNotRequested()
{
var json = """
{ "ok": true, "channel": { "id": "C012AB3CD", "name": "general", "is_channel": true, "is_archived": false } }
""";
var response = await ClientReturning(json).ConversationsInfo("C012AB3CD");

Assert.True(response.Ok);
Assert.Null(response.Channel.Num_Members);
}

// Based on https://docs.slack.dev/reference/methods/conversations.members - a flat list of
// member user ids, not a list of Conversation objects.
[Fact]
public async Task ConversationsMembers_PopulatesMemberIds_AndPagination()
{
var json = """
{
"ok": true,
"members": ["U023BECGF", "U061F7AUR", "W012A3CDE"],
"response_metadata": { "next_cursor": "dXNlcjpVMDYxTkZUVDI=" }
}
""";
var response = await ClientReturning(json).ConversationsMembers("C012AB3CD");

Assert.True(response.Ok);
Assert.Equal(["U023BECGF", "U061F7AUR", "W012A3CDE"], response.Members);
Assert.Equal("dXNlcjpVMDYxTkZUVDI=", response.Response_Metadata.Next_Cursor);
}
}
Loading