Post to Slack from Godot

Posted on Sun 17 July 2022 in Tech • 2 min read

Recently I've been working on a new game in Godot called Cosmic Trading Crew. The game has a central server that the manages the logins for "captains" and allows other clients to join the same ship. I wanted to be notified any time a new captain was registered. My current personal workflow uses Slack.

In the past, I've had a Godot app call an AWS Lambda function instead of calling the Slack Webhook directly. This allowed me the flexibility to change out the end target without worrying about someone running an older version of the Godot app and pointing to the old target. For example, if I wanted to use a different Slack channel or a different chat app entirely but I wasn't using the Lambda function as an intermediate call, I'd have to deploy a new version of the Godot app and maintain the old chat target until all users had upgraded.

Since the new game server is entirely managed by me, I can call the Slack webhook directly and update it in the future without worrying about old clients. This makes it less complex while still giving me future flexibility.

Slack Setup

I already have a Slack workspace, so I created a new channel specifically for Cosmic Trading Crew. To do this, I clicked the "plus" icon next to "Channels" in the sidebar, then chose "Create a channel".

Then, I went to the custom integrations section of my workspace. This was in a browser at https://<YOUR_SLACK_DOMAIN>/apps/manage/custom-integrations. Here, I clicked on Incoming Webhooks, then chose the green "Add To Slack" button on the left.

In the "Post to channel" section, I chose my new channel that I created, then clicked on "Add Incoming Webhooks Integration". This creates the webhook and tells you the Webhook URL. I left everything else as the defaults. The Webhook URL looks like https://hooks.slack.com/services/XX1234XXX/YYYYYGW123B/ZZdI0ZI5mdZwu4AsZbo9gvZZ

Godot setup

In my server project in Godot, there is a base scene that does nothing. I added a child node to this scene of type "HTTPRequest".

Then, in the script attached to the base scene, I added this function:

func post_to_slack(message):
    var url = "https://hooks.slack.com/services/<YOUR_URL_HERE>"
    var data = {
        'text': message,
        'username': "CTC-Server",
        'icon_emoji': ':rocket:'
    }
    var query = JSON.print(data)
    var headers = ["Content-Type: application/json"]
    $HTTPRequest.request(url, headers, true, HTTPClient.METHOD_POST, query)

Now, any time I want to send a message to slack, I can make a call using post_to_slack("New captain signup") or something similar.

One optional step I took was to enable my global GDscript to call this function as well using signals. To do this, I created a signal in the script that is set in the autoload configuration and called that signal from there:

signal post_to_slack(message)

...

emit_signal("post_to_slack", "New captain created: "+str(captain_name))

To catch the signal in my scene script, I used this in the _ready() function:

func _ready():
    g.connect("post_to_slack", self, "post_to_slack")

While debugging this, I also set up the HTTPRequest node to connect the request_completed() signal to this function, but this is optional:

func _on_HTTPRequest_request_completed(result, response_code, headers, body):
    print("Response from Slack: "+str(result)+" "+str(response_code)+" "+str(body))