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

Overview

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.

Issues
  • 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
    opened by pmcintyresfdc 25
  • 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!

    opened by pmcintyresfdc 17
  • 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
    opened by Dasio 15
  • 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
    opened by pmcintyresfdc 10
  • 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.

    opened by pmcintyresfdc 8
  • 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
    opened by pmcintyresfdc 5
  • 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
    opened by alexandre-normand 5
  • 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.

    opened by Dasio 4
  • 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

    opened by saidmasoud 4
  • 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.

    opened by alexandre-normand 4
  • 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.

    opened by pmcintyresfdc 4
Owner
Alexandre Normand
Alexandre Normand
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

Muhammad Thomas Fadhila Yahya 4 Nov 12, 2021
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,

Raed Shomali 571 Nov 17, 2021
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

null 711 Nov 30, 2021
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,

Skycoin 3 May 13, 2021
🦎 A slack bot for random gex quotes

?? A slack bot for random gex quotes

Matt Gleich 5 Nov 29, 2021
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

Malin J. 0 Oct 8, 2021
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

Andrew Dunai 15 Aug 21, 2021
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

Rafael Delos Santos 0 Nov 29, 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

FTC Team 7 Oct 23, 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

Sebastian Müller 134 Oct 11, 2021
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

Go Hagiwara 212 Dec 4, 2021
Slack remind generator

slack-reminder Slack remind generator slack-reminder.mov Why Slack's /reminder is very hard to use. Required Go 1.16 ~ Installation $ go install githu

skanehira 27 Nov 6, 2021
Library for testing interactive Slack applications.

Slackster Library for testing interactive Slack applications. Mock Slack API: user info, post and update message, publish view. Testing Slack UI in th

 Юла 12 Oct 1, 2021
Send Slack reports of pull requests pending review

preport Born out of a desire to get pull requests reviewed faster without having to send manual reminders, preport generates reports of GitLab pull re

Emile 1 Nov 23, 2021
Notify Slack about KPIs test

This action is a part of GitHub Actions Library created by rtCamp. Slack Notify - GitHub Action A GitHub Action to send a message to a Slack channel.

pipl.com 0 Nov 28, 2021
Telegram Bot Framework for Go

Margelet Telegram Bot Framework for Go is based on telegram-bot-api It uses Redis to store it's states, configs and so on. Any low-level interactions

Gleb Sinyavskiy 66 Nov 15, 2021
Telebot is a Telegram bot framework in Go.

Telebot "I never knew creating Telegram bots could be so sexy!" go get -u gopkg.in/tucnak/telebot.v2 Overview Getting Started Poller Commands Files Se

Ian P Badtrousers 2.2k Dec 1, 2021
Parr(B)ot is a Telegram bot framework based on top of Echotron

Parr(B)ot framework A just born Telegram bot framework in Go based on top of the echotron library. You can call it Parrot, Parr-Bot, Parrot Bot, is up

DazFather 1 Nov 21, 2021
The modern cryptocurrency trading bot written in Go.

bbgo A trading bot framework written in Go. The name bbgo comes from the BB8 bot in the Star Wars movie. aka Buy BitCoin Go! Current Status Features E

Yo-An Lin 375 Nov 25, 2021