Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update for MsgOptions #9

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7c9c35a
Update for MsgOptions
Oct 15, 2018
00884b2
Add options to bot.New
Oct 15, 2018
e86ac2c
Fix ReplyWithAttachments
Oct 18, 2018
caf0fff
Switch to GrantStreetGroup/slack
Oct 31, 2019
ab665ad
Revert "Switch to GrantStreetGroup/slack"
Nov 5, 2019
aad87f3
Update to slack-go/slack and add Go Mod support
Aug 31, 2020
8edef09
Merge pull request #1 in GITHUB/go-slackbot from quickfix1 to master
Oct 5, 2020
8494797
Add some debug
Dec 11, 2020
0990f91
More Debug
Dec 11, 2020
70b7b7c
Fix to handle multiline
Dec 11, 2020
3e34122
Removew debug
Dec 11, 2020
eb46c8d
Merge pull request #1 from GrantStreetGroup/debug
Dec 11, 2020
fa4f219
Add Reaction Matcher to Router
Jul 26, 2022
3ccd037
Add bot.ReactTo for reactions
Jul 26, 2022
17113e2
Add ReactTo to SimplerRouter
Jul 26, 2022
6165f28
Handle null msg in TypeMatcher
Oct 18, 2022
06b8ca0
Fix RegExp Matcher
Oct 18, 2022
41e6747
Add debug suport
Oct 18, 2022
bbd136e
Fix context markers
Oct 27, 2022
866ab6b
Add ReactionHandler Support
Oct 27, 2022
fe3059e
Merge pull request #2 from GrantStreetGroup/ReactionMatcher
Oct 27, 2022
d6cb446
Update
Jul 15, 2024
b1e78ee
Skip MessageReplied subtypes
Jul 15, 2024
1084ca4
remove litter.dump
Jul 15, 2024
e4c52dd
Ignore thread broadcast messages
Jul 15, 2024
14242d4
Ignore Bot Messages
Jul 15, 2024
bf47328
Merge pull request #3 from GrantStreetGroup/Debug2
Jul 15, 2024
87a6711
Initial checkin
Nov 17, 2022
c89b407
Update module name
Oct 15, 2024
b505aa9
Update socketmode logs
Oct 15, 2024
df8b75c
Add debug eventtype
Oct 15, 2024
54216b3
Add missing reaction item details to socketmode
Oct 23, 2024
83b68d1
Merge pull request #1 from ArcticSnowman/EventsAPI
ArcticSnowman Oct 23, 2024
cf8cb50
Merge pull request #4 from ArcticSnowman/master
mattdgsg Oct 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## go-slackbot - Build Slackbots in Go

The go-slackbot project hopes to ease development of Slack bots by adding helpful
methods and a mux-router style interface to the github.com/nlopes/slack package.
methods and a mux-router style interface to the github.com/slack-go/slack package.

Incoming Slack RTM events are mapped to a handler in the following form:

Expand All @@ -19,18 +19,18 @@ In addition to several useful functions in the utils.go file, the slackbot.Bot s
attachment := slack.Attachment{
Pretext: "We bring bots to life. :sunglasses: :thumbsup:",
Title: "Host, deploy and share your bot in seconds.",
TitleLink: "https:beepboophq.com/",
TitleLink: "https:GrantStreetGroup.com/",
Text: txt,
Fallback: txt,
ImageURL: "https:storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png",
ImageURL: "https:storage.googleapis.com/GrantStreetGroup/_assets/bot-1.22f6fb.png",
Color: "#7CD197",
}

attachments := []slack.Attachment{attachment}
bot.ReplyWithAttachments(evt, attachments, slackbot.WithTyping)
}

But wait, there's more! Well, until there's more, the slackbot package exposes github.com/nlopes/slack RTM and Client objects enabling a consumer to interact with the lower level package directly:
But wait, there's more! Well, until there's more, the slackbot package exposes github.com/slack-go/slack RTM and Client objects enabling a consumer to interact with the lower level package directly:

func HowAreYouHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.MessageEvent) {
bot.RTM.NewOutgoingMessage("Hello", "#random")
Expand All @@ -39,5 +39,24 @@ But wait, there's more! Well, until there's more, the slackbot package exposes g

If you want to kick the tires, we would love feedback. Check out these two examples:

- [simple.go](https://github.com/BeepBoopHQ/go-slackbot/blob/master/examples/simple/simple.go)
- [wit.go](https://github.com/BeepBoopHQ/go-slackbot/blob/master/examples/wit/wit.go).
- [simple.go](https://github.com/GrantStreetGroup/go-slackbot/blob/master/examples/simple/simple.go)
- [wit.go](https://github.com/GrantStreetGroup/go-slackbot/blob/master/examples/wit/wit.go).

## Events API SocketMode support

To enable SocketMode support over RTM you need an AppLevelToken for your bot app

bot := slackbot.New(token, slack.OptionAppLevelToken(app), ...options)
bot.EventMode = "Socket"
...



## ReactionEvent

Bot can respond to the use of reactions in a slack message stream.

bot.ReactTo("reaction").ReactionHandler(handlerfunc)



130 changes: 95 additions & 35 deletions bot.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
// Package slackbot hopes to ease development of Slack bots by adding helpful
// methods and a mux-router style interface to the github.com/nlopes/slack package.
// methods and a mux-router style interface to the github.com/slack-go/slack package.
//
// Incoming Slack RTM events are mapped to a handler in the following form:
// bot.Hear("(?i)how are you(.*)").MessageHandler(HowAreYouHandler)
//
// bot.Hear("(?i)how are you(.*)").MessageHandler(HowAreYouHandler)
//
// The package adds Reply and ReplyWithAttachments methods:
//
// func HowAreYouHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.MessageEvent) {
// bot.Reply(evt, "A bit tired. You get it? A bit?", slackbot.WithTyping)
// bot.Reply(evt, "A bit tired. You get it? A bit?", slackbot.WithTyping)
// }
//
// func HowAreYouAttachmentsHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.MessageEvent) {
// txt := "Beep Beep Boop is a ridiculously simple hosting platform for your Slackbots."
// attachment := slack.Attachment{
// Pretext: "We bring bots to life. :sunglasses: :thumbsup:",
// Title: "Host, deploy and share your bot in seconds.",
// TitleLink: "https://beepboophq.com/",
// Text: txt,
// Fallback: txt,
// ImageURL: "https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png",
// Color: "#7CD197",
// }
// txt := "Beep Beep Boop is a ridiculously simple hosting platform for your Slackbots."
// attachment := slack.Attachment{
// Pretext: "We bring bots to life. :sunglasses: :thumbsup:",
// Title: "Host, deploy and share your bot in seconds.",
// TitleLink: "https://GrantStreetGroup.com/",
// Text: txt,
// Fallback: txt,
// ImageURL: "https://storage.googleapis.com/GrantStreetGroup/_assets/bot-1.22f6fb.png",
// Color: "#7CD197",
// }
//
// attachments := []slack.Attachment{attachment}
// bot.ReplyWithAttachments(evt, attachments, slackbot.WithTyping)
// }
//
// The slackbot package exposes github.com/nlopes/slack RTM and Client objects
// The slackbot package exposes github.com/slack-go/slack RTM and Client objects
// enabling a consumer to interact with the lower level package directly:
// func HowAreYouHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.MessageEvent) {
// bot.RTM.NewOutgoingMessage("Hello", "#random")
// }
//
// func HowAreYouHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.MessageEvent) {
// bot.RTM.NewOutgoingMessage("Hello", "#random")
// }
//
// Project home and samples: https://github.com/BeepBoopHQ/go-slackbot
// Project home and samples: https://github.com/GrantStreetGroup/go-slackbot
package slackbot

import (
Expand All @@ -41,19 +43,21 @@ import (

"golang.org/x/net/context"

"github.com/nlopes/slack"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
)

const (
WithTyping bool = true
WithoutTyping bool = false

maxTypingSleepMs time.Duration = time.Millisecond * 2000
maxTypingSleep time.Duration = time.Millisecond * 2000
)

// New constructs a new Bot using the slackToken to authorize against the Slack service.
func New(slackToken string) *Bot {
b := &Bot{Client: slack.New(slackToken)}
func New(slackToken string, options ...slack.Option) *Bot {
b := &Bot{Client: slack.New(slackToken, options...)}
b.EventMode = "RTM"
return b
}

Expand All @@ -64,35 +68,87 @@ type Bot struct {
// Slack UserID of the bot UserID
botUserID string
// Slack API
Client *slack.Client
RTM *slack.RTM
Client *slack.Client
RTM *slack.RTM
Socket *socketmode.Client
Debug bool
EventMode string
}

// Run listens for incoming slack RTM events, matching them to an appropriate handler.
// Run listeners for incoming slack events via RTM or Socketmode, matching them to an appropriate handler.
func (b *Bot) Run() {

if b.EventMode == "RTM" {
b.RunRTM()
} else {
b.RunSocketMode()
}
}

// Run listeners for incoming slack RTM events, matching them to an appropriate handler.
func (b *Bot) RunRTM() {
b.RTM = b.Client.NewRTM()
go b.RTM.ManageConnection()
for {
select {
case msg := <-b.RTM.IncomingEvents:
ctx := context.Background()
ctx = AddBotToContext(ctx, b)
if b.Debug {
ctx = SetDebug(ctx)
}
switch ev := msg.Data.(type) {
case *slack.ConnectedEvent:
fmt.Printf("Connected: %#v\n", ev.Info.User)
b.setBotID(ev.Info.User.ID)
b.SetBotID(ev.Info.User.ID)
case *slack.MessageEvent:
// ignore messages from the current user, the bot user
if b.botUserID == ev.User {
continue
}
// ignore message_replies subtypes as we will get the full message event.
if ev.SubType == slack.MsgSubTypeMessageReplied {
continue
}

// Ignore thread broadcast messages
if ev.SubType == slack.MsgSubTypeThreadBroadcast {
continue
}

// Ignore bot_messages
if ev.SubType == slack.MsgSubTypeBotMessage {
continue
}
ctx = AddMessageToContext(ctx, ev)
var match RouteMatch
if matched, ctx := b.Match(ctx, &match); matched {
match.Handler(ctx)
}

case *slack.ReactionAddedEvent:
// Handle reaction events
if b.botUserID == ev.User {
continue
}

ctx = AddReactionAddedToContext(ctx, ev)
var match RouteMatch
if matched, ctx := b.Match(ctx, &match); matched {
match.Handler(ctx)
}
case *slack.ReactionRemovedEvent:
// Handle reaction events
if b.botUserID == ev.User {
continue
}

ctx = AddReactionRemovedToContext(ctx, ev)
var match RouteMatch
if matched, ctx := b.Match(ctx, &match); matched {
match.Handler(ctx)
}

case *slack.RTMError:
fmt.Printf("Error: %s\n", ev.Error())

Expand All @@ -110,27 +166,31 @@ func (b *Bot) Run() {

// Reply replies to a message event with a simple message.
func (b *Bot) Reply(evt *slack.MessageEvent, msg string, typing bool) {
if typing {
b.Type(evt, msg)
if b.EventMode == "RTM" {
if typing {
b.Type(evt, msg)
}
b.RTM.SendMessage(b.RTM.NewOutgoingMessage(msg, evt.Channel))
} else {
b.Client.PostMessage(evt.Channel, slack.MsgOptionText(msg, true), slack.MsgOptionAsUser(true))
}
b.RTM.SendMessage(b.RTM.NewOutgoingMessage(msg, evt.Channel))
}

// ReplyWithAttachments replys to a message event with a Slack Attachments message.
func (b *Bot) ReplyWithAttachments(evt *slack.MessageEvent, attachments []slack.Attachment, typing bool) {
params := slack.PostMessageParameters{AsUser: true}
params.Attachments = attachments
// params := slack.PostMessageParameters{AsUser: true}
// params.Attachments = attachments

b.Client.PostMessage(evt.Msg.Channel, "", params)
b.Client.PostMessage(evt.Msg.Channel, slack.MsgOptionAttachments(attachments...), slack.MsgOptionAsUser(true))
}

// Type sends a typing message and simulates delay (max 2000ms) based on message size.
func (b *Bot) Type(evt *slack.MessageEvent, msg interface{}) {
msgLen := msgLen(msg)

sleepDuration := time.Minute * time.Duration(msgLen) / 3000
if sleepDuration > maxTypingSleepMs {
sleepDuration = maxTypingSleepMs
if sleepDuration > maxTypingSleep {
sleepDuration = maxTypingSleep
}

b.RTM.SendMessage(b.RTM.NewTypingMessage(evt.Channel))
Expand All @@ -142,7 +202,7 @@ func (b *Bot) BotUserID() string {
return b.botUserID
}

func (b *Bot) setBotID(ID string) {
func (b *Bot) SetBotID(ID string) {
b.botUserID = ID
}

Expand Down
52 changes: 49 additions & 3 deletions context.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package slackbot

import (
"github.com/nlopes/slack"
"github.com/slack-go/slack"
"golang.org/x/net/context"
)

const (
BOT_CONTEXT = "__BOT_CONTEXT__"
MESSAGE_CONTEXT = "__MESSAGE_CONTEXT__"
BOT_CONTEXT = "__BOT_CONTEXT__"
MESSAGE_CONTEXT = "__MESSAGE_CONTEXT__"
REACTION_CONTEXT = "__REACTION_CONTEXT__"
REACTION_EVENT = "__REACTION_EVENT__"
BOT_DEBUG = "__BOT_DEBUG__"
)

func BotFromContext(ctx context.Context) *Bot {
Expand All @@ -33,3 +36,46 @@ func MessageFromContext(ctx context.Context) *slack.MessageEvent {
func AddMessageToContext(ctx context.Context, msg *slack.MessageEvent) context.Context {
return context.WithValue(ctx, MESSAGE_CONTEXT, msg)
}

// AddReactionAddedToContext
func AddReactionAddedToContext(ctx context.Context, react *slack.ReactionAddedEvent) context.Context {
nctx := context.WithValue(ctx, REACTION_CONTEXT, "Added")
return context.WithValue(nctx, REACTION_EVENT, react)
}

// AddReactionAddedToContext
func AddReactionRemovedToContext(ctx context.Context, react *slack.ReactionRemovedEvent) context.Context {
nctx := context.WithValue(ctx, REACTION_CONTEXT, "Removed")
return context.WithValue(nctx, REACTION_EVENT, react)
}

func ReactionTypeFromContext(ctx context.Context) string {
if result, ok := ctx.Value(REACTION_CONTEXT).(string); ok {
return result
}
return ""
}
func ReactionAddedFromContext(ctx context.Context) *slack.ReactionAddedEvent {
if result, ok := ctx.Value(REACTION_EVENT).(*slack.ReactionAddedEvent); ok {
return result
}
return nil
}

func ReactionRemovedFromContext(ctx context.Context) *slack.ReactionRemovedEvent {
if result, ok := ctx.Value(REACTION_EVENT).(*slack.ReactionRemovedEvent); ok {
return result
}
return nil
}

func SetDebug(ctx context.Context) context.Context {
return context.WithValue(ctx, BOT_DEBUG, true)
}

func IsDebug(ctx context.Context) bool {
if result, ok := ctx.Value(BOT_DEBUG).(bool); ok {
return result
}
return false
}
8 changes: 4 additions & 4 deletions examples/simple/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (

"golang.org/x/net/context"

slackbot "github.com/BeepBoopHQ/go-slackbot"
"github.com/nlopes/slack"
slackbot "github.com/GrantStreetGroup/go-slackbot"
"github.com/slack-go/slack"
)

func main() {
Expand All @@ -32,10 +32,10 @@ func AttachmentsHandler(ctx context.Context, bot *slackbot.Bot, evt *slack.Messa
attachment := slack.Attachment{
Pretext: "We bring bots to life. :sunglasses: :thumbsup:",
Title: "Host, deploy and share your bot in seconds.",
TitleLink: "https://beepboophq.com/",
TitleLink: "https://GrantStreetGroup.com/",
Text: txt,
Fallback: txt,
ImageURL: "https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png",
ImageURL: "https://storage.googleapis.com/GrantStreetGroup/_assets/bot-1.22f6fb.png",
Color: "#7CD197",
}

Expand Down
4 changes: 2 additions & 2 deletions examples/wit/wit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (

"golang.org/x/net/context"

slackbot "github.com/BeepBoopHQ/go-slackbot"
slackbot "github.com/GrantStreetGroup/go-slackbot"
"github.com/chris-skud/go-wit"
"github.com/nlopes/slack"
"github.com/slack-go/slack"
)

func main() {
Expand Down
Loading