How to post a message via Slack-App from c#, as a user not App, with attachment, in a specific channel

10,699

Solution 1

1. Incoming webhook vs. chat.postMessage

The incoming webhook of a Slack app is always fixed to a channel. There is legacy variant of the Incoming Webhook that supports overriding the channel, but that has to be installed separately and will not be part of your Slack app. (see also this answer).

So for your case you want to use the web API method chat.postMessage instead.

2. Example implementation

Here is a very basic example implementation for sending a message with chat.postMessage in C#. It will work for different channels and messages will be send as the user who owns the token (same that installed the app), not the app.

using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;

public class SlackExample
{    
    public static void SendMessageToSlack()
    {        
        var data = new NameValueCollection();
        data["token"] = "xoxp-YOUR-TOKEN";
        data["channel"] = "blueberry";        
        data["as_user"] = "true";           // to send this message as the user who owns the token, false by default
        data["text"] = "test message 2";
        data["attachments"] = "[{\"fallback\":\"dummy\", \"text\":\"this is an attachment\"}]";

        var client = new WebClient();
        var response = client.UploadValues("https://slack.com/api/chat.postMessage", "POST", data);
        string responseInString = Encoding.UTF8.GetString(response);
        Console.WriteLine(responseInString);        
    }

    public static void Main()
    {
        SendMessageToSlack();
    }
}

This is a very rudimentary implementation using synchronous calls. Check out this question for how to use the more advanced asynchronous approach.

Solution 2

Here is an improved example on how send a Slack message with attachments.

This example is using the better async approach for sending requests and the message incl. attachments is constructed from C# objects.

Also, the request is send as modern JSON Body POST, which requires the TOKEN to be set in the header.

Note: Requires Json.Net

using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace SlackExample
{
    class SendMessageExample
    {
        private static readonly HttpClient client = new HttpClient();

        // reponse from message methods
        public class SlackMessageResponse
        {
            public bool ok { get; set; }
            public string error { get; set; }
            public string channel { get; set; }
            public string ts { get; set; }
        }

        // a slack message
        public class SlackMessage
        {
            public string channel{ get; set; }
            public string text { get; set; }
            public bool as_user { get; set; }
            public SlackAttachment[] attachments { get; set; }
        }

        // a slack message attachment
        public class SlackAttachment
        {
            public string fallback { get; set; }
            public string text { get; set; }
            public string image_url { get; set; }
            public string color { get; set; }
        }

        // sends a slack message asynchronous
        // throws exception if message can not be sent
        public static async Task SendMessageAsync(string token, SlackMessage msg)
        {
            // serialize method parameters to JSON
            var content = JsonConvert.SerializeObject(msg);
            var httpContent = new StringContent(
                content,
                Encoding.UTF8,
                "application/json"
            );

            // set token in authorization header
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // send message to API
            var response = await client.PostAsync("https://slack.com/api/chat.postMessage", httpContent);

            // fetch response from API
            var responseJson = await response.Content.ReadAsStringAsync();

            // convert JSON response to object
            SlackMessageResponse messageResponse =
                JsonConvert.DeserializeObject<SlackMessageResponse>(responseJson);

            // throw exception if sending failed
            if (messageResponse.ok == false)
            {
                throw new Exception(
                    "failed to send message. error: " + messageResponse.error
                );
            }
        }

        static void Main(string[] args)
        {           
            var msg = new SlackMessage
            {
                channel = "test",
                text = "Hi there!",
                as_user = true,
                attachments = new SlackAttachment[] 
                {
                    new SlackAttachment
                    {
                        fallback = "this did not work",
                        text = "This is attachment 1",
                        color = "good"
                    },

                    new SlackAttachment
                    {
                        fallback = "this did not work",
                        text = "This is attachment 2",
                        color = "danger"
                    }
                }
            };

            SendMessageAsync(
                "xoxp-YOUR-TOKEN",                
                msg
            ).Wait();

            Console.WriteLine("Message has been sent");
            Console.ReadKey();

        }
    }

}
Share:
10,699
Fabian Held
Author by

Fabian Held

I am a former draftsman, 33 years of age, studied civil engineering(didn't finish) and now am becoming a programmer. I am a father, gamer, husband, and try my hardest to become a good programmer/engineer. At this stage I'm all about asking questions - not answering them ;)

Updated on June 14, 2022

Comments

  • Fabian Held
    Fabian Held almost 2 years

    I can not for the life of me post a message to another channel than the one I webhooked. And I can not do it as myself(under my slackID), just as the App.

    Problem is I have to do this for my company, so we can integrate slack to our own Software. I just do not understand what the "payload" for JSON has to look like (actually I'm doing it exactly like it says on Slack's Website, but it doesn't work at all - it always ignores things like "token", "user", "channel" etc.).

    I also do not understand how to use the url-methods like "https://slack.com/api/chat.postMessage" - where do they go? As you might see in my code I only have the webhookurl, and if I don't use that one I can not post to anything. Also I do not understand how to use arguments like a token, specific userId, a specific channel... - if I try to put them in the Payload they are just ignored, it seems.

    Okay, enough whining! I'll show you now what I got so far. This is from someone who posted this online! But I changed and added a few things:

    public static void Main(string[] args)
            {
                Task.WaitAll(IntegrateWithSlackAsync());
            }
    
            private static async Task IntegrateWithSlackAsync()
            {
                var webhookUrl = new Uri("https://hooks.slack.com/services/TmyHook");  
                var slackClient = new SlackClient(webhookUrl);
                while (true)
                {
                    Console.Write("Type a message: ");
                    var message = Console.ReadLine();
                    Payload testMessage = new Payload(message);
                    var response = await slackClient.SendMessageAsync(testMessage);
                    var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
                    Console.WriteLine($"Received {isValid} response.");
                    Console.WriteLine(response);
                }
            }
        }
    }
    
    public class SlackClient
    {
            private readonly Uri _webhookUrl;
            private readonly HttpClient _httpClient = new HttpClient {};
    
            public SlackClient(Uri webhookUrl)
            {
                _webhookUrl = webhookUrl;
            }
    
            public async Task<HttpResponseMessage> SendMessageAsync(Payload payload)
            {
    
                var serializedPayload = JsonConvert.SerializeObject(payload);
    
                var stringCont = new StringContent(serializedPayload, Encoding.UTF8, "application/x-www-form-urlencoded");
    
                var response = await _httpClient.PostAsync(_webhookUrl, stringCont);
    
                return response;
            }
        }
    }
    

    I made this class so I can handle the Payload as an Object:

        public class Payload
    {
        public string token = "my token stands here";
        public string user = "my userID";
        public string channel = "channelID";
        public string text = null;
    
        public bool as_user = true;
    
    
        public Payload(string message)
        {
            text = message;
        }
    }
    

    I would be so appreciative for anyone who could post a complete bit of code that really shows how I would have to handle the payload. And/or what the actual URL would look like that gets send to slack... so I maybe can understand what the hell is going on :)