1646908560
IRC, Slack & Telegram bot written in Go using go-ircevent for IRC connectivity, nlopes/slack for Slack and Syfaro/telegram-bot-api for Telegram.
Please see the plugins repository for a complete list of plugins.
You can also write your own, it's really simple.
This project uses the new Go 1.11 modules if you have Go 1.11 installed, just clone the project and follow the instructions bellow, when you build Go will automatically download all dependencies.
To test the bot, use the debug console app.
go get github.com/go-chat-bot/bot
go build ./...
cd debug
go build
./debug
!help
to see the list of available commandsdebug/main.go
import listTo deploy your go-bot to Slack, you need to:
github.com/go-chat-bot/bot/slack
slack.Run(token)
Here is a full example reading the Slack token from the SLACK_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/slack"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
slack.Run(os.Getenv("SLACK_TOKEN"))
}
To deploy your own go-bot to IRC, you need to:
github.com/go-chat-bot/bot/irc
irc.Run(config)
Here is a full example:
package main
import (
"github.com/go-chat-bot/bot/irc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
"os"
"strings"
)
func main() {
irc.Run(&irc.Config{
Server: os.Getenv("IRC_SERVER"),
Channels: strings.Split(os.Getenv("IRC_CHANNELS"), ","),
User: os.Getenv("IRC_USER"),
Nick: os.Getenv("IRC_NICK"),
Password: os.Getenv("IRC_PASSWORD"),
UseTLS: true,
Debug: os.Getenv("DEBUG") != "",})
}
To join channels with passwords just put the password after the channel name separated by a space:
Channels: []string{"#mychannel mypassword", "#go-bot"}
To deploy your go-bot to Telegram, you need to:
github.com/go-chat-bot/bot/telegram
telegram.Run(token, debug)
Here is a full example reading the telegram token from the TELEGRAM_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/telegram"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
telegram.Run(os.Getenv("TELEGRAM_TOKEN"), os.Getenv("DEBUG") != "")
}
To deploy your go-bot to Rocket.chat, you need to:
github.com/go-chat-bot/bot/rocket
rocket.Run(config)
Here is a full example:
package main
import (
"os"
"github.com/go-chat-bot/bot/rocket"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
config := &rocket.Config{
Server: os.Getenv("ROCKET_SERVER"),
Port: os.Getenv("ROCKET_PORT"),
User: os.Getenv("ROCKET_USER"),
Email: os.Getenv("ROCKET_EMAIL"),
Password: os.Getenv("ROCKET_PASSWORD"),
UseTLS: false,
Debug: os.Getenv("DEBUG") != "",
}
rocket.Run(config)
}
To deploy your go-bot to Google Chat (also known as Hangouts Chat, not plain Hangouts) you will first need to follow documentation to setup pub/sub project in Google Cloud. This will enable your bot to receive messages even when it is behind a firewall.
Condensed, the steps you will need to take are as follows:
Config.SubscriptionName should be unique for each environment or you'll not process messages correctly. If you encounter issues make sure your credentials are correct and permissions for topics/queues are set up correctly.
Config.WelcomeMessage is sent each time the bot joins a new room or private chat.
Full example is here:
package main
import (
"os"
"github.com/go-chat-bot/bot/google-chat"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
googlechat.Run(&googlechat.Config{
PubSubProject: os.Getenv("HANGOUTS_PROJECT"),
TopicName: os.Getenv("HANGOUTS_TOPIC"),
SubscriptionName: os.Getenv("HANGOUTS_SUB"),
WelcomeMessage: os.Getenv("HANGOUTS_WELCOME"),
}
To see an example project on how to deploy your bot, please see my own configuration:
Author: Go-chat-bot
Source Code: https://github.com/go-chat-bot/bot
License: MIT License
1555901576
In this article we are going to focus on building a basic sidebar, and the main chat window inside our chat shell. See below.
Chat shell with a fixed width sidebar and expanded chat window
This is the second article in this series. You can check out the previous article for setting up the shell OR you can just check out the chat-shell branch from the following repository.
https://github.com/lyraddigital/flexbox-chat-app.git
Open up the chat.html file. You should have the following HTML.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Chat App</title>
<link rel="stylesheet" type="text/css" media="screen" href="css/chat.css" />
</head>
<body>
<div id="chat-container">
</div>
</body>
</html>
Now inside of the chat-container div add the following HTML.
<div id="side-bar">
</div>
<div id="chat-window">
</div>
Now let’s also add the following CSS under the #chat-container selector in the chat.css file.
#side-bar {
background: #0048AA;
border-radius: 10px 0 0 10px;
}
#chat-window {
background: #999;
border-radius: 0 10px 10px 0;
}
Now reload the page. You should see the following:-
So what happened? Where is our sidebar and where is our chat window? I expected to see a blue side bar and a grey chat window, but it’s no where to be found. Well it’s all good. This is because we have no content inside of either element, so it can be 0 pixels wide.
Sizing Flex Items
So now that we know that our items are 0 pixels wide, let’s attempt to size them. We’ll attempt to try this first using explicit widths.
Add the following width property to the #side-bar rule, then reload the page.
width: 275px;
Hmm. Same result. It’s still a blank shell. Oh wait I have to make sure the height is 100% too. So we better do that too. Once again add the following property to the #side-bar rule, then reload the page.
height: 100%;
So now we have our sidebar that has grown to be exactly 275 pixels wide, and is 100% high. So that’s it. We’re done right? Wrong. Let me ask you a question. How big is the chat window? Let’s test that by adding some text to it. Try this yourself just add some text. You should see something similar to this.
So as you can see the chat window is only as big as the text that’s inside of it, and it is not next to the side bar. And this makes sense because up until now the chat shell is not a flex container, and just a regular block level element.
So let’s make our chat shell a flex container. Set the following display property for the #chat-window selector. Then reload the page.
display: flex;
So as you can see by the above illustration, we can see it’s now next to the side bar, and not below it. But as you can see currently it’s only as wide as the text that’s inside of it.
But we want it to take up the remaining space of the chat shell. Well we know how to do this, as we did it in the previous article. Set the flex-grow property to 1 on the #chat-window selector. Basically copy and paste the property below and reload the page.
flex-grow: 1;
So now we have the chat window taking up the remaining space of the chat shell. Next, let’s remove the background property, and also remove all text inside the chat-window div if any still exists. You should now see the result below.
But are we done? Technically yes, but before we move on, let’s improve things a little bit.
Understanding the default alignment
If you remember, before we had defined our chat shell to be a flex container, we had to make sure we set the height of the side bar to be 100%. Otherwise it was 0 pixels high, and as a result nothing was displayed. With that said, try removing the height property from the #side-bar selector and see what happens when you reload the page. Yes that’s right, it still works. The height of the sidebar is still 100% high.
So what happened here? Why do we no longer have to worry about setting the height to 100%? Well this is one of the cool things Flexbox gives you for free. By default every flex item will stretch vertically to fill in the entire height of the flex container. We can in fact change this behaviour, and we will see how this is done in a future article.
Setting the size of the side bar properly
So another feature of Flexbox is being able to set the size of a flex item by using the flex-basis property. The flex-basis property allows you to specify an initial size of a flex item, before any growing or shrinking takes place. We’ll understand more about this in an upcoming article.
For now I just want you to understand one important thing. And that is using width to specify the size of the sidebar is not a good idea. Let’s see why.
Say that potentially, if the screen is mobile we want the side bar to now appear across the top of the chat shell, acting like a top bar instead. We can do this by changing the direction flex items can flex inside a flex container. For example, add the following CSS to the #chat-container selector. Then reload the page.
flex-direction: column;
So as you can see we are back to a blank shell. So firstly let’s understand what we actually did here. By setting the flex-direction property to column, we changed the direction of how the flex items flex. By default flex items will flex from left to right. However when we set flex-direction to column, it changes this behaviour forcing flex items to flex from top to bottom instead. On top of this, when the direction of flex changes, the sizing and alignment of flex items changes as well.
When flexing from left to right, we get a height of 100% for free as already mentioned, and then we made sure the side bar was set to be 275 pixels wide, by setting the width property.
However now that we a flexing from top to bottom, the width of the flex item by default would be 100% wide, and you would need to specify the height instead. So try this. Add the following property to the #side-bar selector to set the height of the side bar. Then reload the page.
height: 275px;
Now we are seeing the side bar again, as we gave it a fixed height too. But we still have that fixed width. That’s not what we wanted. We want the side bar (ie our new top bar) here to now be 100% wide. Comment out the width for a moment and reload the page again.
So now we were able to move our side bar so it appears on top instead, acting like a top bar. Which as previously mentioned might be suited for mobile device widths. But to do this we had to swap the value of width to be the value of height. Wouldn’t it be great if this size was preserved regardless of which direction our items are flexing.
Try this, remove all widths and height properties from the #side-bar selector and write the following instead. Then reload the page.
flex-basis: 275px;
As you can see we get the same result. Now remove the flex-direction property from the #chat-container selector. Then once again reload the page.
Once again we are back to our final output. But now we also have the flexibility to easily change the side bar to be a top bar if we need to, by just changing the direction items can flow. Regardless of the direction of flex, the size of our side bar / top bar is preserved.
Conclusion
Ok so once again we didn’t build much, but we did cover a lot of concepts about Flexbox around sizing.
#css #programming #webdev
1655540400
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.
This project uses the new Go 1.11 modules if you have Go 1.11 installed, just clone the project and follow the instructions bellow, when you build Go will automatically download all dependencies.
To test the bot, use the debug console app.
go get github.com/go-chat-bot/bot
go build ./...
cd debug
go build
./debug
!help
to see the list of available commandsdebug/main.go
import listTo deploy your go-bot to Slack, you need to:
github.com/go-chat-bot/bot/slack
slack.Run(token)
Here is a full example reading the Slack token from the SLACK_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/slack"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
slack.Run(os.Getenv("SLACK_TOKEN"))
}
To deploy your own go-bot to IRC, you need to:
github.com/go-chat-bot/bot/irc
irc.Run(config)
Here is a full example:
package main
import (
"github.com/go-chat-bot/bot/irc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
"os"
"strings"
)
func main() {
irc.Run(&irc.Config{
Server: os.Getenv("IRC_SERVER"),
Channels: strings.Split(os.Getenv("IRC_CHANNELS"), ","),
User: os.Getenv("IRC_USER"),
Nick: os.Getenv("IRC_NICK"),
Password: os.Getenv("IRC_PASSWORD"),
UseTLS: true,
Debug: os.Getenv("DEBUG") != "",})
}
To join channels with passwords just put the password after the channel name separated by a space:
Channels: []string{"#mychannel mypassword", "#go-bot"}
To deploy your go-bot to Telegram, you need to:
github.com/go-chat-bot/bot/telegram
telegram.Run(token, debug)
Here is a full example reading the telegram token from the TELEGRAM_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/telegram"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
telegram.Run(os.Getenv("TELEGRAM_TOKEN"), os.Getenv("DEBUG") != "")
}
To deploy your go-bot to Rocket.chat, you need to:
github.com/go-chat-bot/bot/rocket
rocket.Run(config)
Here is a full example:
package main
import (
"os"
"github.com/go-chat-bot/bot/rocket"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
config := &rocket.Config{
Server: os.Getenv("ROCKET_SERVER"),
Port: os.Getenv("ROCKET_PORT"),
User: os.Getenv("ROCKET_USER"),
Email: os.Getenv("ROCKET_EMAIL"),
Password: os.Getenv("ROCKET_PASSWORD"),
UseTLS: false,
Debug: os.Getenv("DEBUG") != "",
}
rocket.Run(config)
}
To deploy your go-bot to Google Chat (also known as Hangouts Chat, not plain Hangouts) you will first need to follow documentation to setup pub/sub project in Google Cloud. This will enable your bot to receive messages even when it is behind a firewall.
Condensed, the steps you will need to take are as follows:
Config.SubscriptionName should be unique for each environment or you'll not process messages correctly. If you encounter issues make sure your credentials are correct and permissions for topics/queues are set up correctly.
Config.WelcomeMessage is sent each time the bot joins a new room or private chat.
Full example is here:
package main
import (
"os"
"github.com/go-chat-bot/bot/google-chat"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
googlechat.Run(&googlechat.Config{
PubSubProject: os.Getenv("HANGOUTS_PROJECT"),
TopicName: os.Getenv("HANGOUTS_TOPIC"),
SubscriptionName: os.Getenv("HANGOUTS_SUB"),
WelcomeMessage: os.Getenv("HANGOUTS_WELCOME"),
}
To see an example project on how to deploy your bot, please see my own configuration:
Please see the plugins repository for a complete list of plugins.
You can also write your own, it's really simple.
Author: go-chat-bot
Source Code: https://github.com/go-chat-bot/bot
License: MIT license
1646908560
IRC, Slack & Telegram bot written in Go using go-ircevent for IRC connectivity, nlopes/slack for Slack and Syfaro/telegram-bot-api for Telegram.
Please see the plugins repository for a complete list of plugins.
You can also write your own, it's really simple.
This project uses the new Go 1.11 modules if you have Go 1.11 installed, just clone the project and follow the instructions bellow, when you build Go will automatically download all dependencies.
To test the bot, use the debug console app.
go get github.com/go-chat-bot/bot
go build ./...
cd debug
go build
./debug
!help
to see the list of available commandsdebug/main.go
import listTo deploy your go-bot to Slack, you need to:
github.com/go-chat-bot/bot/slack
slack.Run(token)
Here is a full example reading the Slack token from the SLACK_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/slack"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
slack.Run(os.Getenv("SLACK_TOKEN"))
}
To deploy your own go-bot to IRC, you need to:
github.com/go-chat-bot/bot/irc
irc.Run(config)
Here is a full example:
package main
import (
"github.com/go-chat-bot/bot/irc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
"os"
"strings"
)
func main() {
irc.Run(&irc.Config{
Server: os.Getenv("IRC_SERVER"),
Channels: strings.Split(os.Getenv("IRC_CHANNELS"), ","),
User: os.Getenv("IRC_USER"),
Nick: os.Getenv("IRC_NICK"),
Password: os.Getenv("IRC_PASSWORD"),
UseTLS: true,
Debug: os.Getenv("DEBUG") != "",})
}
To join channels with passwords just put the password after the channel name separated by a space:
Channels: []string{"#mychannel mypassword", "#go-bot"}
To deploy your go-bot to Telegram, you need to:
github.com/go-chat-bot/bot/telegram
telegram.Run(token, debug)
Here is a full example reading the telegram token from the TELEGRAM_TOKEN
env var:
package main
import (
"os"
"github.com/go-chat-bot/bot/telegram"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
// Import all the commands you wish to use
)
func main() {
telegram.Run(os.Getenv("TELEGRAM_TOKEN"), os.Getenv("DEBUG") != "")
}
To deploy your go-bot to Rocket.chat, you need to:
github.com/go-chat-bot/bot/rocket
rocket.Run(config)
Here is a full example:
package main
import (
"os"
"github.com/go-chat-bot/bot/rocket"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
config := &rocket.Config{
Server: os.Getenv("ROCKET_SERVER"),
Port: os.Getenv("ROCKET_PORT"),
User: os.Getenv("ROCKET_USER"),
Email: os.Getenv("ROCKET_EMAIL"),
Password: os.Getenv("ROCKET_PASSWORD"),
UseTLS: false,
Debug: os.Getenv("DEBUG") != "",
}
rocket.Run(config)
}
To deploy your go-bot to Google Chat (also known as Hangouts Chat, not plain Hangouts) you will first need to follow documentation to setup pub/sub project in Google Cloud. This will enable your bot to receive messages even when it is behind a firewall.
Condensed, the steps you will need to take are as follows:
Config.SubscriptionName should be unique for each environment or you'll not process messages correctly. If you encounter issues make sure your credentials are correct and permissions for topics/queues are set up correctly.
Config.WelcomeMessage is sent each time the bot joins a new room or private chat.
Full example is here:
package main
import (
"os"
"github.com/go-chat-bot/bot/google-chat"
_ "github.com/go-chat-bot/plugins/godoc"
_ "github.com/go-chat-bot/plugins/catfacts"
_ "github.com/go-chat-bot/plugins/catgif"
_ "github.com/go-chat-bot/plugins/chucknorris"
)
func main() {
googlechat.Run(&googlechat.Config{
PubSubProject: os.Getenv("HANGOUTS_PROJECT"),
TopicName: os.Getenv("HANGOUTS_TOPIC"),
SubscriptionName: os.Getenv("HANGOUTS_SUB"),
WelcomeMessage: os.Getenv("HANGOUTS_WELCOME"),
}
To see an example project on how to deploy your bot, please see my own configuration:
Author: Go-chat-bot
Source Code: https://github.com/go-chat-bot/bot
License: MIT License
1652942940
Slack API in Go
This is the original Slack library for Go created by Norberto Lopes, transferred to a GitHub organization.
You can also chat with us on the #slack-go, #slack-go-ja Slack channel on the Gophers Slack.
This library supports most if not all of the api.slack.com
REST calls, as well as the Real-Time Messaging protocol over websocket, in a fully managed way.
There is currently no major version released. Therefore, minor version releases may include backward incompatible changes.
See CHANGELOG.md or Releases for more information about the changes.
$ go get -u github.com/slack-go/slack
import (
"fmt"
"github.com/slack-go/slack"
)
func main() {
api := slack.New("YOUR_TOKEN_HERE")
// If you set debugging, it will log all requests to the console
// Useful when encountering issues
// slack.New("YOUR_TOKEN_HERE", slack.OptionDebug(true))
groups, err := api.GetUserGroups(false)
if err != nil {
fmt.Printf("%s\n", err)
return
}
for _, group := range groups {
fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name)
}
}
import (
"fmt"
"github.com/slack-go/slack"
)
func main() {
api := slack.New("YOUR_TOKEN_HERE")
user, err := api.GetUserInfo("U023BECGF")
if err != nil {
fmt.Printf("%s\n", err)
return
}
fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email)
}
See https://github.com/slack-go/slack/blob/master/examples/socketmode/socketmode.go
As mentioned in https://api.slack.com/rtm - for most applications, Socket Mode is a better way to communicate with Slack.
See https://github.com/slack-go/slack/blob/master/examples/websocket/websocket.go
See https://github.com/slack-go/slack/blob/master/examples/eventsapi/events.go
You are more than welcome to contribute to this project. Fork and make a Pull Request, or create an Issue if you see any problem.
Before making any Pull Request please run the following:
make pr-prep
This will check/update code formatting, linting and then run all tests
Author: Slack-go
Source Code: https://github.com/slack-go/slack
License: BSD-2-Clause license
1655547868
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, assign commands to them and extract parameters.
context.Context
help
commandcommander
github.com/shomali11/commanderslack
github.com/slack-go/slackInstall
go get github.com/shomali11/slacker
Preparing your Slack App
To use Slacker you'll need to create a Slack App, either manually or with an app manifest. The app manifest feature is easier, but is a beta feature from Slack and thus may break/change without much notice.
Slacker works by communicating with the Slack Events API using the Socket Mode connection protocol.
To get started, you must have or create a Slack App and enable Socket Mode
, which will generate your app token (SLACK_APP_TOKEN
in the examples) that will be needed to authenticate.
Additionally, you need to subscribe to events for your bot to respond to under the Event Subscriptions
section. Common event subscriptions for bots include app_mention
or message.im
.
After setting up your subscriptions, add scopes necessary to your bot in the OAuth & Permissions
. The following scopes are recommended for getting started, though you may need to add/remove scopes depending on your bots purpose:
app_mentions:read
channels:history
chat:write
groups:history
im:history
mpim:history
Once you've selected your scopes install your app to the workspace and navigate back to the OAuth & Permissions
section. Here you can retrieve yor bot's OAuth token (SLACK_BOT_TOKEN
in the examples) from the top of the page.
With both tokens in hand, you can now proceed with the examples below.
Slack App Manifests make it easy to share a app configurations. We provide a simple manifest that should work with all the examples provided below.
The manifest provided will send all messages in channels your bot is in to the bot (including DMs) and not just ones that actually mention them in the message.
If you wish to only have your bot respond to messages they are directly messaged in, you will need to add the app_mentions:read
scope, and remove:
im:history
# single-person dmmpim:history
# multi-person dmchannels:history
# public channelsgroups:history
# private channelsYou'll also need to adjust the event subscriptions, adding app_mention
and removing:
message.channels
message.groups
message.im
message.mpim
Examples
Defining a command using slacker
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("pong")
},
}
bot.Command("ping", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Defining a command with an optional description and example. The handler replies to a thread.
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Ping!",
Example: "ping",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("pong", slacker.WithThreadReply(true))
},
}
bot.Command("ping", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Defining a command with a parameter
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Echo a word!",
Example: "echo hello",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.Param("word")
response.Reply(word)
},
}
bot.Command("echo <word>", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Defining a command with two parameters. Parsing one as a string and the other as an integer. (The second parameter is the default value in case no parameter was passed or could not parse the value)
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Repeat a word a number of times!",
Example: "repeat hello 10",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.StringParam("word", "Hello!")
number := request.IntegerParam("number", 1)
for i := 0; i < number; i++ {
response.Reply(word)
}
},
}
bot.Command("repeat <word> <number>", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Defines two commands that display sending errors to the Slack channel. One that replies as a new message. The other replies to the thread.
package main
import (
"context"
"errors"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
messageReplyDefinition := &slacker.CommandDefinition{
Description: "Tests errors in new messages",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.ReportError(errors.New("Oops!"))
},
}
threadReplyDefinition := &slacker.CommandDefinition{
Description: "Tests errors in threads",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.ReportError(errors.New("Oops!"), slacker.WithThreadError(true))
},
}
bot.Command("message", messageReplyDefinition)
bot.Command("thread", threadReplyDefinition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Showcasing the ability to access the github.com/slack-go/slack API and upload a file
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Upload a word!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.Param("word")
client := botCtx.Client()
ev := botCtx.Event()
if ev.Channel != "" {
client.PostMessage(ev.Channel, slack.MsgOptionText("Uploading file ...", false))
_, err := client.UploadFile(slack.FileUploadParameters{Content: word, Channels: []string{ev.Channel}})
if err != nil {
fmt.Printf("Error encountered when uploading file: %+v\n", err)
}
}
},
}
bot.Command("upload <word>", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Showcasing the ability to leverage context.Context
to add a timeout
package main
import (
"context"
"errors"
"log"
"os"
"time"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Process!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
timedContext, cancel := context.WithTimeout(botCtx.Context(), time.Second)
defer cancel()
select {
case <-timedContext.Done():
response.ReportError(errors.New("timed out"))
case <-time.After(time.Minute):
response.Reply("Processing done!")
}
},
}
bot.Command("process", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Showcasing the ability to add attachments to a Reply
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Echo a word!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.Param("word")
attachments := []slack.Attachment{}
attachments = append(attachments, slack.Attachment{
Color: "red",
AuthorName: "Raed Shomali",
Title: "Attachment Title",
Text: "Attachment Text",
})
response.Reply(word, slacker.WithAttachments(attachments))
},
}
bot.Command("echo <word>", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Showcasing the ability to add blocks to a Reply
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Echo a word!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.Param("word")
attachments := []slack.Block{}
attachments = append(attachments, slack.NewContextBlock("1",
slack.NewTextBlockObject("mrkdwn", "Hi!", false, false)),
)
response.Reply(word, slacker.WithBlocks(attachments))
},
}
bot.Command("echo <word>", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Showcasing the ability to create custom responses via CustomResponse
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
)
const (
errorFormat = "> Custom Error: _%s_"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
bot.CustomResponse(NewCustomResponseWriter)
definition := &slacker.CommandDefinition{
Description: "Custom!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("custom")
response.ReportError(errors.New("oops"))
},
}
bot.Command("custom", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
// NewCustomResponseWriter creates a new ResponseWriter structure
func NewCustomResponseWriter(botCtx slacker.BotContext) slacker.ResponseWriter {
return &MyCustomResponseWriter{botCtx: botCtx}
}
// MyCustomResponseWriter a custom response writer
type MyCustomResponseWriter struct {
botCtx slacker.BotContext
}
// ReportError sends back a formatted error message to the channel where we received the event from
func (r *MyCustomResponseWriter) ReportError(err error, options ...slacker.ReportErrorOption) {
defaults := slacker.NewReportErrorDefaults(options...)
client := r.botCtx.Client()
event := r.botCtx.Event()
opts := []slack.MsgOption{
slack.MsgOptionText(fmt.Sprintf(errorFormat, err.Error()), false),
}
if defaults.ThreadResponse {
opts = append(opts, slack.MsgOptionTS(event.TimeStamp))
}
_, _, err = client.PostMessage(event.Channel, opts...)
if err != nil {
fmt.Println("failed to report error: %v", err)
}
}
// Reply send a attachments to the current channel with a message
func (r *MyCustomResponseWriter) Reply(message string, options ...slacker.ReplyOption) error {
defaults := slacker.NewReplyDefaults(options...)
client := r.botCtx.Client()
event := r.botCtx.Event()
if event == nil {
return fmt.Errorf("Unable to get message event details")
}
opts := []slack.MsgOption{
slack.MsgOptionText(message, false),
slack.MsgOptionAttachments(defaults.Attachments...),
slack.MsgOptionBlocks(defaults.Blocks...),
}
if defaults.ThreadResponse {
opts = append(opts, slack.MsgOptionTS(event.TimeStamp))
}
_, _, err := client.PostMessage(
event.Channel,
opts...,
)
return err
}
Showcasing the ability to toggle the slack Debug option via WithDebug
package main
import (
"context"
"github.com/shomali11/slacker"
"log"
"os"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
definition := &slacker.CommandDefinition{
Description: "Ping!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("pong")
},
}
bot.Command("ping", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Defining a command that can only be executed by authorized users
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
authorizedUsers := []string{"<User ID>"}
authorizedDefinition := &slacker.CommandDefinition{
Description: "Very secret stuff",
AuthorizationFunc: func(botCtx slacker.BotContext, request slacker.Request) bool {
return contains(authorizedUsers, botCtx.Event().User)
},
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("You are authorized!")
},
}
bot.Command("secret", authorizedDefinition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
func contains(list []string, element string) bool {
for _, value := range list {
if value == element {
return true
}
}
return false
}
Adding handlers to when the bot is connected, encounters an error and a default for when none of the commands match
package main
import (
"log"
"os"
"context"
"fmt"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
bot.Init(func() {
log.Println("Connected!")
})
bot.Err(func(err string) {
log.Println(err)
})
bot.DefaultCommand(func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("Say what?")
})
bot.DefaultEvent(func(event interface{}) {
fmt.Println(event)
})
definition := &slacker.CommandDefinition{
Description: "help!",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("Your own help function...")
},
}
bot.Help(definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Listening to the Commands Events being produced
package main
import (
"fmt"
"log"
"os"
"context"
"github.com/shomali11/slacker"
)
func printCommandEvents(analyticsChannel <-chan *slacker.CommandEvent) {
for event := range analyticsChannel {
fmt.Println("Command Events")
fmt.Println(event.Timestamp)
fmt.Println(event.Command)
fmt.Println(event.Parameters)
fmt.Println(event.Event)
fmt.Println()
}
}
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
go printCommandEvents(bot.CommandEvents())
bot.Command("ping", &slacker.CommandDefinition{
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("pong")
},
})
bot.Command("echo <word>", &slacker.CommandDefinition{
Description: "Echo a word!",
Example: "echo hello",
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
word := request.Param("word")
response.Reply(word)
},
})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Slack interaction example
package main
import (
"context"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"log"
"os"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))
bot.Interactive(func(s *slacker.Slacker, event *socketmode.Event, callback *slack.InteractionCallback) {
if callback.Type != slack.InteractionTypeBlockActions {
return
}
if len(callback.ActionCallback.BlockActions) != 1 {
return
}
action := callback.ActionCallback.BlockActions[0]
if action.BlockID != "mood-block" {
return
}
var text string
switch action.ActionID {
case "happy":
text = "I'm happy to hear you are happy!"
case "sad":
text = "I'm sorry to hear you are sad."
default:
text = "I don't understand your mood..."
}
_, _, _ = s.Client().PostMessage(callback.Channel.ID, slack.MsgOptionText(text, false),
slack.MsgOptionReplaceOriginal(callback.ResponseURL))
s.SocketMode().Ack(*event.Request)
})
definition := &slacker.CommandDefinition{
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
happyBtn := slack.NewButtonBlockElement("happy", "true", slack.NewTextBlockObject("plain_text", "Happy 🙂", true, false))
happyBtn.Style = "primary"
sadBtn := slack.NewButtonBlockElement("sad", "false", slack.NewTextBlockObject("plain_text", "Sad ☹️", true, false))
sadBtn.Style = "danger"
err := response.Reply("", slacker.WithBlocks([]slack.Block{
slack.NewSectionBlock(slack.NewTextBlockObject(slack.PlainTextType, "What is your mood today?", true, false), nil, nil),
slack.NewActionBlock("mood-block", happyBtn, sadBtn),
}))
if err != nil {
panic(err)
}
},
}
bot.Command("mood", definition)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Configure bot to process other bot events
package main
import (
"context"
"log"
"os"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(
os.Getenv("SLACK_BOT_TOKEN"),
os.Getenv("SLACK_APP_TOKEN"),
slacker.WithBotInteractionMode(slacker.BotInteractionModeIgnoreApp),
)
bot.Command("hello", &slacker.CommandDefinition{
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("hai!")
},
})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Override the default event input cleaning function (to sanitize the messages received by Slacker)
package main
import (
"context"
"log"
"os"
"fmt"
"strings"
"github.com/shomali11/slacker"
)
func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"), slacker.WithDebug(true))
bot.CleanEventInput(func(in string) string {
fmt.Println("My slack bot does not like backticks!")
return strings.ReplaceAll(in, "`", "")
})
bot.Command("my-command", &slacker.CommandDefinition{
Handler: func(botCtx slacker.BotContext, request slacker.Request, response slacker.ResponseWriter) {
response.Reply("it works!")
},
})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}
Contributing / Submitting an Issue
Please review our Contribution Guidelines if you have found an issue with Slacker or wish to contribute to the project.
Troubleshooting
There are a few common issues that can cause this:
Author: Shomali11
Source Code: https://github.com/shomali11/slacker
License: MIT license