5 min read

In Part 1 of this series, I introduced you to SlackKit and Zewo, which allows us to build and deploy a Slack bot written in Swift to a Linux server. Here in Part 2, we will finish the app, showing all of the Swift code. We will also show how to get an API token, how to test the app and deploy it on Heroku, and finally how to launch it.

Show Me the Swift Code!

Finally, some Swift code! To create our bot, we need to edit our main.swift file to contain our bot logic:

import String
importSlackKit

class Leaderboard: MessageEventsDelegate {

    // A dictionary to hold our leaderboard
var leaderboard: [String: Int] = [String: Int]()
letatSet = CharacterSet(characters: ["@"])
    // A SlackKit client instance
let client: SlackClient

    // Initalize the leaderboard with a valid Slack API token
init(token: String) {
client = SlackClient(apiToken: token)
client.messageEventsDelegate = self
    }

    // Enum to hold commands the bot knows
enum Command: String {
case Leaderboard = "leaderboard"
    }

    // Enum to hold logic that triggers certain bot behaviors
enum Trigger: String {
casePlusPlus = "++"
caseMinusMinus = "--"
    }

    // MARK: MessageEventsDelegate
    // Listen to the messages that are coming in over the Slack RTM connection
funcmessageReceived(message: Message) {
listen(message: message)
    }

funcmessageSent(message: Message){}
funcmessageChanged(message: Message){}
funcmessageDeleted(message: Message?){}

    // MARK: Leaderboard Internal Logic
privatefunc listen(message: Message) {
        // If a message contains our bots user ID and a recognized command, handle that command
if let id = client.authenticatedUser?.id, text = message.text {
iftext.lowercased().contains(query: Command.Leaderboard.rawValue) &&text.contains(query: id) {
handleCommand(command: .Leaderboard, channel: message.channel)
            }
        }
        // If a message contains a trigger value, handle that trigger
ifmessage.text?.contains(query: Trigger.PlusPlus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .PlusPlus)
        }
ifmessage.text?.contains(query: Trigger.MinusMinus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .MinusMinus)
        }
    }

    // Text parsing can be messy when you don't have Foundation...
privatefunchandleMessageWithTrigger(message: Message, trigger: Trigger) {
if let text = message.text,
start = text.index(of: "@"),
end = text.index(of: trigger.rawValue) {
let string = String(text.characters[start...end].dropLast().dropFirst())
let users = client.users.values.filter{$0.id == self.userID(string: string)}
            // If the receiver of the trigger is a user, use their user ID
ifusers.count> 0 {
letidString = userID(string: string)
initalizationForValue(dictionary: &leaderboard, value: idString)
scoringForValue(dictionary: &leaderboard, value: idString, trigger: trigger)
            // Otherwise just store the receiver value as is
            } else {
initalizationForValue(dictionary: &leaderboard, value: string)
scoringForValue(dictionary: &leaderboard, value: string, trigger: trigger)
            }
        }
    }

    // Handle recognized commands
privatefunchandleCommand(command: Command, channel:String?) {
switch command {
case .Leaderboard:
            // Send message to the channel with the leaderboard attached
if let id = channel {
client.webAPI.sendMessage(channel:id, text: "Leaderboard", linkNames: true, attachments: [constructLeaderboardAttachment()], success: {(response) in

                    }, failure: { (error) in
print("Leaderboard failed to post due to error:(error)")
                })
            }
        }
    }

privatefuncinitalizationForValue(dictionary: inout [String: Int], value: String) {
if dictionary[value] == nil {
dictionary[value] = 0
        }
    }

privatefuncscoringForValue(dictionary: inout [String: Int], value: String, trigger: Trigger) {
switch trigger {
case .PlusPlus:
dictionary[value]?+=1
case .MinusMinus:
dictionary[value]?-=1
        }
    }

    // MARK: Leaderboard Interface
privatefuncconstructLeaderboardAttachment() -> Attachment? {
let

Great! But we’ll need to replace the dummy API token with the real deal before anything will work.

Getting an API Token

We need to create a bot integration in Slack. You’ll need a Slack instance that you have administrator access to. If you don’t already have one of those to play with, go sign up. Slack is free for small teams:

  1. Create a new bot here.
  2. Enter a name for your bot. I’m going to use “leaderbot”.
  3. Click on “Add Bot Integration”.
  4. Copy the API token that Slack generates and replace the placeholder token at the bottom of main.swift with it.

Testing 1,2,3…

Now that we have our API token, we’re ready to do some local testing. Back in Xcode, select the leaderbot command-line application target and run your bot (⌘+R). When we go and look at Slack, our leaderbot’s activity indicator should show that it’s online. It’s alive! To ensure that it’s working, we should give our helpful little bot some karma points:

@leaderbot++

And ask it to see the leaderboard:

@leaderbot leaderboard

Head in the Clouds

Now that we’ve verified that our leaderboard bot works locally, it’s time to deploy it. We are deploying on Heroku, so if you don’t have an account, go and sign up for a free one.

First, we need to add a Procfile for Heroku. Back in the terminal, run:

echo slackbot: .build/debug/leaderbot > Procfile

Next, let’s check in our code:

git init
git add .
git commit -am’leaderbot powering up’

Finally, we’ll setup Heroku:

  1. Install the Heroku toolbelt.
  2. Log in to Heroku in your terminal:
         heroku login
  3. Create our application on Heroku and set our buildpack:
     	heroku create --buildpack https://github.com/pvzig/heroku-buildpack-swift.git leaderbot 
  4. Set up our Heroku remote:
     	heroku git:remote -a leaderbot 
  5. Push to master:
     	git push heroku master

Once you push to master, you’ll see Heroku going through the process of building your application.

Launch!

When the build is complete, all that’s left to do is to run our bot:

heroku run:detached slackbot

Like when we tested locally, our bot should become active and respond to our commands!

You’re Done!

Congratulations, you’ve successfully built and deployed a Slack bot written in Swift onto a Linux server!

Built With:

Jay: Pure-Swift JSON parser and formatterkylef’s Heroku buildpack for Swift

Open Swift: Open source cross project standards for Swift

SlackKit: A Slack client library

Zewo: Open source libraries for modern server software

Disclaimer

The linux version of SlackKit should be considered an alpha release. It’s a fun tech demo to show what’s possible with Swift on the server, not something to be relied upon. Feel free to report issues you come across.

About the author

Peter Zignego is an iOS developer in Durham, North Carolina, USA. He writes at bytesized.co, tweets at @pvzig, and freelances at Launch Software.

LEAVE A REPLY

Please enter your comment!
Please enter your name here