Skip to content

Commit 8624ffd

Browse files
Merge pull request #5 from groupme/magnificrogue/go-twitter-bot
Add go based twitter bot example
2 parents a4349eb + 3fd3c66 commit 8624ffd

10 files changed

+685
-0
lines changed

go/twitterbot/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bin

go/twitterbot/Dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM golang:1.16-alpine as base
2+
WORKDIR /app
3+
RUN apk add --no-cache make
4+
5+
FROM base as builder
6+
COPY . ./
7+
RUN go mod download
8+
RUN make build
9+
RUN chmod +x /app/bin/main
10+
11+
FROM base as runtime
12+
13+
COPY --from=builder /app/bin/main .
14+
15+
CMD /app/main -tweet-query=${TWEET_QUERY} -groupme-bot-id=${GROUPME_BOT_ID} -twitter-app-key=${TWITTER_APP_KEY} -twitter-app-secret=${TWITTER_APP_SECRET}

go/twitterbot/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
build:
2+
go build -o bin/main cmd/main.go
3+
4+
run:
5+
go run cmd/main.go -tweet-query=${TWEET_QUERY} -groupme-bot-id=${GROUPME_BOT_ID} -twitter-app-key=${TWITTER_APP_KEY} -twitter-app-secret=${TWITTER_APP_SECRET}
6+
7+
container:
8+
docker build -t groupme/twitterbot .

go/twitterbot/README.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# What is it
2+
3+
It is a simple bot that attempts to read tweets from twitter, specified by the tweet-query passed, and write it to a given group by using a bot user.
4+
5+
6+
# How to use it
7+
To use this bot in either a docker container or locally, you'll need the following:
8+
9+
1) A query for tweets set in an environment variable TWEET_QUERY
10+
2) A groupMe bot ID set in GROUPME_BOT_ID
11+
3) A twitter app key and secret, set in TWITTER_APP_KEY and TWITTER_APP_SECRET respectively
12+
13+
## Locally
14+
1. Have go/make installed.
15+
2. $ make run # Run in your terminal
16+
17+
## In a container
18+
1. Ensure the environment variables above are set
19+
2. Run /app/main -tweet-query=${TWEET_QUERY} -groupme-bot-id=${GROUPME_BOT_ID} -twitter-app-key=${TWITTER_APP_KEY} -twitter-app-secret=${TWITTER_APP_SECRET}

go/twitterbot/bot_client.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package twitterbot
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net/http"
9+
10+
"github.com/Azure/go-autorest/autorest"
11+
)
12+
13+
// BotClient is a simple wrapper for writing messages on GroupMe
14+
type BotClient struct {
15+
botID string
16+
}
17+
18+
// NewBotClient returns a configured BotClient suitable for use
19+
func NewBotClient(botID string) (*BotClient, error) {
20+
if botID == "" {
21+
return nil, errors.New("Can't instantiate a bot client with no botID")
22+
} else {
23+
return &BotClient{
24+
botID: botID,
25+
}, nil
26+
}
27+
}
28+
29+
// botMessage is a simplified message body for some text sent on behalf of a bot.
30+
type botMessage struct {
31+
BotID string `json:"bot_id"`
32+
Text string `json:"text"`
33+
}
34+
35+
// SendMessage posts a message on behalf of a bot. If the message is empty, an error will be returned
36+
func (bc *BotClient) SendMessage(message string) error {
37+
req, err := autorest.Prepare(&http.Request{},
38+
autorest.WithBaseURL("https://api.groupme.com/v3/bots/post"),
39+
autorest.WithMethod("POST"),
40+
autorest.WithHeader("Content-Type", "application/json"),
41+
autorest.WithJSON(botMessage{
42+
BotID: bc.botID,
43+
Text: message,
44+
}),
45+
)
46+
if err != nil {
47+
return fmt.Errorf("Unable to construct request to send message on behalf of bot: %w", err)
48+
}
49+
50+
resp, err := autorest.Send(req)
51+
if err != nil {
52+
return fmt.Errorf("Error sending request to create a message: %w", err)
53+
}
54+
55+
if resp.StatusCode != http.StatusAccepted {
56+
bytes, _ := ioutil.ReadAll(resp.Body)
57+
log.Fatal("Bad response code from GroupMe:", string(bytes))
58+
err := fmt.Errorf("Unexpected HTTP status code when creating message: %d", resp.StatusCode)
59+
return err
60+
}
61+
62+
return nil
63+
}

go/twitterbot/cmd/main.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"os"
7+
"os/signal"
8+
"strings"
9+
"time"
10+
11+
twitterbot "github.com/groupme/BotExamples/go/twitter-bot"
12+
)
13+
14+
var (
15+
flagTweetQuery = flag.String("tweet-query", "groupme,cats,burrito", "What tags to filter on. Passed as a single comma seperated string")
16+
flagGroupMeBotID = flag.String("groupme-bot-id", "", "Identifier for your bot on GroupMe")
17+
flagTwitterAppKey = flag.String("twitter-app-key", "", "Developer Key associated with your twitter project")
18+
flagTwitterAppSecret = flag.String("twitter-app-secret", "", "Developer secret associated with your twitter project")
19+
)
20+
21+
func main() {
22+
flag.Parse()
23+
24+
// setup signal aware context.
25+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
26+
defer cancel()
27+
28+
twitterClient, _ := twitterbot.NewTwitterClient(*flagTwitterAppKey, *flagTwitterAppSecret)
29+
botClient, _ := twitterbot.NewBotClient(*flagGroupMeBotID)
30+
31+
poster, err := twitterbot.NewTwitterPoster(twitterClient, botClient, strings.Split(*flagTweetQuery, ",")...)
32+
if err != nil {
33+
panic(err)
34+
}
35+
36+
// Default rate limit is 450 requests in an hour, so fetch new tweets every 10 seconds
37+
go poster.StartListening(time.Second * 10)
38+
39+
<-ctx.Done()
40+
41+
poster.StopListening()
42+
}

go/twitterbot/go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module github.com/groupme/BotExamples/go/twitter-bot
2+
3+
go 1.16
4+
5+
require (
6+
github.com/Azure/go-autorest/autorest v0.11.19 // indirect
7+
github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e // indirect
8+
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
9+
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 // indirect
10+
)

0 commit comments

Comments
 (0)