-
Notifications
You must be signed in to change notification settings - Fork 0
Plugins #3 Slack API
At this point, you should have learned the basic types of handlers you can create as a plugin. However, handlers may not be that interesting if you can't get information from Slack, or post something to Slack. So here, we will give an overview of how to use the Slack APIs. First we will discuss the conveniences that Slacksoc affords us, and then introduce you to the true source of power, the underlying Slack API.
A big theme in this document is the concept of "blocking" - that simply means that the program is waiting for some result (like a Slack API response). Blocking is bad for a bot, because when a plugin blocks waiting for something, it delays the bot from handling other events and executing other plugins. So don't block the main thread!
As we've already seen, Slacksoc provides some utilities to make replying and reacting to messages easy. They are:
You can use these without any worries about blocking the bot.
When constructing mentions, you should be aware of a couple things. First, the Slack API refers to users, channels, and most other things by an ID. The ID is an alphanumeric string that may look something like this: C09B4TCCQ
. The API communicates only in terms of IDs, and messages themselves contain IDs rather than actual usernames.
So when you @mention someone, your message actually looks like this: <@U09B4TCCQ>: yo!
. When you write a channel name, your message looks more like this: check out <#C09B4TCCQ>
. If you want your bot to be able to construct and/or recognize these things, you may want some help.
First of all, given an ID, you can immediately tell if it corresponds to a Channel, User, DM, Group, or File by its first letter. To make this readable in code, use the functions Is(DM|Channel|User|Group|DM|File)
.
Next, if you want to create a mention, use these handy bot methods, none of which block:
-
bot.Mention
,bot.MentionI
,bot.MentionN
: these take a User, ID, or Name, and return a string containing an @mention -
bot.SayChannelI
,bot.SayChannelN
: these take a channel ID or name and return a string containing a linked #channel mention. -
bot.SpecialMention
: takes a string (either "channel", "here", "group", or "everyone") and creates a special @mention string out of it.
To parse a user mention into the ID it contains, use this function rather than trying to do it yourself:
-
ParseUserMention
- note that it is part of the library, not a method of the bot
See more documentation on message formatting here.
Before turning to the API, some of the answers you're looking for (about users and channels, at least) may be available from the bot already. When Slacksoc connects to the bot, it receives a ton of info about the users and channels on the team. It saves that info, and keeps it up to date as it changes on the team. You can ask the bot for this information using the following functions, none of which block:
bot.GetUserByID()
bot.GetUserByName()
bot.GetUsers()
bot.GetChannelByID()
bot.GetChannelByName()
bot.GetChannels()
In particular, the User
objects returned have lots of juicy details:
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Deleted bool `json:"deleted"`
Color string `json:"color"`
RealName string `json:"real_name"`
TZ string `json:"tz,omitempty"`
TZLabel string `json:"tz_label"`
TZOffset int `json:"tz_offset"`
Profile UserProfile `json:"profile"`
IsBot bool `json:"is_bot"`
IsAdmin bool `json:"is_admin"`
IsOwner bool `json:"is_owner"`
IsPrimaryOwner bool `json:"is_primary_owner"`
IsRestricted bool `json:"is_restricted"`
IsUltraRestricted bool `json:"is_ultra_restricted"`
Has2FA bool `json:"has_2fa"`
HasFiles bool `json:"has_files"`
Presence string `json:"presence"`
}
UserProfile
struct, included in Profile
field of User
type UserProfile struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
RealName string `json:"real_name"`
RealNameNormalized string `json:"real_name_normalized"`
Email string `json:"email"`
Skype string `json:"skype"`
Phone string `json:"phone"`
Image24 string `json:"image_24"`
Image32 string `json:"image_32"`
Image48 string `json:"image_48"`
Image72 string `json:"image_72"`
Image192 string `json:"image_192"`
ImageOriginal string `json:"image_original"`
Title string `json:"title"`
BotID string `json:"bot_id,omitempty"`
ApiAppID string `json:"api_app_id,omitempty"`
}
Channels do not have nearly as much interesting information associated with them. However, you can get info (like lists of users in the channel) from the API. Details on that are coming right up.
So you want to do something that hasn't been discussed yet? Well, for everything else, there's MasterCard.
Just kidding. For everything else, there's the API
object.
The Bot contains another struct member named API
. This is an authenticated Slack Web API client, which can be used to do all sorts of goodies - just check out the methods listed on the GoDoc!
However, every method of API blocks, and so you need to take care to avoid blocking the main thread!
The nice and easy way to do this is to make your whole handler run in a goroutine. See the concurrency section for information about how to do this - it's not hard! The TL;DR is:
func (l *lov) Foobar(bot *lib.Bot, evt *slack.MessageEvent) error {
go func() {
// do everything in here
}
return nil
}
The Bot contains a struct member named RTM
. This object, maintained by the Slack library, doesn't have a ton that is useful to plugin developers, but it exists. The RTM connection is how messages are posted (don't use the API object for that, unless you have a good reason). Interestingly, there is actually a separate goroutine already handling the connection, and sending a message simply hands it off to that thread. This is why bot.Reply
does not block!