Slack bot core/framework written in Go with support for reactions to message updates/deletes

License GoDoc Build Go Report Card Test Coverage Maintainability Mentioned in Awesome Go Latest Version

logo.svg

name.svg

Overview

Slackscot is a slack bot core written in Go. Think of it as the assembly kit to making your own friendly slack bot. It comes with a set of plugins you might enjoy and a friendly API for you to realize your ambitious dreams (if you dreams include this sort of thing).

Requirements

Go 1.11 or above is required, mostly for go module support.

Features

  • Support for reactions to message updates. slackscot does the following:

    • Keeps track of plugin action responses and the message that triggered them

    • On message updates:

      1. Update responses for each triggered action

      2. Delete responses that aren't triggering anymore (or result in errors during the message update)

    • On deletion of triggering messages, responses are also deleted

    • Limitation: Sending a message automatically splits it into multiple slack messages when it's too long. When updating messages, this spitting doesn't happen and results in an message too long error. Effectively, the last message in the initial response might get deleted as a result. Handling of this could be better but that is the current limitation 😕

  • Support for threaded replies to user message with option to also broadcast on channels (disabled by default). See configuration example below where both are enabled.

    • Plugin actions may also explicitely reply in threads with/without broadcasting via AnswerOption
  • Concurrent processing of unrelated messages with guarantees of proper ordering of message updates/deletions

  • Simple extensible storage API for persistence in two flavors: StringStorer and BytesStorer. Both are basic key:value maps. A default file-based implementation is provided backed by leveldb

  • Implementation of StringStorer backed by Google Cloud Datastore. See datastoredb's godoc for documentation, usage and example.

  • In-memory implementation of StringStorer wrapping any StringStorer implementation to offer low-latency and potentially cost-saving storage implementation well-suited for small datasets. Plays well with cloud storage like the [datastoredb]((https://godoc.org/github.com/alexandre-normand/slackscot/store/datastoredb) See inmemorydb's godoc for documentation, usage and example.

  • Support for various configuration sources/formats via viper

  • Support for various ways to implement functionality:

    1. scheduled actions: run something every second, minute, hour, week. Oh Monday is a plugin that demos this by sending a Monday greeting every Monday at 10am (or the time you configure it to).
    2. commands: respond to a command directed at your slackscot. That means something like @slackscot help or a direct message help sent to slackscot.
    3. hear actions (aka "listeners"): actions that evaluated for a match on every message that slackscot hears. You'll want to make sure your Match function isn't too heavy. An example is the "famous" finger quoter plugin
  • Experimental and subject to change: Testing functions to help validate plugin action behavior (see example in triggerer_test.go). Testing functions are found in assertplugin and assertanswer

  • Built-in help plugin supporting a decently formatted help message as a command listing all plugins' actions. If you'd like some actions to not be shown in the help, you can set Hidden to true in its ActionDefinition (especially useful for hear actions)

  • The plugin interface as a logical grouping of one or many commands and hear actions and/or scheduled actions

  • Support for injected services providing plugins easy access to an optionally caching user info and a logger.

Demo

  • slackscot deleting a triggered reaction after seeing a message updated that caused the first action to not trigger anymore and a new action to now trigger (it makes sense when you see it)

different-action-triggered-on-message-update

  • slackscot updating a triggered reaction after seeing a triggering message being updated

same-action-answer-update-on-message-update

  • slackscot deleting a reaction after seeing the triggering message being deleted

reaction-deletion-on-message-delete

  • slackscot threaded replies enabled (with broadcast => on)

threaded-reply-with-broadcast

The Name

The first concrete bot implementation using this code was youppi, named after the great mascot of the Montreal Expos and, when the Expos left Montreal, the Montreal Canadiens.

Slackscot is a variation on the expected theme of slackbot with the implication that this is the core to more than just a regular bot. You know, a friendly company mascot that hangs out on your slack.

Concepts

  • Commands: commands are well-defined actions with a format. Slackscot handles all direct messages as implicit commands as well as @mention <command> on channels. Responses to commands are directed to the person who invoked it.

  • Hear actions: those are listeners that can potentially match on any message sent on channels that slackscot is a member of. This can include actions that will randomly generate a response. Note that responses are not automatically directed to the person who authored the message triggering the response (although an implementation is free to use the user id of the triggering message if desired).

Create Your Own Slackscot

Slackscot provides the pieces to make your mascot but you'll have to assemble them for him/her to come alive. The easiest to get started is to look at a real example: youppi.

youppi running

The godoc is also a good reference especially if you're looking to implement something like a new implementation of the storer interfaces.

Assembling the Parts and Bringing Your slackscot to Life

Here's an abbreviated example of how youppi does it:

package main

import (
	"github.com/alexandre-normand/slackscot"
	"github.com/alexandre-normand/slackscot/config"
	"github.com/alexandre-normand/slackscot/plugins"
	"github.com/alexandre-normand/slackscot/store"
	"github.com/spf13/viper"
	"gopkg.in/alecthomas/kingpin.v2"
	"log"
	"os"
	"io"
)

const (
	name           = "youppi"
)

func main() {
	kingpin.Version(VERSION)
	kingpin.Parse()

	// TODO: initialize storer implementations required by plugins and do any other initialization 
	// required
	...

	// This is the where we create youppi with all of its plugins
	youppi, err := slackscot.NewBot(name, v, options...).
		WithPlugin(plugins.NewKarma(karmaStorer)).
		WithPlugin(plugins.NewTriggerer(triggererStorer)).
		WithConfigurablePluginErr(plugins.FingerQuoterPluginName, func(conf *config.PluginConfig) (p *slackscot.Plugin, err error) { return plugins.NewFingerQuoter(conf) }).
		WithConfigurablePluginCloserErr(plugins.EmojiBannerPluginName, func(conf *config.PluginConfig) (c io.Closer, p *slackscot.Plugin, err error) {
			return plugins.NewEmojiBannerMaker(conf)
		}).
		WithConfigurablePluginErr(plugins.OhMondayPluginName, func(conf *config.PluginConfig) (p *slackscot.Plugin, err error) { return plugins.NewOhMonday(conf) }).
		WithPlugin(plugins.NewVersionner(name, version)).
		Build()
	defer youppi.Close()

	if err != nil {
		log.Fatal(err)
	}

	err = youppi.Run()
	if err != nil {
		log.Fatal(err)
	}
}

Configuration Example

You'll also need to define your configuration for the core, used built-in plugins and any configuration required by your own custom plugins (not shown here). Slackscot uses viper for loading configuration which means that you are free to use a different file format (yaml, toml, env variables, etc.) as desired.

{
   "token": "your-slack-bot-token",
   "debug": false,
   "responseCacheSize": 5000,
   "userInfoCacheSize": 0,
   "maxAgeHandledMessages": 86400,
   "timeLocation": "America/Los_Angeles",
   "storagePath": "/your-path-to-bot-home",
   "replyBehavior": {
      "threadedReplies": true,
      "broadcastThreadedReplies": true
   },
   "plugins": {
      "ohMonday": {
   	     "channelIDs": ["slackChannelId"]
      },
      "fingerQuoter": {
         "frequency": "100",
         "channelIDs": []
      },
      "emojiBanner": {
         "figletFontUrl": "http://www.figlet.org/fonts/banner.flf"
      }
   }
}

Creating Your Own Plugins

It might be best to look at examples in this repo to guide you through it:

  • The simplest plugin with a single command is the versioner
  • One example of scheduled actions is oh monday
  • One example of a mix of hear actions / commands that also uses the store api for persistence is the karma

Contributing

  1. Fork it (preferrably, outside the GOPATH as per the new go modules guidelines)
  2. Make your changes, commit them (don't forget to go build ./... and go test ./...) and push your branch to your fork
  3. Open a PR and fill in the template (you don't have to but I'd appreciate context)
  4. Check the code climate and travis PR builds. You might have to fix things and there's no shame if you do. I probably won't merge something that doesn't pass CI build but I'm willing to help to get it to pass 🖖 .

Open-telemetry integration

Slackscot now supports integration with opentelemetry. To aid with the addition of new metrics, you can find a gowrap template here. To add a metrics to a new interface, you can do something like

gowrap gen -p . -i <interfaceName> -t opentelemetry.template -o <interfaceName>metrics.go

When updating the template, you should consider running go generate in order to refresh the already generated files with the template changes.

Some Credits

slackscot uses Norberto Lopes's Slack API Integration found at https://github.com/nlopes/slack. The core functionality of the bot is previously used James Bowman's Slack RTM API integration and was heavily inspired by talbot, also written by James Bowman.

Comments
  • Global Command Prefix

    Global Command Prefix

    What is this about

    Added an option for a global command prefix

    Checklist

    • [x] I've reviewed my own code
    • [x] I've executed go build ./... and confirmed the build passes
    • [x] I've run go test ./... and confirmed the tests pass
  • Is There A Way To Access The slack.Client Object?

    Is There A Way To Access The slack.Client Object?

    Goal: Create channels and possibly other direct slack commands

    What's the best way to go about this? I can't find any way to directly access the slack.Client object. Did I miss something?

    Thanks!

  • Spamming

    Spamming "I don't understand"

    Describe the bug

    Bot is reacting to his own messages

    Version

    Slackscot version: v1.37.0

    To Reproduce

    Steps to reproduce the behavior:

    1. DM to bot something he can't handle
    2. Bot responds " I don't understand. Ask me for "help" to get a list of things I do"
    3. He starts responding to his previous messages in infinity loop

    Expected behavior

    Bot should ignore his own messages

    Anything you'd like to add

    I found that s.selfID differs from m.BotID, any idea why?

    // Ignore messages_replied and messages send by "us"
    	if m.User == s.selfID || m.BotID == s.selfID {
    		s.log.Debugf("Ignoring message from user [%s] because that's \"us\" [%s]", m.User, s.selfID)
    		return responses
    	}
    

    One more question:

    • Can I send more than one message to one action? I need to split up messages but by my own splitting (split daily menu by restaurant)
    • Probably I can do it by returning nil in answerer and sending messages via SlackClient and PostMessage
  • Command Prefix Support

    Command Prefix Support

    What is this about

    Adds support for using a configurable prefix for commands. This is used in addition to DM/@mention support.

    This PR also adds the vendor directory to .gitignore and adds an excessive number of unit tests for slackscot.isCommand.

    Ideas For Improvements

    • Condense unit tests a bit
    • Allow for multiple command prefixes by making the isCommand function to be passed in via arg to NewSlackscot and/or per plugin.

    Checklist

    • [x] I've reviewed my own code
    • [x] I've executed go build ./... and confirmed the build passes
    • [x] I've run go test ./... and confirmed the tests pass
    • [ ] Test that it works with a real bot
  • Is Single Threaded Behavior For Commands Normal?

    Is Single Threaded Behavior For Commands Normal?

    Describe the bug

    When our bot has a command with a long running Answerer function it delays handling of other messages until the long running Answerer has completed.

    Version

    v1.37.0

    Reference code

    n/a

    To Reproduce

    Steps to reproduce the behavior:

    1. Create command XYZ that runs for at least 10 seconds
    2. Run xyz via slack in channel a
    3. Run xyz via slack in channel b
    4. Results for channel a post
    5. 30 seconds later the results for channel b post

    Expected behavior

    Commands Answerer functions run in parallel when multiple messages/commands are queued up.

    Anything you'd like to add

    Based on the code in slackscot.Run() I'm thinking this is expected. It may be easily solved by running more go s.runInternal(....) workers. Although a through review for any maps and other structures that aren't thread safe would be advisable.

    If we can figure out a good way to do this change I may have the bandwidth to make the change.

  • Switched to slack-go/slack rather than nlopes old repo

    Switched to slack-go/slack rather than nlopes old repo

    What is this about

    Using the new slack-go/slack repo

    Checklist

    • [x] I've reviewed my own code
    • [x] I've executed go build ./... and confirmed the build passes
    • [x] I've run go test ./... and confirmed the tests pass
  • Enable async processing of messages

    Enable async processing of messages

    What is this about

    This intends to address the use-case described by @pmcintyresfdc in issue #145.

    This is still a big rough and I'll be pushing updates in the coming days but wanted to get something out to show you, @pmcintyresfdc.

    TODO:

    • [x] Move hashing-related functions and write missing table tests for those.
    • [x] Review the godoc to make sure it's clear enough to users that might want to play with this advanced configuration.
    • [x] I'd also like to comment on the main test that proves this works since it might not be obvious to me or others what's going on if we ever have to revisit later.
    • [x] Add some timeout to the new test since someone that would break the parallel processing of messages would end up having that test hang and that wouldn't be easy to troubleshoot if I'm not providing a descriptive error message.

    That said, I have that good robust test that makes me feel confident with this implementation.

    And thanks to @coderunner for having inspired the partition hashing scheme from previous work done together. I think this nice feature doesn't look too bad as far as maintenance cost goes.

    Checklist

    • [x] I've reviewed my own code
    • [x] I've executed go build ./... and confirmed the build passes
    • [x] I've run go test ./... and confirmed the tests pass
  • Karma plugin: allow multiple users to gain karma in a single post

    Karma plugin: allow multiple users to gain karma in a single post

    Is your feature request related to a problem? Do you think it's something that needs to be implemented in slackscot (versus your integrating bot)? Please describe

    The karma plugin only works for one user per message. Example: if a user posts the following in a Slack channel/thread, only user1 will gain karma:

    @user1++ @user2++ @user3++
    

    We've often has to individually give karma to multiple users, which is a bit of a pain.

    Describe the solution you'd like

    The aforementioned example message should give karma to multiple users all at once. Multiple responses by the Slack bot for each user gaining karma is acceptable.

    Describe alternatives you've considered

    N/A

    Additional context

    Seems like it would be as simple as iterating over the regexp matches found here

  • Compile error: schedule

    Compile error: schedule

    Describe the bug

    Can't download latest slackscot because of compile error:

    go get -u github.com/alexandre-normand/slackscot
                           
    go: finding golang.org/x/sys latest
    go: finding github.com/marcsantiago/gocron latest
    # github.com/alexandre-normand/slackscot/schedule
    ../../../go/pkg/mod/github.com/alexandre-normand/[email protected]/schedule/schedule.go:167:17: cannot use false (type bool) as type func(*gocron.Job) in argument to s.Every
    

    Version

    Slackscot version: v1.37.1

    To Reproduce

    Steps to reproduce the behavior:

    1. go get -u github.com/alexandre-normand/slackscot in your bot project

    Expected behavior

    It should compile

    Anything you'd like to add

    It's weird because I can't see any changes regarding this functionality nor in gocron. And it was working for me yesterday with 1.37.0

    https://github.com/marcsantiago/gocron/blob/master/gocron.go#L467 There is no boolean parameter.

  • Fix Storage Path Not Resolving ~ as Home Directory.

    Fix Storage Path Not Resolving ~ as Home Directory.

    @timoman @skuehn @ericchang: Here's the bug that @timoman's "friend" ran into a while back. It was with some other tool but with the same underlying issue.

    In this case, if someone was using something like ~/botstorage/, the resulting files would get created as ./~/botstorage instead of resolving the ~ as the home directory.

    This fixes it so that no more evil ~ get inadvertently created.

  • Add support for granting/revoking karma to multiple users

    Add support for granting/revoking karma to multiple users

    What is this about

    Addresses #161. Example:

    image

    Checklist

    • [x] I've reviewed my own code
    • [x] I've executed go build ./... and confirmed the build passes
    • [x] I've run go test ./... and confirmed the tests pass
  • Rewrite For New API

    Rewrite For New API

    Your Question

    Any thoughts about creating a new framework based on the new slack API style using inbound HTTPS calls from slack?

    We're needing to move in that direction to support some of the newer features like modals. We already have a hack to get around that and some firewall issues but I'm wondering if you'd be interested in help with any clean-slate work here.

REPLbot is a Slack bot that allows you to control a REPL from within Slack.
REPLbot is a Slack bot that allows you to control a REPL from within Slack.

?? REPLbot REPLbot is a Slack bot that allows you to control a REPL from within Slack. It comes with a few REPLs (Go ?? , Java, NodeJS, PHP, Python, R

Dec 31, 2022
This slack-bot collects all @gophers notifications into one slack channel

slack-bot Slack bot copying reference to a ping to notifications channel The bot is designed to listen on messages containing specific tag, copy refer

Apr 11, 2022
send current weather updates from openweathermap api to your slack profile status using github action
send current weather updates from openweathermap api to your slack profile status using github action

go-slack-weather Send current weather updates from OpenWeatherMap API to your Slack profile status using GitHub Action Contents Setup Change Update Pe

Oct 31, 2022
Slack Bot Framework

slacker Built on top of the Slack API github.com/slack-go/slack with the idea to simplify the Real-Time Messaging feature to easily create Slack Bots,

Dec 25, 2022
Auto send check-in message to Slack channel at a specific time.
Auto send check-in message to Slack channel at a specific time.

Auto Slack Check-in auto-slack-checkin is a command line interface (CLI) that sends check-in or check-out message for you while you are sleeping ??.

Feb 15, 2022
IRC, Slack, Telegram and RocketChat bot written in go
IRC, Slack, Telegram and RocketChat bot written in go

go-bot IRC, Slack & Telegram bot written in Go using go-ircevent for IRC connectivity, nlopes/slack for Slack and Syfaro/telegram-bot-api for Telegram

Dec 20, 2022
Bot that polls activity API for Github organisation and pushes updates to Telegram.

git-telegram-bot Telegram bot for notifying org events Requirements (for building) Go version 1.16.x Setup If you don't have a telegram bot token yet,

Apr 8, 2022
Go Blog Updates (Telegram Bot service)

Go Blog Updates - Telegram Bot This service consumes events about new posts in go blog (go.dev) from message broker (rabbitmq) (gbu-scanner service pu

Jan 10, 2022
Bot-template - A simple bot template for creating a bot which includes a config, postgresql database

bot-template This is a simple bot template for creating a bot which includes a c

Sep 9, 2022
🦎 A slack bot for random gex quotes

?? A slack bot for random gex quotes

Jan 3, 2022
Golang bot that connects to slack using Socketclient to read and write messages.
Golang bot that connects to slack using Socketclient to read and write messages.

?? (not)simple go project ?? Golang bot that connects to slack using Socketclient to read and write messages. ?? Use ?? @SquidBot : Mentions your name

Aug 23, 2022
Snitch - a Slack bot that fetches pending review PRs built in Go
Snitch - a Slack bot that fetches pending review PRs built in Go

Snitch Snitch is a Slack bot that fetches and publish PRs with pendings reviews

Nov 29, 2022
A serverless bot which periodically checks configured BigQuery capacity commitments, reservations and assignments against actual slot consumption of running jobs and reports findings to Slack/Google Chat.
A serverless bot which periodically checks configured BigQuery capacity commitments, reservations and assignments against actual slot consumption of running jobs and reports findings to Slack/Google Chat.

Solution Guide This solution implements a ChatOps-like approach to monitoring slot utilization of Google Cloud BigQuery reservations. As an alternativ

Dec 7, 2022
CultureBot - A slack bot used to act as a companion to new people in tech

Culture Bot A slack bot used to act as a companion to new people in tech. Helps

Feb 6, 2022
Flexible message router add-on for go-telegram-bot-api library.
Flexible message router add-on for go-telegram-bot-api library.

telemux Flexible message router add-on for go-telegram-bot-api library. Table of contents Motivation Features Minimal example Documentation Changelog

Oct 24, 2022
Discord bot to check if a URL attached on a message is part of the verified list

OCM URL Verify Bot Checks the url included in a discord message. Displays a verified status when the domain of the url attached is in the verified-dom

Nov 30, 2021
A bot based on Telegram Bot API written in Golang allows users to download public Instagram photos, videos, and albums without receiving the user's credentials.

InstagramRobot InstagramRobot is a bot based on Telegram Bot API written in Golang that allows users to download public Instagram photos, videos, and

Dec 16, 2021
Golang Framework for writing Slack bots

hanu - Go for Slack Bots! The Go framework hanu is your best friend to create Slack bots! hanu uses allot for easy command and request parsing (e.g. w

Oct 24, 2022
Simple yet customizable bot framework written in Go.
Simple yet customizable bot framework written in Go.

Introduction Sarah is a general-purpose bot framework named after the author's firstborn daughter. This comes with a unique feature called "stateful c

Dec 12, 2022