alertify

package module
v0.0.0-...-3835bca Latest Latest
Warning

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

Go to latest
Published: Jan 2, 2021 License: Apache-2.0 Imports: 12 Imported by: 0

README

alertify

Build Status go.dev reference Go Report Card License DeepSource

alertify is a simple Go package that allows to play a song on a Spotify device upon receiving alert message from a preconfigured source or via HTTP API request.

The original goal of the project was to play a Spotify song when a critical infrastructure alert is detected in a dedicated Slack channel. The project has since evolved beyond this goal and now allows to plug in different monitoring sources.

Prerequisites

alertify uses Spotify API therefore there is a couple of prerequisites that need to be satisfied before you can use it.

Spotify API access

Before you use alertify package you need to register your application on Spotify developer portal. Retrieve the Client ID and Client Secret API keys and store them in a secure location.

alertify uses Spotify Connect Web API to play a song on a Spotify device. Spotify Connect API uses OAuth to grant the permissions to your application with the following Spotify API access scopes.

Spotify application settings

Once your application has been registered, you need to visit your application settings in the developer portal and specify Redirect URIs where the alertify Spotify API client will redirect you after the API authentication failure or success.

Design notes

At the core of the package is alertify.Bot object, which is responsible for playing the songs on the preconfigured Spotify device. Besides the ability to play the Spotify songs, alertify.Bot also provides a simple HTTP API service, alas at this point the API service does not provide any authentication so be careful if you use this project on publicly accessible network: luckily the API service can be bound to a local unix socket, so you might want to use that option.

alertify.Bot is intended to run in a dedicated goroutine and when run on its own it does nothing unless being explicitly requested to play a song via its HTTP API. The API also provides an endpoint which allows to pause the song playback.

Things get more interesting when you register some alert "monitors" with the alertify.Bot. The monitors are objects which satisfy alertify.Monitor interface and which can communicate with alertify.Bot by sending it alertify.Msg objects over the predefined Go channel. Please see the Godoc for implementation details.

If I have more time I'll move the local in-process communication from Go channels to protobufs or provide protobufs communication interface as well, but at this point I couldnt be bothered as it's just a fun side project and Go channel communication is easy to implement without any extra dependencies.

Simple example

The code snippet below illustrates how you would normally use the alertify package. For a more elaborate example please read below about the slackertify example or see the full source code in the project examples directory.

        // create bot
        bot, err := alertify.NewBot(botConfig)
        if err != nil {
                log.Printf("Error creating bot: %v", err)
                os.Exit(1)
        }

        // Create Slack monitor
        slack, err := monitor.NewSlackMonitor(slackConfig)
        if err != nil {
                log.Printf("Error creating slack monitor: %s", err)
                os.Exit(1)
        }

        // register slack monitor
        if err := bot.RegisterMonitor(slack); err != nil {
                log.Printf("Error registering %s: %s", slack, err)
                os.Exit(1)
        }

        log.Fatal(bot.ListenAndAlert())

slackertify

The project provides a slightly more elaborate example about how to use alertify to monitor message in a predefined Slack channel for a particular message pattern. It uses Slack RTM (Real Time Message) API, so beside the Spotify API keys described earlier in this README, you will need to register the example on the Slack workspace portal to retrieve Slack API keys.

Spotify API keys

Provided you have registered slackertify via the earlier described Spotify API developer portal, you need to export your Spotify ID and secret keys via the following environment variables:

export SPOTIFY_ID="xxx"
export SPOTIFY_SECRET="xxx"

Slack API keys

Similarly, once you've registered slackertify on Slack, export your Slack API keys via the following environment variable:

export SLACK_API_KEY="xxx"

Get started

go get the project and ensure its dependencies are vendored:

$ go get -u github.com/milosgajdos/alertify
$ cd $GOPATH/src/github.com/milosgajdos/alertify && make dep

Build the slackertify binary:

$ make slackertify
mkdir -p ./_build
go build -o "./_build/slackertify" "examples/slackertify/slackertify.go"

You can also build all examples as follows:

$ make all
mkdir -p ./_build
for example in slackertify; do \
		go build -o "./_build/$example" "examples/$example/$example.go"; \
	done

Display the help:

$ ./_build/slackertify -help
Usage of ./_build/slackertify:
  -device-id string
    	Spotify device ID as recognised by Spotify API
  -device-name string
    	Spotify device name as recognised by Spotify API
  -redirect-uri string
    	Spotify API redirect URI (default "http://localhost:8080/callback")
  -slack-channel string
    	Slack channel that receives alerts (default "devops-production")
  -slack-msg string
    	A regexp we are matching the slack messages on (default "alert")
  -slack-user string
    	Slack username whose message we alert on (default "production")
  -song-uri string
    	Spotify song URI (default "spotify:track:2xYlyywNgefLCRDG8hlxZq")

Running slackertify

You can run the slackertify like this:

$ ./_build/slackertify -slack-channel "test-bot" -slack-msg "alert" -slack-user "gyre"

On the start you will be prompted to visit Spotify authentication URL where you'll grant the access to the earlier described Spotify API scopes. Once you have successfully authentication you are ready to start alerting \o/:

[ slackertify ] Registering HTTP route -> Method: POST, Path: /alert/play
[ slackertify ] Registering HTTP route -> Method: POST, Path: /alert/silence
[ slackertify ] Starting bot message listener
[ slackertify ] Starting Slack Monitor
[ slackertify ] Starting HTTP API service

Spotify devices

slackertify allows you to specify a specific Spotify device ID to play a song configured by passing in Spotify Song URI via command line parameters. If you leave these empty, slackertify will scan the local network for all available Spotify devices and play a default song on the first active device. If no active device is found, slackertify won't start and will fail straight away with non-zero exit status.

API service

As discussed earlier, alertify.Bot implements a simple HTTP API which allows you to trigger the playback of a song or pause it. Here is a simple example what this looks like in practice:

Trigger the alert playback:

$ curl -X POST localhost:8080/alert/play

The song should start playing on either the explicitly Spotify device or on the firs available device:

[ slackertify ] POST	/alert/play
[ slackertify ] Received message: alert
[ slackertify ] Attempting to play: "Take Me Home, Country Roads" on Device ID: f6e2bbe128cd0b9b5137ee18dd5afdc34b6a2598 Name: ceres

You can also silence the alert song via the API:

$ curl -X POST localhost:8080/alert/silence

The song should now be paused:

[ slackertify ] POST	/alert/silence
[ slackertify ] Received message: silence
[ slackertify ] Attempting to pause alert playback on Device ID: f6e2bbe128cd0b9b5137ee18dd5afdc34b6a2598 Name: ceres

Slack messages

slackertify listens to all Slack messages in a Slack channel specified via -slack-channel command line switch. slackertify will play a song once it detects a message sent by a user specified via -slack-user command line switch which has a pattern specified via -slack-msg command line switch:

[ slackertify ] Slack alert message match detected!
[ slackertify ] Received message: alert

Shutting down

slackertify implements basic signal handler and stops all goroutines safely:

^C[ slackertify ] Got signal: interrupt: Shutting down
[ slackertify ] Stopping message listener
[ slackertify ] HTTP API service shutting down
[ slackertify ] HTTP API service stopped
[ slackertify ] Message listener shutting down
[ slackertify ] Message listener stopped
[ slackertify ] Shutting down Slack Monitor
[ slackertify ] Slack Monitor stopped
[ slackertify ] Slack Monitor stopped
[ slackertify ] Bot message listener stopped

Contributing

IF YOU HAVE TIME, THEN YES PLEASE!!!

Documentation

Index

Constants

View Source
const (
	// APIVERSION specifies API version
	APIVERSION = "v1"
	// Timeout is bot response timeout
	Timeout = 3 * time.Second
)

Variables

This section is empty.

Functions

This section is empty.

Types

type API

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

API provides a simple HTTP API

func NewAPI

func NewAPI(ctx *Context, address string, tlsConfig *tls.Config) (*API, error)

NewAPI creates and configures API server

NewAPI creates and initializes API server with provided configuration It returns error if either configuration is invalid or if API server could not be created

func (*API) ListenAndServe

func (a *API) ListenAndServe() error

ListenAndServe starts API server and listens for HTTP requests

ListenAndServe blocks until http server returns error Due to its blocking behaviour this function should be run in its own goroutine

type Bot

type Bot struct {

	// mutex
	*sync.Mutex
	// contains filtered or unexported fields
}

Bot plays spotify songs when requested

func NewBot

func NewBot(c *BotConfig) (*Bot, error)

NewBot creates new alertify bot and returns it It fails with error if neither of the following couldnt be created: Spotify API client, Slack API client, HTTP API service

func (*Bot) Alert

func (b *Bot) Alert(songURI string) error

Alert plays songURI song on Spotify

func (*Bot) ListenAndAlert

func (b *Bot) ListenAndAlert() error

ListenAndAlert starts Bot message listener and plays Spotify song when it receives alert message This is a blocking function call and therefore should be run in a dedicated goroutine

func (*Bot) RegisterMonitor

func (b *Bot) RegisterMonitor(monitors ...Monitor) error

RegisterMonitor registers remote monitor

func (*Bot) Silence

func (b *Bot) Silence() error

Silence pauses Spotify playback

func (*Bot) Stop

func (b *Bot) Stop()

Stop stops bot listener

type BotConfig

type BotConfig struct {
	// Spotify configures Spotify API client
	Spotify *SpotifyConfig
}

BotConfig configures alertify bot

type Context

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

Context provides API service context

type Monitor

type Monitor interface {
	// MonitorAndAlert sends messages to message channel
	MonitorAndAlert(chan<- *Msg) error
	// Stop stops the monitor
	Stop()
	// String implements stringer interface
	String() string
}

Monitor monitors some activity and sends message to channel

type Msg

type Msg struct {
	// Cmd is specifies command name
	Cmd string
	// Data allows to attach arbitrary data to the message
	Data interface{}
	// Resp is a channel used to send response back to monitor
	Resp chan interface{}
}

Msg is allows to control aleritfy bot behavior

type SpotifyAuth

type SpotifyAuth struct {
	// Spotify authenticator
	*spotify.Authenticator
	// State is OAuth state
	State string
	// RedirectURI is Spotify RedirectURI
	RedirectURI string
}

SpotifyAuth allows to authenticate with Spotify API

func NewSpotifyAuth

func NewSpotifyAuth(clientID, clientSecret, redirectURI, state string) *SpotifyAuth

NewSpotifyAuth returns SpotifyAuth which is used to authenticate with Spotify API

func (*SpotifyAuth) URL

func (a *SpotifyAuth) URL() string

URL returns Spotify API OAuth URL

type SpotifyClient

type SpotifyClient struct {
	// Spotify client
	*spotify.Client

	// mutex
	*sync.Mutex
	// contains filtered or unexported fields
}

SpotifyClient implements Spotify client

func NewSpotifyClient

func NewSpotifyClient(c *SpotifyConfig) (*SpotifyClient, error)

NewSpotifyClient authenticates with Spotify API and returns SpotifyClient It returns error if Spotify API authentication fails

func (*SpotifyClient) Device

func (s *SpotifyClient) Device() *spotify.PlayerDevice

Device returns Spotify active playback device

func (*SpotifyClient) Pause

func (s *SpotifyClient) Pause() error

Pause pauses active playback on a currently active Spotify device It returns error if the playback can't be paused

func (*SpotifyClient) PlaySong

func (s *SpotifyClient) PlaySong(songURI string) error

PlaySong plays Spotify song passed in as songURI

func (*SpotifyClient) SetDevice

func (s *SpotifyClient) SetDevice(deviceID, deviceName string) error

SetDevice allows to set Spotify player device on which you can play alert song If deviceID is empty string, it searches for device ID of the device specified by deviceName If both deviceID and deviceName are empty Spotify client will use the first active device it finds

type SpotifyConfig

type SpotifyConfig struct {
	// ClientID is Spotify Client ID
	ClientID string
	// ClientSecret is Spotify Client secret
	ClientSecret string
	// RedirectURI is Spotify app OAuth redirect URI
	RedirectURI string
	// DeviceID is Spotify device ID
	DeviceID string
	// DeviceName is Spotify device name
	DeviceName string
	// SongURI is Spotify song URI
	SongURI string
}

SpotifyConfig configures Spotify API client

Directories

Path Synopsis
examples
slackertify command

Jump to

Keyboard shortcuts

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