10

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 :)

Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114
Fabian Held
  • 373
  • 1
  • 6
  • 17
  • There are two kinds of webhooks. The standard one is fixed to a channel. check this post for details: https://stackoverflow.com/a/51467673/4379151 – Erik Kalkoken Nov 07 '18 at 15:30
  • So if you install and use the incoming webhook app from the app directory, you can use different channels and usernames. However, I would recommend to use the web API method`chat.postMessage` if you can. Its the only approach that solves your problem and works with a Slack app. – Erik Kalkoken Nov 07 '18 at 15:34
  • here is a c# example class for sending message via incoming webhook: https://gist.github.com/jogleasonjr/7121367 – Erik Kalkoken Nov 07 '18 at 15:58
  • The Github-example is out of date, it doesn't work like that anymore with slack. Just wnated to let you know. – Fabian Held Nov 08 '18 at 15:58

2 Answers2

10

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.

Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114
  • Wow, thank you very much! I wasn't expecting an answer so soon. And on top of that it's a really explicit one! I'll get to it and try it as soon as I find time. And then I'll obviously will give feedback on this. Thank you so much! – Fabian Held Nov 08 '18 at 08:28
  • Awesome man! I just tried your code - it worked flawlessly. You helped me so much. Now I'll try to figure out how to get this working for other users than my self. I'm still a little unclear on how the tokens work, since I only have one for the App. But if I get stuck again, I might post another question. Again, thank you so much - you made my day! – Fabian Held Nov 08 '18 at 09:37
  • Happy to help! If you want to send message on behalf of other users [this question](https://stackoverflow.com/a/42663007/4379151) might be helpful. – Erik Kalkoken Nov 08 '18 at 13:52
  • Now I also got the async approach working - thank god! :D Or better, thank you. I'll try to figure out how to attach files to the messages now... Thank you so far, have a nice weekend. – Fabian Held Nov 09 '18 at 13:48
  • Well done. Hint: You can only attach images to messages (in the attachments). Other files need to be uploaded via `files.upload` API method. – Erik Kalkoken Nov 09 '18 at 13:58
  • How do I get that `xoxp-YOUR-TOKEN`? This is a user token, not an "App" API token, right? https://api.slack.com/authentication/token-types talks about "User Tokens", but doesn't specify how to get them. https://stackoverflow.com/questions/53129239 talks about App tokens, not User tokens... – lmat - Reinstate Monica Jan 26 '23 at 18:47
8

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();

        }
    }

}
Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114