bot

package
v0.9.0-pre2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 25, 2017 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package bot provides the interfaces for creating a chatops bot.

Index

Constants

This section is empty.

Variables

View Source
var Version = "0.9.0-pre2"

Version is the current version of Gopherbot

Functions

func Log

func Log(l LogLevel, v ...interface{})

Log logs messages whenever the connector log level is less than the given level

func RegisterConnector

func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector)

RegisterConnector should be called in an init function to register a type of connector. Currently only Slack is implemented.

func RegisterElevator

func RegisterElevator(name string, provider func(Handler) Elevate)

RegisterElevator allows elevate methods to register themselves.

func RegisterPlugin

func RegisterPlugin(name string, plug PluginHandler)

RegisterPlugin allows plugins to register a PluginHandler in a func init(). When the bot initializes, it will call each plugin's handler with a command "init", empty channel, the bot's username, and no arguments, so the plugin can store this information for, e.g., scheduled jobs.

func RegisterSimpleBrain

func RegisterSimpleBrain(name string, provider func(Handler, *log.Logger) SimpleBrain)

RegisterSimpleBrain allows brain implementations to register a function with a named brain type that returns an SimpleBrain interface. This can only be called from a brain provider's init() function(s). Pass in a Logger so the brain can log it's own error messages if needed.

func Start

func Start()

Start gets the robot going

Types

type AttrRet

type AttrRet struct {
	Attribute string
	RetVal
}

AttrRet implements Stringer so it can be interpolated with fmt if the plugin author is ok with ignoring the RetVal.

func (*AttrRet) String

func (bar *AttrRet) String() string

type Connector

type Connector interface {
	// GetProtocolUserAttribute retrieves a piece of information about a user
	// from the connector protocol, or "",!ok if the connector doesn't have the
	// information. Plugins should normally call GetUserAttribute, which
	// supplements protocol data with data from users.json.
	// The current attributes are:
	// email, realName, firstName, lastName, phone, sms, connections
	GetProtocolUserAttribute(user, attr string) (value string, ret RetVal)
	// JoinChannel joins a channel given it's human-readable name, e.g. "general"
	JoinChannel(c string) RetVal
	// SendProtocolChannelMessage sends a message to a channel
	SendProtocolChannelMessage(channelname, msg string, format MessageFormat) RetVal
	// SendProtocolUserChannelMessage directs a message to a user in a channel
	SendProtocolUserChannelMessage(user, channelname, msg string, format MessageFormat) RetVal
	// SendProtocolUserMessage sends a direct message to a user if supported.
	// For protocols not supportint DM, the bot should send a message addressed
	// to the user in an implementation-specific channel.
	SendProtocolUserMessage(user, msg string, format MessageFormat) RetVal
	// The Run method starts the main loop, and never returns; if it's
	// called a second time, it just returns.
	Run(stopchannel chan struct{})
}

Connector is the interface defining methods that should be provided by the connector for use by plugins/robot.

type Elevate

type Elevate func(r *Robot, alwayscheck bool) bool

Elevate is a configurable plugin function for elevating privileges. It take a Robot and bool indicating whether to always check regardless of any timeouts in effect. It returns a bool indicating success or failure.

type Handler

type Handler interface {
	// IncomingMessage is called by the connector for all messages the bot
	// can hear. The channelName and userName should be human-readable,
	// not internal representations. If channelName is blank, it's a direct message
	IncomingMessage(channelName, userName, message string)
	// GetProtocolConfig unmarshals the ProtocolConfig section of gopherbot.json
	// into a connector-provided struct
	GetProtocolConfig(interface{}) error
	// GetBrainConfig unmarshals the BrainConfig section of gopherbot.json
	// into a struct provided by the brain provider
	GetBrainConfig(interface{}) error
	// GetElevateConfig unmarshals the ElevateConfig section of gopherbot.json
	// into a struct provided by the elevate provider
	GetElevateConfig(interface{}) error
	// SetFullName allows the connector to set the robot's full name if it
	// has access to it.
	SetFullName(n string)
	// SetName allows the connect to set the robot's name that it should be addressed
	// by, if it has access to it.
	SetName(n string)
	// GetLogLevel allows the connector to check the robot's configured log level
	// to make it's own decision about how much it should log. For slack, this
	// determines whether the plugin does api logging.
	GetLogLevel() LogLevel
	// GetInstallPath returns the installation path of the gopherbot
	GetInstallPath() string
	// GetLocalPath returns the path to the local config of the gopherbot
	GetLocalPath() string
	// Log provides a standard logging interface with a level as defined in
	// bot/logging.go
	Log(l LogLevel, v ...interface{})
}

Handler is the interface that defines the callback API for Connectors

type InputMatcher

type InputMatcher struct {
	Regex    string   // The regular expression string to match - bot adds ^\w* & \w*$
	Command  string   // The name of the command to pass to the plugin with it's arguments
	Label    string   // ReplyMatchers use "Label" instead of "Command"
	Contexts []string // label the contexts corresponding to capture groups, for supporting "it" & optional args
	// contains filtered or unexported fields
}

InputMatcher specifies the command or message to match and what to pass to the plugin

type LogLevel

type LogLevel int

LogLevel for determining when to output a log entry

const (
	Trace LogLevel = iota
	Debug
	Info
	Warn
	Error
	Fatal
)

Definitions of log levels in order from most to least verbose

type Logger

type Logger interface {
	Log(l LogLevel, v ...interface{})
}

Logger is used by a Brain for logging errors

type MessageFormat

type MessageFormat int

MessageFormat indicates how the connector should display the content of the message. One of Variable, Fixed or Raw (To Be Implemented)

const (
	Variable MessageFormat = iota // variable font width
	Fixed
	Raw
)

Outgoing message format, Variable or Fixed

type Plugin

type Plugin struct {
	Disabled                 bool            // Set true to disable the plugin
	AllowDirect              bool            // Set this true if this plugin can be accessed via direct message
	DirectOnly               bool            // Set this true if this plugin ONLY accepts direct messages
	Channels                 []string        // Channels where the plugin is active - rifraf like "memes" should probably only be in random, but it's configurable. If empty uses DefaultChannels
	AllChannels              bool            // If the Channels list is empty and AllChannels is true, the plugin should be active in all the channels the bot is in
	RequireAdmin             bool            // Set to only allow administrators to access a plugin
	ElevatedCommands         []string        // Commands that require elevation, usually via 2fa
	ElevateImmediateCommands []string        // Commands that always require elevation promting, regardless of timeouts
	Users                    []string        // If non-empty, list of all the users with access to this plugin
	Help                     []PluginHelp    // All the keyword sets / help texts for this plugin
	CommandMatchers          []InputMatcher  // Input matchers for messages that need to be directed to the 'bot
	ReplyMatchers            []InputMatcher  // Input matchers for replies to questions, only match after a RequestContinuation
	MessageMatchers          []InputMatcher  // Input matchers for messages the 'bot hears even when it's not being spoken to
	CatchAll                 bool            // Whenever the robot is spoken to, but no plugin matches, plugins with CatchAll=true get called with command="catchall" and argument=<full text of message to robot>
	Config                   json.RawMessage // Arbitrary Plugin configuration, will be stored and provided in a thread-safe manner via GetPluginConfig()
	// contains filtered or unexported fields
}

Plugin specifies the structure of a plugin configuration - plugins should include an example / default config

type PluginHandler

type PluginHandler struct {
	DefaultConfig string /* A yaml-formatted multiline string defining the default Plugin configuration. It should be liberally commented for use in generating
	local/custom configuration for the plugin. If a Config: section is defined, it should match the structure of the optional Config interface{} */
	Handler func(bot *Robot, command string, args ...string) // The callback function called by the robot whenever a Command is matched
	Config  interface{}                                      // An optional empty struct defining custom configuration for the plugin
}

PluginHandler is the struct a plugin registers for the Gopherbot plugin API.

type PluginHelp

type PluginHelp struct {
	Keywords []string // match words for 'help XXX'
	Helptext []string // help string to give for the keywords, conventionally starting with (bot) for commands or (hear) when the bot needn't be addressed directly
}

PluginHelp specifies keywords and help text for the 'bot help system

type RetVal

type RetVal int

RetVal is a bit field for returning error conditions, or 0 for Ok

const (
	// Ok indicates a successful result
	Ok RetVal = iota // success

	// UserNotFound - failed lookup
	UserNotFound
	// ChannelNotFound - failed lookup
	ChannelNotFound
	// AttributeNotFound - failed looking up user/robot attributes like email, name, etc.
	AttributeNotFound
	// FailedUserDM - the bot was not able to send an direct message (DM) to a given user
	FailedUserDM
	// FailedChannelJoin - the robot couldn't join a channel; e.g. slack doesn't allow bots to join
	FailedChannelJoin

	// DatumNotFound - key not found in the global hash when update called
	DatumNotFound
	// DatumLockExpired - A datum was checked out for too long, and the lock expired
	DatumLockExpired
	// DataFormatError - Problem unmarshalling JSON
	DataFormatError
	// BrainFailed - An error condition prevented the brain from storing/retrieving; redis down, file write failed, etc.
	BrainFailed
	// InvalidDatumKey - Key name didn't match the regex for valid key names
	InvalidDatumKey

	// InvalidDblPtr - GetPluginConfig wasn't called with a double-pointer to a config struct
	InvalidDblPtr
	// InvalidCfgStruct - The struct type in GetPluginConfig doesn't match the struct registered for the plugin
	InvalidCfgStruct
	// NoConfigFound - The plugin doesn't have any config data
	NoConfigFound

	/* Elevator */
	// TechnicalProblem - There was a problem with the elevator (e.g. service unreachable)
	TechnicalProblem

	// GeneralError - Only indicates an error occurred, vs. e.g. failure
	GeneralError

	// ReplyNotMatched - The user reply didn't match the pattern waited for
	ReplyNotMatched
	// UseDefaultValue - The user replied with a single '=', meaning use a default value
	UseDefaultValue
	// TimeoutExpired - The user didn't reply within the given timeout
	TimeoutExpired
	// Interrupted - The user issued another command instead of replying
	Interrupted
	// MatcherNotFound - There was no matcher configured with the given string, or the regex didn't compile
	MatcherNotFound

	// NoUserEmail - Couldn't look up the user's email address
	NoUserEmail
	// NoBotEmail - Couldn't look up the robot's email address
	NoBotEmail
	// MailError - There was an error sending email
	MailError
)

func (RetVal) String

func (ret RetVal) String() string

type Robot

type Robot struct {
	User    string        // The user who sent the message; this can be modified for replying to an arbitrary user
	Channel string        // The channel where the message was received, or "" for a direct message. This can be modified to send a message to an arbitrary channel.
	Format  MessageFormat // The outgoing message format, one of Fixed or Variable
	// contains filtered or unexported fields
}

Robot is passed to the plugin to enable convenience functions Say and Reply

func (*Robot) CheckAdmin

func (r *Robot) CheckAdmin() bool

CheckAdmin returns true if the user is a configured administrator of the robot. Should be used sparingly, when a single plugin has multiple commands, some which require admin. Otherwise the plugin should just configure RequireAdmin: true

func (*Robot) CheckinDatum

func (r *Robot) CheckinDatum(key, locktoken string)

CheckinDatum unlocks a datum without updating it, it always succeeds

func (*Robot) CheckoutDatum

func (r *Robot) CheckoutDatum(key string, datum interface{}, rw bool) (locktoken string, exists bool, ret RetVal)

CheckoutDatum gets a datum from the robot's brain and unmarshals it into a struct. If rw is set, the datum is checked out read-write and a non-empty lock token is returned that expires after lockTimeout (250ms). The bool return indicates whether the datum exists.

func (*Robot) Direct

func (r *Robot) Direct() *Robot

Direct is a convenience function for initiating a DM conversation with a user. Created initially so a plugin could prompt for a password in a DM.

func (*Robot) Elevate

func (r *Robot) Elevate(immediate bool) bool

Elevate lets a plugin request elevation on the fly. When immediate = true, the elevator should always prompt for 2fa; otherwise a configured timeout should apply.

func (*Robot) Email

func (r *Robot) Email(subject string, messageBody *bytes.Buffer) (ret RetVal)

Email provides a simple interface for sending the user an email from the robot.. It relies on both the robot and the user having an email address. For the robot, this can be conifigured in gopherbot.conf, Email attribute. For the user, this should be provided by the chat protocol, or in gopherbot.conf. (TODO: not yet implemented) It returns an error and RetVal != 0 if there's a problem.

func (*Robot) Fixed

func (r *Robot) Fixed() *Robot

Fixed is a convenience function for sending a message with fixed width font. e.g. r.Reply(xxx) replies in variable width font, but r.Fixed().Reply(xxx) replies in a fixed-width font.

func (*Robot) GetBotAttribute

func (r *Robot) GetBotAttribute(a string) *AttrRet

GetBotAttribute returns an attribute of the robot or "" if unknown. Current attributes: name, alias, fullName, contact

func (*Robot) GetPluginConfig

func (r *Robot) GetPluginConfig(dptr interface{}) RetVal

GetPluginConfig sets a struct pointer to point to a config struct populated from configuration when plugins were loaded. To use, a plugin should define a struct for it's configuration data, e.g.:

type pConf struct {
	Username, Password string
}

In conf/plugins/<pluginname>.yaml, you would add a Config: stanza, e.g.:

Config:
  Username: foo
  Password: bar

When registering the plugin, you pass a pointer to an empty config template, which the robot will use to populate a struct when configuration is loaded:

func init() {
	bot.RegisterPlugin("memes", bot.PluginHandler{
		DefaultConfig: defaultConfig, // yaml string providing default configuration
		Handler:       plugfunc, // callback function
		Config:        &pConf{}, // pointer to empty config struct
	})
}

Then, to get a current copy of configuration when the plugin runs, define a struct pointer and call GetPluginConfig with a double-pointer:

var c *pConf
r.GetPluginConfig(&c)

... And voila! *pConf is populated with the contents from the configured Config: stanza

func (*Robot) GetSenderAttribute

func (r *Robot) GetSenderAttribute(a string) *AttrRet

GetSenderAttribute returns a AttrRet with - The string Attribute of the sender, or "" if unknown/error - A RetVal which is one of Ok, UserNotFound, AttributeNotFound Current attributes: name(handle), fullName, email, firstName, lastName, phone TODO: supplement data with gopherbot.json user's table

func (*Robot) GetUserAttribute

func (r *Robot) GetUserAttribute(u, a string) *AttrRet

GetUserAttribute returns a AttrRet with - The string Attribute of a user, or "" if unknown/error - A RetVal which is one of Ok, UserNotFound, AttributeNotFound Current attributes: name(handle), fullName, email, firstName, lastName, phone TODO: supplement data with gopherbot.json user's table

func (*Robot) Log

func (r *Robot) Log(l LogLevel, v ...interface{})

Log logs a message to the robot's log file (or stderr) if the level is lower than or equal to the robot's current log level

func (*Robot) Pause

func (r *Robot) Pause(s float64)

Pause is a convenience function to pause some fractional number of seconds.

func (*Robot) RandomInt

func (r *Robot) RandomInt(n int) int

RandomInt uses the robot's seeded random to return a random int 0 <= retval < n

func (*Robot) RandomString

func (r *Robot) RandomString(s []string) string

RandomString is a convenience function for returning a random string from a slice of strings, so that replies can vary.

func (*Robot) Recall

func (r *Robot) Recall(key string) string

Recall recalls a short term memory, or the empty string if it doesn't exist

func (*Robot) Remember

func (r *Robot) Remember(key, value string)

Remember adds a short-term memory (with no backing store) to the robot's brain. This is used internally for resolving the meaning of "it", but can be used by plugins to remember other contextual facts. Since memories are indexed by user and channel, but not plugin, these facts can be referenced between plugins. This functionality is considered EXPERIMENTAL.

func (*Robot) RememberContext

func (r *Robot) RememberContext(context, value string)

RememberContext is a convenience function that stores a context reference in short term memories. e.g. RememberContext("server", "web1.my.dom") means that next time the user uses "it" in the context of a "server", the robot will substitute "web1.my.dom".

func (*Robot) Reply

func (r *Robot) Reply(msg string) RetVal

Reply directs a message to the user

func (*Robot) Say

func (r *Robot) Say(msg string) RetVal

Say just sends a message to the user or channel

func (*Robot) SendChannelMessage

func (r *Robot) SendChannelMessage(channel, msg string) RetVal

SendChannelMessage lets a plugin easily send a message to an arbitrary channel. Use Robot.Fixed().SencChannelMessage(...) for fixed-width font.

func (*Robot) SendUserChannelMessage

func (r *Robot) SendUserChannelMessage(user, channel, msg string) RetVal

SendUserChannelMessage lets a plugin easily send a message directed to a specific user in a specific channel without fiddling with the robot object. Use Robot.Fixed().SencChannelMessage(...) for fixed-width font.

func (*Robot) SendUserMessage

func (r *Robot) SendUserMessage(user, msg string) RetVal

SendUserMessage lets a plugin easily send a DM to a user. If a DM isn't possible, the connector should message the user in a channel.

func (*Robot) UpdateDatum

func (r *Robot) UpdateDatum(key, locktoken string, datum interface{}) (ret RetVal)

UpdateDatum tries to update a piece of data in the robot's brain, providing a struct to marshall and a (hopefully good) lock token. If err != nil, the update failed.

func (*Robot) WaitForReply

func (r *Robot) WaitForReply(regexID string, timeout int) (string, RetVal)

WaitForReply lets a plugin temporarily register a regex for a reply expected to an multi-step command when the robot needs more info. If the regular expression matches, it returns the matched text and RetVal = Ok. If the regular expression doesn't match, it returns an empty string with one of the following RetVals:

	Interrupted - the user issued a new command that ran
 UseDefaultValue - user supplied a single "=", meaning "use the default value"
	ReplyNotMatched - didn't successfully match for any reason
	MatcherNotFound - the regexId didn't correspond to a valid regex
	TimeoutExpired - the user didn't respond within the timeout window given

Plugin authors can define regex's for regexId's in the plugin's JSON config, with the restriction that the regexId must start with a lowercase letter. A pre-definied regex from the following list can also be used:

	Email
	Domain - an alpha-numeric domain name
	OTP - a 6-digit one-time password code
	IPAddr
	SimpleString - Characters commonly found in most english sentences, doesn't
   include special characters like @, {, etc.
	YesNo

func (*Robot) WaitForReplyRegex

func (r *Robot) WaitForReplyRegex(regex string, timeout int) (string, RetVal)

WaitForReplyRegex is identical to WaitForReply except that the first argument is the regex to compile and use. If the regex doesn't compile an error will be logged and ("", MatcherNotFound) will be returned.

type SimpleBrain

type SimpleBrain interface {
	// Store stores a blob of data with a string key, returns error
	// if there's a problem storing the datum.
	Store(key string, blob []byte) error
	// Retrieve returns a blob of data (probably JSON) given a string key,
	// and exists=true if the data blob was found, or error if the brain
	// malfunctions.
	Retrieve(key string) (blob []byte, exists bool, err error)
}

SimpleBrain is the simple interface for a configured brain, where the robot handles all locking issues.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL