Documentation ¶
Overview ¶
Package slackscot provides the building blocks to create a slack bot.
It is easily extendable via plugins that can combine commands, hear actions (listeners) as well as scheduled actions. It also supports updating of triggered responses on message updates as well as deleting triggered responses when the triggering messages are deleted by users.
Plugins also have access to services injected on startup by slackscot such as:
- UserInfoFinder: To query user info
- SLogger: To log debug/info statements
- EmojiReactor: To emoji react to messages
- FileUploader: To upload files
- RealTimeMessageSender: To send unmanaged real time messages outside the normal reaction flow (i.e. for sending many messages or sending via a scheduled action)
Example code (from https://github.com/alexandre-normand/youppi):
package main import ( "github.com/alexandre-normand/slackscot" "github.com/alexandre-normand/slackscot/config" "github.com/alexandre-normand/slackscot/plugins" "gopkg.in/alecthomas/kingpin.v2" "io" ) func main() { // TODO: Parse command-line, initialize viper and instantiate Storer implementation for needed for some plugins youppi, err := slackscot.NewBot("youppi", v, options...). WithPlugin(plugins.NewKarma(karmaStorer)). WithPlugin(plugins.NewTriggerer(triggererStorer)). WithConfigurablePluginErr(plugins.FingerQuoterPluginName, func(conf *config.PluginConfig) (p *slackscot.Plugin, err) { return plugins.NewFingerQuoter(c) }). WithConfigurablePluginCloserErr(plugins.EmojiBannerPluginName, func(conf *config.PluginConfig) (c io.Closer, p *slackscot.Plugin, err) { return plugins.NewEmojiBannerMaker(c) }). WithConfigurablePluginErr(plugins.OhMondayPluginName, func(conf *config.PluginConfig) (p *slackscot.Plugin, err) { return plugins.NewOhMonday(c) }). WithPlugin(plugins.NewVersionner(name, version)). Build() defer youppi.Close() if err != nil { log.Fatal(err) } err = youppi.Run() if err != nil { log.Fatal(err) } }
Index ¶
- Constants
- func ApplyAnswerOpts(opts ...AnswerOption) (sendOptions map[string]string)
- func NewSLogger(log *log.Logger, debug bool) (l *sLogger)
- type ActionDefinition
- type ActionDefinitionWithID
- type Answer
- type AnswerOption
- type Answerer
- type Builder
- func (sb *Builder) Build() (s *Slackscot, err error)
- func (sb *Builder) WithConfigurablePluginCloserErr(name string, newInstance CloserPluginInstantiator) *Builder
- func (sb *Builder) WithConfigurablePluginErr(name string, newInstance PluginInstantiator) *Builder
- func (sb *Builder) WithPlugin(p *Plugin) *Builder
- func (sb *Builder) WithPluginCloserErr(closer io.Closer, p *Plugin, err error) *Builder
- func (sb *Builder) WithPluginErr(p *Plugin, err error) *Builder
- type CloserPluginInstantiator
- type DefaultFileUploader
- type EmojiReactor
- type FileUploader
- type IncomingMessage
- type Matcher
- type Option
- type OutgoingMessage
- type Plugin
- type PluginInstantiator
- type RealTimeMessageSender
- type SLogger
- type ScheduledAction
- type ScheduledActionDefinition
- type SlackFileUploader
- type SlackMessageID
- type Slackscot
- type UploadOption
- type UserInfoFinder
Constants ¶
const ( // ThreadedReplyOpt is the name of the option indicating a threaded-reply answer ThreadedReplyOpt = "threadedReply" // BroadcastOpt is the name of the option indicating a broadcast answer BroadcastOpt = "broadcast" // ThreadTimestamp is the name of the option indicating the explicit timestamp of the thread to reply to ThreadTimestamp = "threadTimestamp" // EphemeralAnswerToOpt marks an answer to be sent as an ephemeral message to the provided userID EphemeralAnswerToOpt = "ephemeralMsgToUserID" )
const (
// VERSION represents the current slackscot version
VERSION = "1.37.0"
)
GENERATED and MANAGED by giddyup (https://github.com/alexandre-normand/giddyup)
Variables ¶
This section is empty.
Functions ¶
func ApplyAnswerOpts ¶
func ApplyAnswerOpts(opts ...AnswerOption) (sendOptions map[string]string)
ApplyAnswerOpts applies answering options to build the send configuration
func NewSLogger ¶
NewSLogger creates a new Slackscot logger provided with an interface logger and a debug flag
Types ¶
type ActionDefinition ¶
type ActionDefinition struct { // Indicates whether the action should be omitted from the help message Hidden bool // Matcher that will determine whether or not the action should be triggered Match Matcher // Usage example Usage string // Help description for the action Description string // Function to execute if the Matcher matches Answer Answerer }
ActionDefinition represents how an action is triggered, published, used and described along with defining the function defining its behavior
type ActionDefinitionWithID ¶
type ActionDefinitionWithID struct { ActionDefinition // contains filtered or unexported fields }
ActionDefinitionWithID holds an action definition along with its identifier string
type Answer ¶
type Answer struct { Text string // Options to apply when sending a message Options []AnswerOption // BlockKit content blocks to apply when sending the message ContentBlocks []slack.Block }
Answer holds data of an Action's Answer: namely, its text and options to use when delivering it
type AnswerOption ¶
AnswerOption defines a function applied to Answers
func AnswerEphemeral ¶
func AnswerEphemeral(userID string) AnswerOption
AnswerEphemeral sends the answer as an ephemeral message to the provided userID
func AnswerInExistingThread ¶
func AnswerInExistingThread(threadTimestamp string) AnswerOption
AnswerInExistingThread sets threaded replying with the existing thread timestamp
func AnswerInThreadWithBroadcast ¶
func AnswerInThreadWithBroadcast() AnswerOption
AnswerInThreadWithBroadcast sets threaded replying with broadcast enabled
func AnswerInThreadWithoutBroadcast ¶
func AnswerInThreadWithoutBroadcast() AnswerOption
AnswerInThreadWithoutBroadcast sets threaded replying with broadcast disabled
func AnswerWithoutThreading ¶
func AnswerWithoutThreading() AnswerOption
AnswerWithoutThreading sets an answer to threading (and implicitly, broadcast) disabled
type Answerer ¶
type Answerer func(m *IncomingMessage) *Answer
Answerer is what gets executed when an ActionDefinition is triggered. To signal the absence of an answer, an action should return nil
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder holds a slackscot instance to build
func (*Builder) Build ¶
Build returns the built slackscot instance. If there was an error during setup, the error is returned along with a nil slackscot
func (*Builder) WithConfigurablePluginCloserErr ¶
func (sb *Builder) WithConfigurablePluginCloserErr(name string, newInstance CloserPluginInstantiator) *Builder
WithConfigurablePluginCloserErr adds a closer plugin to the slackscot instance by first checking and getting its configuration
func (*Builder) WithConfigurablePluginErr ¶
func (sb *Builder) WithConfigurablePluginErr(name string, newInstance PluginInstantiator) *Builder
WithConfigurablePluginErr adds a plugin to the slackscot instance by first checking and getting its configuration
func (*Builder) WithPlugin ¶
WithPlugin adds a plugin to the slackscot instance
func (*Builder) WithPluginCloserErr ¶
WithPluginCloserErr adds a plugin that has a creation function returning (io.Closer, Plugin, error) to the slackscot instance
type CloserPluginInstantiator ¶
CloserPluginInstantiator creates a new instance of a closer plugin given a PluginConfig
type DefaultFileUploader ¶
type DefaultFileUploader struct {
// contains filtered or unexported fields
}
DefaultFileUploader holds a bare-bone SlackFileUploader
func NewFileUploader ¶
func NewFileUploader(slackFileUploader SlackFileUploader) (fileUploader *DefaultFileUploader)
NewFileUploader returns a new DefaultFileUploader wrapping a FileUploader
func (*DefaultFileUploader) UploadFile ¶
func (fileUploader *DefaultFileUploader) UploadFile(params slack.FileUploadParameters, options ...UploadOption) (file *slack.File, err error)
UploadFile uploads a file given the slack.FileUploadParameters with the UploadOptions applied to it
type EmojiReactor ¶
type EmojiReactor interface { // AddReaction adds an emoji reaction to a ItemRef using the emoji associated // with the given name (i.e. name should be thumbsup rather than :thumbsup:) AddReaction(name string, item slack.ItemRef) error }
EmojiReactor is implemented by any value that has the AddReaction method. The main purpose is a slight decoupling of the slack.Client in order for plugins to be able to write cleaner tests more easily
type FileUploader ¶
type FileUploader interface { // UploadFile uploads a file to slack. For more info in this API, check // https://godoc.org/github.com/nlopes/slack#Client.UploadFile UploadFile(params slack.FileUploadParameters, options ...UploadOption) (file *slack.File, err error) }
FileUploader is implemented by any value that has the UploadFile method. slack.Client *almost* implements it but requires a thin wrapping to do so to handle UploadOption there for added extensibility. The main purpose remains is a slight decoupling of the slack.Client in order for plugins to be able to write cleaner tests more easily.
type IncomingMessage ¶
type IncomingMessage struct { // The original slack.Msg text stripped from the "<@Mention>" prefix, if applicable NormalizedText string slack.Msg }
IncomingMessage holds data for an incoming slack message. In addition to a slack.Msg, it also has a normalized text that is the original text stripped from the "<@Mention>" prefix when a message is addressed to a slackscot instance. Since commands are usually received either via direct message (without @Mention) or on channels with @Mention, the normalized text is useful there to allow plugins to have a single version to do Match and Answer against
type Matcher ¶
type Matcher func(m *IncomingMessage) bool
Matcher is the function that determines whether or not an action should be triggered based on a IncomingMessage (which includes a slack.Msg and a normalized text content. Note that a match doesn't guarantee that the action should actually respond with anything once invoked
type Option ¶
type Option func(*Slackscot)
Option defines an option for a Slackscot
func OptionLogfile ¶
OptionLogfile sets a logfile for Slackscot while using the other default logging prefix and options
func OptionNoPluginNamespacing ¶
func OptionNoPluginNamespacing() Option
OptionNoPluginNamespacing disables plugin command namespacing for this instance. This means that namespacing plugin candidates will run without any extra plugin name matching required This is useful to simplify command usage for instances running a single plugin
func OptionTestMode ¶
OptionTestMode sets the instance in test mode which instructs it to react to a goodbye event to terminate its execution. It is meant to be used for testing only and mostly in conjunction with github.com/nlopes/slack/slacktest. Very importantly, the termination message must be formed correctly so that the slackscot instance terminates correctly for tests to actually terminate.
Here's an example:
testServer := slacktest.NewTestServer() testServer.Handle("/channels.create", slacktest.Websocket(func(conn *websocket.Conn) { // Trigger a termination on any API call to channels.create slacktest.RTMServerSendGoodbye(conn) })) testServer.Start() defer testServer.Stop() termination := make(chan bool) s, err := New("BobbyTables", config.NewViperWithDefaults(), OptionWithSlackOption(slack.OptionAPIURL(testServer.GetAPIURL())), OptionTestMode(termination)) require.NoError(t, err) tp := newTestPlugin() s.RegisterPlugin(tp) go s.Run() // TODO: Use the testserver to send events and messages and assert your plugin's behavior // Send this event to the testServer's websocket. This gets transformed into a // slack.DisconnectedEvent with Cause equal to slack.ErrRTMGoodbye that slackscot will // interpret as a signal to self-terminate testServer.SendToWebsocket("{\"type\":\"goodbye\"}") // Wait for slackscot to terminate <-termination
func OptionWithSlackOption ¶
OptionWithSlackOption adds a slack.Option to apply on the slack client
type OutgoingMessage ¶
type OutgoingMessage struct { *slack.OutgoingMessage // Answer from plugins/internal commands *Answer // contains filtered or unexported fields }
OutgoingMessage holds a plugin generated slack outgoing message along with the plugin identifier
type Plugin ¶
type Plugin struct { Name string NamespaceCommands bool // Set to true for slackscot-managed namespacing of commands where the namespace/prefix to all commands is set to the plugin name Commands []ActionDefinition HearActions []ActionDefinition ScheduledActions []ScheduledActionDefinition // Those slackscot services are injected post-creation when slackscot is called. // A plugin shouldn't rely on those being available during creation UserInfoFinder UserInfoFinder Logger SLogger EmojiReactor EmojiReactor FileUploader FileUploader RealTimeMsgSender RealTimeMessageSender // The slack.Client is injected post-creation. It gives access to all the https://godoc.org/github.com/nlopes/slack#Client. // Plugin writers might want to check out https://godoc.org/github.com/nlopes/slack/slacktest to create a slack test server in order // to mock a slack server to test plugins using the SlackClient. SlackClient *slack.Client }
Plugin represents a plugin (its name, action definitions and slackscot injected services)
Set NamespaceCommands to true if the plugin's commands are to be namespaced by slackscot. This means that commands will be first checked for a prefix that matches the plugin name. Since that's all handled by slackscot, a plugin should be written with matching only considering what comes after the namespace. For example, a plugin with name make would have a coffee command be something like
Match: func(m *IncomingMessage) bool { return strings.HasPrefix(m.NormalizedText, "coffee ") }, Usage: "coffee `<when>`", Description: "Make coffee", Answer: func(m *IncomingMessage) *Answer { when := strings.TrimPrefix(m.NormalizedText, "coffee ") return &Answer{Text: fmt.Sprintf("coffee will be reading %s", when))}} }
In this example, if namespacing is enabled, a user would trigger the command with a message such as:
<@slackscotID> make coffee in 10 minutes
Note that the plugin itself doesn't need to concern itself with the namespace in the matching or answering as the NormalizedText has been formatted to be stripped of namespacing whether or not that's enabled and slackscot will have made sure the namespace matched if enabled.
At runtime, instances of slackscot can request to disregard namespacing with OptionNoPluginNamespacing (for example, to run a single plugin and simplify usage).
type PluginInstantiator ¶
type PluginInstantiator func(c *config.PluginConfig) (p *Plugin, err error)
PluginInstantiator creates a new instance of a plugin given a PluginConfig
type RealTimeMessageSender ¶
type RealTimeMessageSender interface { // NewOutgoingMessage is the function that creates a new message to send. See https://godoc.org/github.com/nlopes/slack#RTM.NewOutgoingMessage for more details NewOutgoingMessage(text string, channelID string, options ...slack.RTMsgOption) *slack.OutgoingMessage // SendMessage is the function that sends a new real time message. See https://godoc.org/github.com/nlopes/slack#RTM.SendMessage for more details SendMessage(outMsg *slack.OutgoingMessage) }
RealTimeMessageSender is implemented by any value that has the NewOutgoingMessage method. The main purpose is a slight decoupling of the slack.RTM in order for plugins to be able to write tests more easily if all they do is send new messages on a channel
type SLogger ¶
type SLogger interface { Printf(format string, v ...interface{}) Debugf(format string, v ...interface{}) }
SLogger is the slackscot internal logging interface. The standard library logger implements this interface
type ScheduledAction ¶
type ScheduledAction func()
ScheduledAction is what gets executed when a ScheduledActionDefinition is triggered (by its ScheduleDefinition) In order to do anything, a plugin should define its scheduled actions functions with itself as a receiver so the function has access to the injected services
type ScheduledActionDefinition ¶
type ScheduledActionDefinition struct { // Indicates whether the action should be omitted from the help message Hidden bool // Schedule definition determining when the action runs Schedule schedule.Definition // Help description for the scheduled action Description string // ScheduledAction is the function that is invoked when the schedule activates Action ScheduledAction }
ScheduledActionDefinition represents when a scheduled action is triggered as well as what it does and how
type SlackFileUploader ¶
type SlackFileUploader interface { // UploadFile uploads a file to slack. For more info in this API, check // https://godoc.org/github.com/nlopes/slack#Client.UploadFile UploadFile(params slack.FileUploadParameters) (file *slack.File, err error) }
SlackFileUploader is implemented by any value that has the UploadFile method. slack.Client implements it. The main purpose remains is a slight decoupling of the slack.Client in order for plugins to be able to write cleaner tests more easily.
type SlackMessageID ¶
type SlackMessageID struct {
// contains filtered or unexported fields
}
SlackMessageID holds the elements that form a unique message identifier for slack. Technically, slack also uses the workspace id as the first part of that unique identifier but since an instance of slackscot only lives within a single workspace, that part is left out
func (SlackMessageID) IsMsgModifiable ¶
func (sid SlackMessageID) IsMsgModifiable() bool
IsMsgModifiable returns true if this slack message id can be used to update/delete the message. In practice, ephemeral messages don't have a channel ID and can't be deleted/updated so this would be a case where IsMsgModifiable would return false
type Slackscot ¶
type Slackscot struct {
// contains filtered or unexported fields
}
Slackscot represents what defines a Slack Mascot (mostly, a name and its plugins)
func (*Slackscot) Close ¶
Close closes all closers of this slackscot. The first error that occurs during a Close is returned but regardless, all closers are attempted to be closed
func (*Slackscot) RegisterPlugin ¶
RegisterPlugin registers a plugin with the Slackscot engine. This should be invoked prior to calling Run
type UploadOption ¶
type UploadOption func(params *slack.FileUploadParameters)
UploadOption defines an option on a FileUploadParameters (i.e. upload on thread)
func UploadInThreadOption ¶
func UploadInThreadOption(m *IncomingMessage) UploadOption
UploadInThreadOption sets the file upload thread timestamp to an existing thread timestamp if the incoming message triggering this is on an existing thread
type UserInfoFinder ¶
UserInfoFinder defines the interface for finding a slack user's info
func NewCachingUserInfoFinder ¶
func NewCachingUserInfoFinder(v *viper.Viper, loader UserInfoFinder, logger SLogger) (uf UserInfoFinder, err error)
NewCachingUserInfoFinder creates a new user info service with caching if enabled via userProfileCacheSizeKey. It requires an implementation of the interface that will do the actual loading when not in cache
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package actions provides a fluent API for creating slackscot plugin actions.
|
Package actions provides a fluent API for creating slackscot plugin actions. |
Package config provides some utilities and structs to access configuration loaded via Viper
|
Package config provides some utilities and structs to access configuration loaded via Viper |
Package plugin provides a fluent API for creating slackscot plugins.
|
Package plugin provides a fluent API for creating slackscot plugins. |
Package plugins provides a collection of example (and usable) plugins for instances of slackscot Package plugins provides a collection of example (and usable) plugins for instances of slackscot
|
Package plugins provides a collection of example (and usable) plugins for instances of slackscot Package plugins provides a collection of example (and usable) plugins for instances of slackscot |
Package schedule defines the interface for scheduling of slackscot actions
|
Package schedule defines the interface for scheduling of slackscot actions |
Package store provides a simple and convenient data store interface for plugins to persist data along with a default filed-based leveldb implementation.
|
Package store provides a simple and convenient data store interface for plugins to persist data along with a default filed-based leveldb implementation. |
datastoredb
Package datastoredb provides an implementation of github.com/alexandre-normand/slackscot/store's StringStorer interface backed by the Google Cloud Datastore.
|
Package datastoredb provides an implementation of github.com/alexandre-normand/slackscot/store's StringStorer interface backed by the Google Cloud Datastore. |
inmemorydb
Package inmemorydb provides an implementation of github.com/alexandre-normand/slackscot/store's StringStorer interface as an in-memory data store relying on a wrapping StringStorer for actual persistence.
|
Package inmemorydb provides an implementation of github.com/alexandre-normand/slackscot/store's StringStorer interface as an in-memory data store relying on a wrapping StringStorer for actual persistence. |
mocks
Package mocks contains a mock of the store package interfaces
|
Package mocks contains a mock of the store package interfaces |
Package test provides testing utilities for users of slackscot to help writing tests for their plugins and extensions
|
Package test provides testing utilities for users of slackscot to help writing tests for their plugins and extensions |
assertanswer
Package assertanswer provides testing functions to validate a plugin's answer This package is most useful when used in combination with github.com/alexandre-normand/slackscot/test/assertplugin but can be used alone to test individual slackscot Actions.
|
Package assertanswer provides testing functions to validate a plugin's answer This package is most useful when used in combination with github.com/alexandre-normand/slackscot/test/assertplugin but can be used alone to test individual slackscot Actions. |
assertplugin
Package assertplugin provides testing functions to validate a plugin's overall functionality.
|
Package assertplugin provides testing functions to validate a plugin's overall functionality. |
capture
Package capture provides dummy implementations of some of the plugin services that capture data fed into them in order to allow validation in tests
|
Package capture provides dummy implementations of some of the plugin services that capture data fed into them in order to allow validation in tests |