Search code examples
githubwebhooksslack-apislackpayload

slack doesn't recognize github webhook payload format


I'm trying to create a slack app that uses incoming webhooks. I want my github repository to post to slack whenever the wiki is updated. I believe I've set up the webhook on github just fine, because I can see that it is attempting a delivery whenever I update the wiki. However, there's always the error, "no_text". I think this error means slack is expecting an item named "text," but the payload from github provides none. I verified this by trying two curl commands from the command prompt (I'm on windows):

curl -X POST -H "Content-type: application/json" --data "{\"text\":\"Hello, World!\"}" [MY_WEBHOOK_URL]
curl -X POST -H "Content-type: application/json" --data "{\"foobar\":\"Hello, World!\"}" [MY_WEBHOOK_URL]

This first one works as expected; the message "Hello, World!" gets posted to the slack channel I wanted, and I got back the "ok" message from curl. The second one did not work; the message was not posted, and I got back the message "no_text" from curl.

I can think of two possible solutions to this problem:

  1. Change the format of the payload coming from github to include an item called "text" and other properties slack actually recognizes.
  2. Get slack to recognize the format the payload is already in, perhaps by telling it to post the contents of a property other than "text."

I don't know how to accomplish either of these, or if they're even possible. Or perhaps there's another solution I haven't thought of?

Note: I already tried to use the github slack app, but couldn't figure out how to get it to post updates to the wiki. (See my other question if you'd like: slack github integration doesn't find wiki repository)


Solution

  • I'm actually looking to do the same thing as you right now. Because the github and slack hooks are fundamentally different, you will need to have something in the middle to process the github webhooks into a Slack message to be posted via an incoming webhook.

    You're going to need to do a couple different things (in no particular order):

    1. Set up Github to send out hooks for the specific events you wish to be notified of.
    2. Configure a middle man (I am currently using AWS SNS and Lambda)
    3. Set up slack for the webhook.

    For the github webhooks, you will need to leverage the more powerful github API to create the hook. You could do this with curl, but that's kind of a pain so I am using a JS script to take care of it. You will need to npm install github bluebird in the same directory before running something like this:

    var GitHubApi = require("github");
    
    var github = new GitHubApi({
        // optional
        debug: true,
        protocol: "https",
        host: "api.github.com", // should be api.github.com for GitHub
        pathPrefix: "", // for some GHEs; none for GitHub
        headers: {
            "user-agent": "ocelotsloth-conf" // GitHub is happy with a unique user agent
        },
        Promise: require('bluebird'),
        followRedirects: false, // default: true; there's currently an issue with non-get redirects, so allow ability to disable follow-redirects
        timeout: 5000
    });
    
    // user token
    github.authenticate({
        type: "token",
        token: "GITHUB_TOKEN_HERE",
    });
    
    // https://mikedeboer.github.io/node-github/#api-repos-createHook
    github.repos.createHook({
      owner: "ocelotsloth",
      repo: "lib-ical",
      name: "amazonsns",
      events: [
        //"commit_comment",
        //"create",
        //"delete",
        //"gollum",
        //"issue_comment",
        "issues"
        //"label",
        //"milestone",
        //"pull_request",
        //"pull_request_review",
        //"pull_request_review_comment",
        //"push",
        //"release"
      ],
      config: {
        aws_key: "AWS_KEY",
        aws_secret: "AWS_SECRET",
        sns_region: "us-east-1",
        sns_topic: "SNS_TOPIC_ARN"
      },
    
    }, function(err, res) {
        console.log(JSON.stringify(res, null, '\t'));
    });
    

    I remember following a blog post a while ago about setting up the SNS topic to work properly, but I don't remember exactly where it is anymore. Some googling should help. Also, you should be able to set up your own server for github to send these to and avoid having to set up AWS at all if you want to avoid the complexity. See https://mikedeboer.github.io/node-github/#api-repos-createHook for specific instructions on that method. You will need to use editHook after you create the hook, so either get it right the first time or use edit it. You just need to change the method call to editHook and add the id to the call as well.

    Something important to see, you can define all of the different Events that you want github to send to you. For all of these, along with their formats, look at https://developer.github.com/v3/activity/events/types/.

    To actually post these events to slack, I have a lambda script that currently looks like this (I literally just started writing this today, and haven't implemented more than just posting issue events, but it should do well as a starting point). For this script, you will need to npm install identify-github-event slack-webhook and have your incoming webhook set up as well.

    var identifyGithubEvent = require('identify-github-event');
    var SlackWebhook = require('slack-webhook')
    
    // slack's link syntax
    function link(url, txt) {
    return "<" + url + "|" + txt + ">";
    }
    
    exports.handler = function(event, context) {
    // 1. extract GitHub event from SNS message
    var ghEvent = JSON.parse(event.Records[0].Sns.Message);
    var eventType, eventName, numb;
    console.log(ghEvent);
    
    var ghEventType = identifyGithubEvent(ghEvent);
    
    if (!ghEventType) {
      return;
    }
    
    var text = "Event!  " + ghEventType;
    
    if (ghEventType === 'IssueCommentEvent') {
      var who = link(ghEvent.comment.user.html_url, ghEvent.comment.user.login);
      var what = link(ghEvent.issue.html_url, "Issue " + ghEvent.issue.number + ": \"" + ghEvent.issue.title + "\"");
      text = who + " commented on " + what;
    }
    else if (ghEventType === 'IssuesEvent') {
        var who = link(ghEvent.sender.html_url, ghEvent.sender.login);
        var action = ghEvent.action;
        var issueNumber = ghEvent.issue.number;
        var issueName = link(ghEvent.issue.html_url, ghEvent.issue.title + "\"");
    
        if (action === "opened" | action === "closed") {
            text = {
                attachments: [{
                  "fallback": who + " opened Issue" + issueNumber + ": " + issueName,
                  "color": "#36a64f",
                  "pretext": "New issue " + action + ":",
                  "author_name": ghEvent.sender.login,
                  "author_link": ghEvent.sender.html_url,
                  "thumb_url": ghEvent.sender.avatar_url,
                  "title": "#" + issueNumber + ": " + ghEvent.issue.title,
                  "title_link": ghEvent.issue.html_url,
                  "text": ghEvent.issue.body,
                  "fields": [
                    {
                      "title": "Status",
                      "value": ghEvent.issue.state,
                      "short": true
                    },
                    {
                      "title": "Labels",
                      "value": ghEvent.issue.labels.map(label => label.name).join("\n"),
                      "short": true
                    }
                  ],
                  "footer": "lib-ical",
                  "footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png",
                  "mrkdwn_in": ["text"]
                }]
            };
        } else return;
    }
    
      // 'commit_comment':
      // 'create':
      // 'delete':
      // 'issues':
      // 'label':
      // 'member':
      // 'milestone':
      // 'pull_request':
      // 'pull_request_review':
      // 'pull_request_review_comment':
      // 'push':
      // 'release':
    
    var slack = new SlackWebhook('https://hooks.slack.com/services/SLACK-WEBHOOK-URL', {
      defaults: {
        username: 'GitHub -- user/project',
        channel: '#CHANNEL-NAME',
        icon_emoji: ':github:'
      }
    })
    
    slack.send(text);
    
    };
    

    It's far from perfect, but it gives a really nice result:

    Example hook

    For that specific example it's an issue close, but currently that script will also work on open. The script also does limited markdown processing, so if the issue contains any source blocks, it will be rendered properly inside of slack.

    I hope this helps you with your approach, feel free to ask me to elaborate on anything else.