A Scala library for interacting with the Slack API and real time messaging interface
Add SBT dependency:
libraryDependencies += "com.github.slack-scala-client" %% "slack-scala-client" % "1.0.1"
<dependency>
<groupId>com.github.slack-scala-client</groupId>
<artifactId>slack-scala-client_${scala.version}</artifactId>
<version>1.0.1</version>
</dependency>
There are two different API clients, one exposing an asynchronous interface and the other exposing a synchronous interface. They can be imported from the slack.api
package:
import slack.api.SlackApiClient // Async
import slack.api.BlockingSlackApiClient // Blocking
Creating an instance of either client simply requires passing in a Slack api token:
val token = "<Your Token Here>"
val client = SlackApiClient(token)
Calling any api functions requires an implicit ActorSystem
... one can be created simply:
implicit val system: ActorSystem = ActorSystem("slack")
The async client returns futures as the result of each of its API functions:
val client = SlackApiClient(token)
val res = client.listChannels() // => Future[Seq[Channel]]
res.onComplete {
case Success(channels) => //...
case Failure(err) => // ...
}
...while the blocking client will block the current thread until the API response has been received:
val client = BlockingSlackApiClient(token) // Default timeout of 5 seconds
val channels = client.listChannels() // => Seq[Channel]
The API clients implement the full Slack API. A full list of the available endpoints can be found directly on the classes: SlackApiClient and BlockingSlackApiClient
The real time messaging client is implemented using Pekko and requires having an implicit ActorSystem
in scope. Either an ActorSystem
or ActorContext
will work:
import slack.rtm.SlackRtmClient
import org.apache.pekko.actor.ActorSystem
implicit val system: ActorSystem = ActorSystem("slack")
Creating an instance of the RTM client requires an API token, just like the API clients:
val token = "<Your Token Here>"
val client = SlackRtmClient(token)
Based on the stream of events coming in, the client maintains active state that contains things like channels and users. It can also be used to look up the ID of a user or channel by name:
val state = client.state
val selfId = state.self.id
val chanId = state.getChannelIdForName("general") // => Option[String]
Sending a message is pretty simple:
val generalChanId = state.getChannelIdForName("general").get
client.sendMessage(generalChanId, "Hello!")
Messages can be received very simply as well:
client.onMessage { message =>
println(s"User: ${message.user}, Message: ${message.text}")
}
Additionally, the client can be used to receive any event sent from Slack:
client.onEvent {
case e: Message => ...
case e: UserTyping => ...
case e: ChannelDeleted => ...
}
A full list of events can be found in Events.scala. One thing to note is the two above functions return an ActorRef
which is a handle to the underlying actor running the above handler function. This can be used to terminate the handler by terminating the actor: system.stop(handler)
, or unregistering it as a listener: client.removeEventListener(handler)
A Pekko actor can be manually registered as an event listener and all events will be sent to that actor:
val actor = system.actorOf(Props[SlackEventHandler])
client.addEventListener(actor)
// Time Passes...
client.removeEventListener(actor)
Finally, an RTM client can easily be terminated and cleaned up by calling close:
client.close()
This is a full implementation of a Slack bot that will listen for anyone mentioning it in a message and will respond to that user.
val token = "..."
implicit val system: ActorSystem = ActorSystem("slack")
implicit val ec: ExecutionContextExecutor = system.dispatcher
val client = SlackRtmClient(token)
val selfId = client.state.self.id
client.onMessage { message =>
val mentionedIds = SlackUtil.extractMentionedIds(message.text)
if(mentionedIds.contains(selfId)) {
client.sendMessage(message.channel, s"<@${message.user}>: Hey!")
}
}
- The Slack API contains a lot methods and not every implemented API method has been executed (i.e. some may not work; pull requests accepted!)
- Responses to RTM messages sent out are not currently checked to verify they were successfully received
- Investigate a way to ensure all missed messages are received during a disconnection
- A small number of response types have yet to be fleshed out
Changelog can be found here