sirius

package module
v0.8.2 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2018 License: MIT Imports: 14 Imported by: 0

README

sirius ⚡

An extension server for Slack.

Extensions are small pieces of user-created functionality that enhance the default Slack experience. For example, the thumbs_up extension converts (y) to 👍 in all outgoing messages.

Sirius is an extension server that allows you to run these extensions as a standalone service, and use them without having to install anything on the devices you use Slack from.

How does it work?

Sirius connects to the Slack Real Time Messaging API using your Slack OAuth credentials. Once logged in, it monitors your active channels for messages you send, and processes them using the enabled extensions. Modifications to the message body are instantly propagated using a regular message edit.

Sirius does not store or forward any message data, including metadata.

Bundled extensions

These extensions come included with sirius by default, and can be enabled immediately using their EIDs (names).

thumbs_up

Converts (y) to 👍 in all outgoing messages.

kayex: Awesome (y)

kayex: Awesome 👍

--

geocode

Type !address followed by any sort of geographical location, and the geocode extension will show the exact address and coordinates.

kayex: !address Empire State Building

kayex: 350 5th Ave, New York, NY 10118, USA
(40.748441, -73.985664)

--

ip_lookup

Type !ip followed by an IP address to show related geolocation information.

kayex: !ip 8.8.8.8
kayex: !ip 2001:4860:4860::8888 // IPv6

kayex: 8.8.8.8
Mountain View, United States (US)
Google
kayex: 2001:4860:4860::8888
Chicago, United States (US)
Google

--

quotes

Avoids breaking blockquotes that contain newlines.

kayex: >This is
a multi-line
quote.

kayex: >This is
>a multi-line
>quote.

--

Censor

Helps you avoid using inappropriate language.

kayex: I messed up. Fuck.

kayex: I messed up. CENSORED

--

Getting started

Sirius is available as a free, hosted service at http://adsa.se/sirius.

You can also run Sirius yourself by following the instructions below.

Building
$ go build github.com/kayex/sirius/cmd/sirius-local
Running

Before starting the service, a users.json file needs to be created in the same directory as the executable will run from. The file should consist of a single JSON array containing OAuth tokens for the Slack accounts that Sirius should be enabled for:

users.json

[
	"xoxp-234234234-23234234-234234324234234-2342343242433"
]

You can then start the main service by simply running the sirius-local executable:

$ ./sirius-local

Can I request a new extension?

Of course! Just submit a new issue and make sure to tag it with the extension label. You can also submit your own extension by creating a pull request.

Creating a new extension

Creating a new extension is only a matter of implementing the Extension interface:

package sirius

type Extension interface {
	Run(Message, ExtensionConfig) (MessageAction, error)
}

The Run function is called with every outgoing message captured via the RTM API, and should return either an error or a MessageAction. Each extension is passed an ExtensionConfig with every invocation, which is a read-only key/value configuration store. The ExtensionConfig is unique per user and extension, and is used to access all extension-specific configuration values.

MessageActions are returned by extensions to describe changes that should be made to the processed message. This includes things such as editing the message text or deleting the message entirely. Actions are accumulated by the extension runner and broadcasted via the RTM API in timed batches.

Extensions that do not need to modify the message in any way can simply return NoAction(), nil.

An extension has exactly 2000 ms to finish execution if it wishes to provide a MessageAction (other than NoAction()). Extensions executing past this deadline will be allowed to finish, but any message actions returned will be discarded.

MessageActions

These are the default MessageActions. New actions can be created by implementing the MessageAction interface:

type MessageAction interface {
	Perform(*Message) error
}
TextEditAction

Modifications to the message text are easily done using (*Message) EditText():

func (*ThumbsUp) Run(m Message, cfg ExtensionConfig) (MessageAction, error) {
	edit := m.EditText()
	
	edit.Substitute("(y)", ":+1:")
	edit.Substitute("(Y)", ":+1:")

	return edit, nil
}

Documentation

Index

Constants

View Source
const (
	NEW    SyncAction = "new"
	UPDATE            = "update"
	DELETE            = "delete"
)
View Source
const EMOJI = "⚡" // The high voltage/lightning bolt emoji (:zap: in Slack)

Variables

This section is empty.

Functions

This section is empty.

Types

type API

type API interface {
	GetUserID(token string) (slack.ID, error)
}

API is a connection to the Slack API.

type CancelClient

type CancelClient struct {
	*Client
	// contains filtered or unexported fields
}

func (*CancelClient) Start

func (c *CancelClient) Start() error

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(cfg ClientConfig) *Client

func (*Client) Start

func (c *Client) Start(ctx context.Context) error

type ClientConfig

type ClientConfig struct {
	// contains filtered or unexported fields
}

type Command

type Command struct {
	Name string
	Args []string
}

func (*Command) Arg

func (c *Command) Arg(a int) string

Arg returns argument number a, or "" if there is no argument in that position.

func (*Command) CollapseArgs

func (c *Command) CollapseArgs() string

type ConfigExtension

type ConfigExtension struct {
	Extension
	Cfg ExtensionConfig
}

ConfigExtension represents an extension with an associated configuration.

func FromConfiguration

func FromConfiguration(l ExtensionLoader, cfg *Configuration) (*ConfigExtension, error)

func LoadFromSettings

func LoadFromSettings(l ExtensionLoader, s Profile) ([]ConfigExtension, error)

func NewConfigExtension

func NewConfigExtension(ex Extension, cfg ExtensionConfig) *ConfigExtension

func (*ConfigExtension) Run

func (ex *ConfigExtension) Run(msg Message) (MessageAction, error)

type Configuration

type Configuration struct {
	URL string
	EID EID
	Cfg ExtensionConfig
}

func FromConfigurationMap

func FromConfigurationMap(cfg map[string]interface{}) []Configuration

func NewConfiguration

func NewConfiguration(eid EID) Configuration

func NewHTTPConfiguration

func NewHTTPConfiguration(url string) Configuration

type Connection

type Connection interface {
	Start(ctx context.Context) error
	Send(*Message) error
	Update(*Message) error
	Messages() <-chan Message
	Details() ConnectionDetails

	// Closed returns a channel that can be used for detecting when the connection closes.
	// If the connection was closed due to an error, the error is returned. Otherwise, nil is returned.
	Closed() <-chan error
}

Connection is a connection to the Slack Messaging services.

type ConnectionDetails

type ConnectionDetails struct {
	UserID   slack.UserID
	SelfChan string
}

ConnectionDetails are details that cannot be discerned until the client has performed authentication against Slack.

type EID

type EID string

type EmptyAction

type EmptyAction struct{}

func NoAction

func NoAction() *EmptyAction

func (*EmptyAction) Perform

func (*EmptyAction) Perform(*Message) error

type ExecutionResult

type ExecutionResult struct {
	Err    error
	Action MessageAction
}

type Executor

type Executor struct {
	// contains filtered or unexported fields
}

func NewExecutor

func NewExecutor(loader ExtensionLoader, timeout time.Duration) *Executor

func (*Executor) Load

func (e *Executor) Load(s Profile) error

Load sets up the executor by reading s and creating the appropriate extensions.

func (*Executor) Run

func (e *Executor) Run(msg Message) <-chan ExecutionResult

type Extension

type Extension interface {
	Run(Message, ExtensionConfig) (MessageAction, error)
}

type ExtensionConfig

type ExtensionConfig map[string]interface{}

func (ExtensionConfig) Boolean

func (cfg ExtensionConfig) Boolean(key string) bool

Boolean fetches a boolean value for key. Returns false if key is not set.

func (ExtensionConfig) Float

func (cfg ExtensionConfig) Float(key string, def float64) float64

Float fetches a float value for key. Returns def if key is not set.

func (ExtensionConfig) Integer

func (cfg ExtensionConfig) Integer(key string, def int) int

Integer fetches an integer value for key. Returns def if key is not set.

func (ExtensionConfig) List

func (cfg ExtensionConfig) List(key string) []string

List fetches a list value for key. Returns an empty list if key is not set.

func (ExtensionConfig) Read

func (cfg ExtensionConfig) Read(key string, def interface{}) interface{}

Read fetches a value of any type for key. Returns def if key is not set.

func (ExtensionConfig) String

func (cfg ExtensionConfig) String(key string, def string) string

String fetches a string value for key. Returns def if key is not set.

type ExtensionLoader

type ExtensionLoader interface {
	Load(EID) (Extension, error)
}

type HttpExecution

type HttpExecution struct {
	Message string          `json:"message"`
	Config  ExtensionConfig `json:"config"`
	// contains filtered or unexported fields
}

type HttpExecutionResult

type HttpExecutionResult struct {
	Err     error
	Actions []HttpMessageAction
}

func (*HttpExecutionResult) ExecutionResult

func (her *HttpExecutionResult) ExecutionResult() *ExecutionResult

type HttpExtension

type HttpExtension struct {
	Host string
	// contains filtered or unexported fields
}

func NewHttpExtension

func NewHttpExtension(host string, client *http.Client) *HttpExtension

func (*HttpExtension) Run

type HttpMessageAction

type HttpMessageAction map[string]interface{}

func (HttpMessageAction) ToMessageAction

func (act HttpMessageAction) ToMessageAction() MessageAction

type MQTTSync

type MQTTSync struct {
	// contains filtered or unexported fields
}

func NewMQTTSync

func NewMQTTSync(rmt *Remote, cfg mqtt.Config, topic string) *MQTTSync

func (*MQTTSync) Sync

func (m *MQTTSync) Sync(ctx context.Context, s *Service)

type Message

type Message struct {
	Text      string
	UserID    slack.UserID
	Channel   string
	Timestamp string
}

func NewMessage

func NewMessage(userID slack.UserID, text, channel, timestamp string) Message

func (*Message) Command

func (m *Message) Command(name string) (*Command, bool)

func (*Message) EditText

func (*Message) EditText() *TextEditAction

func (*Message) Query

func (m *Message) Query(q text.Query) bool

type MessageAction

type MessageAction interface {
	Perform(*Message) error
}

MessageAction is an action that can be performed on a message. Used for serializing the modifications generated by extensions while allowing them to execute in parallel.

type Profile

type Profile struct {
	Configurations []Configuration
}

type RTMConnection

type RTMConnection struct {
	// contains filtered or unexported fields
}

RTMConnection is a Connection that utilizes the Slack RTM API.

func (*RTMConnection) Closed

func (conn *RTMConnection) Closed() <-chan error

func (*RTMConnection) Details

func (conn *RTMConnection) Details() ConnectionDetails

func (*RTMConnection) Messages

func (conn *RTMConnection) Messages() <-chan Message

func (*RTMConnection) Send

func (conn *RTMConnection) Send(msg *Message) error

func (*RTMConnection) Start

func (conn *RTMConnection) Start(ctx context.Context) error

func (*RTMConnection) Update

func (conn *RTMConnection) Update(msg *Message) error

type Remote

type Remote struct {
	// contains filtered or unexported fields
}

func NewRemote

func NewRemote(host, token string) *Remote

func (*Remote) GetUser

func (r *Remote) GetUser(id slack.SecureID) (*User, error)

func (*Remote) GetUsers

func (r *Remote) GetUsers() ([]User, error)

type RemoteUser

type RemoteUser struct {
	IDHash         string      `json:"sirius_id"`
	Token          string      `json:"slack_token"`
	Extensions     interface{} `json:"extensions"`
	HttpExtensions interface{} `json:"http_extensions"`
}

func (*RemoteUser) ToUser

func (ru *RemoteUser) ToUser() *User

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(l ExtensionLoader) *Service

func (*Service) AddUser

func (s *Service) AddUser(u *User, notify bool) error

func (*Service) DropUser

func (s *Service) DropUser(id slack.ID)

func (*Service) Start

func (s *Service) Start(ctx context.Context, users []User)

func (*Service) WithSync

func (s *Service) WithSync(sync Sync) *SyncedService

type SlackAPI

type SlackAPI struct {
	// contains filtered or unexported fields
}

func NewSlackAPI

func NewSlackAPI(token string) *SlackAPI

func (*SlackAPI) GetUserID

func (api *SlackAPI) GetUserID(token string) (slack.ID, error)

func (*SlackAPI) NewRTMConnection

func (api *SlackAPI) NewRTMConnection() *RTMConnection

type Sync

type Sync interface {
	Sync(context.Context, *Service)
}

type SyncAction

type SyncAction string

type SyncMessage

type SyncMessage struct {
	Type SyncAction
	ID   slack.SecureID
}

type SyncedService

type SyncedService struct {
	// contains filtered or unexported fields
}

func (*SyncedService) Start

func (s *SyncedService) Start(ctx context.Context, u []User)

type TextEditAction

type TextEditAction struct {
	// contains filtered or unexported fields
}

TextEditAction represents a series of modifications to the message Text property

func TextEdit

func TextEdit() *TextEditAction

func (*TextEditAction) Append

func (edit *TextEditAction) Append(app string) *TextEditAction

func (*TextEditAction) Perform

func (edit *TextEditAction) Perform(msg *Message) error

func (*TextEditAction) Prepend

func (edit *TextEditAction) Prepend(pre string) *TextEditAction

func (*TextEditAction) Set

func (edit *TextEditAction) Set(txt string) *TextEditAction

func (*TextEditAction) Substitute

func (edit *TextEditAction) Substitute(search string, sub string) *TextEditAction

func (*TextEditAction) SubstituteQuery

func (edit *TextEditAction) SubstituteQuery(q text.Query, sub string) *TextEditAction

type User

type User struct {
	Profile
	ID    slack.ID
	Token string
}

func NewUser

func NewUser(token string) *User

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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