Writing A Twitter Bot in Golang

Elliot Forbes Elliot Forbes ⏰ 6 Minutes 📅 Apr 9, 2017

In this tutorial I’m going to be demonstrating how to build a twitter bot using go-twitter, a popular Go client library for the Twitter API.

I’ll be demonstrating how you can build a go based twitter bot that will be able to do such things as automatically reply to tweets and favorite tweets that contain a specific hashtag.

Setting Up Your Project

Note - We’ll be using go modules which is an experimental feature of Go 1.11

Before we can get started, we’ll need to create a new project directory and initialize it using modules:

$ mkdir -p go-twitter-bot
$ go mod init github.com/elliotforbes/go-twitter-bot

This should create a go.mod file within our directory and we should be good to get started writing our twitter bot!

Connecting to Twitter

Just like with the Python version of this tutorial, you’ll have to create an app in twitter’s app control panel. Once you’ve created a new application, it should present you with all the secret tokens and keys that you need in order to proceed.

Environment Variables

So, once we’ve created a new application within the twitter apps dashboard, we’ll have our credentials and we’ll be ready to start coding up our app.

We never want to hard-code credentials into our application’s source code in case we accidentally commit these credentials into github or somewhere public. Instead, we’ll want to set these as environment variables on our local machine and retrieve the credentials from there.

We can export these environment variables like so on MacOS:

$ export CONSUMER_KEY=VALUE
$ export CONSUMER_SECRET=VALUE
$ export ACCESS_TOKEN=VALUE
$ export ACCESS_TOKEN_SECRET=VALUE

Note - If you are developing this on Windows, replace the export command with set and this should still work

Once we’ve done this, we’ll be able to pick these values up safely within our code using the os package.

Writing our Basic Go Twitter Bot

Once you’ve got all the access tokens and secret tokens ready, it’s time to start implementing our Bot.

Let’s start off the process by creating a really simple main.go file within our working directory:

package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

And, just to sanity check that everything is working, let’s try and run this by calling go run:

$ go run ./...
Hello World

Awesome, now that we’ve got our foundation sorted, let’s start building up the rest of our Bot.

The first thing we’ll need, is a way to communicate with the twitter API in such a way that it knows who we are. We can create a simple struct called Credentials which will store our consumer and access tokens for authentication later on.

import (
    // other imports
    "github.com/dghubble/go-twitter/twitter"
    "github.com/dghubble/oauth1"
)

// Credentials stores all of our access/consumer tokens
// and secret keys needed for authentication against
// the twitter REST API.
type Credentials struct {
    ConsumerKey       string
    ConsumerSecret    string
    AccessToken       string
    AccessTokenSecret string
}

// getClient is a helper function that will return a twitter client
// that we can subsequently use to send tweets, or to stream new tweets
// this will take in a pointer to a Credential struct which will contain
// everything needed to authenticate and return a pointer to a twitter Client
// or an error
func getClient(creds *Credentials) (*twitter.Client, error) {
    // Pass in your consumer key (API Key) and your Consumer Secret (API Secret)
    config := oauth1.NewConfig(creds.ConsumerKey, creds.ConsumerSecret)
    // Pass in your Access Token and your Access Token Secret
    token := oauth1.NewToken(creds.AccessToken, creds.AccessTokenSecret)

    httpClient := config.Client(oauth1.NoContext, token)
    client := twitter.NewClient(httpClient)

    // Verify Credentials
    verifyParams := &twitter.AccountVerifyParams{
        SkipStatus:   twitter.Bool(true),
        IncludeEmail: twitter.Bool(true),
    }

    // we can retrieve the user and verify if the credentials
    // we have used successfully allow us to log in!
    user, _, err := client.Accounts.VerifyCredentials(verifyParams)
    if err != nil {
        return nil, err
    }

    log.Printf("User's ACCOUNT:\n%+v\n", user)
    return client, nil
}

Perfect, we now have a means of connecting to the twitter API and validating whether the credentials we have used have worked.

We’ll want to update our main() function to create a Credentials struct and retrieve a new client for us using our newly implemented function:

func main() {
    fmt.Println("Go-Twitter Bot v0.01")
    creds := Credentials{
        AccessToken:       os.Getenv("ACCESS_TOKEN"),
        AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
        ConsumerKey:       os.Getenv("CONSUMER_KEY"),
        ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
    }

    fmt.Printf("%+v\n", creds)

    client, err := getClient(&creds)
    if err != nil {
        log.Println("Error getting Twitter Client")
        log.Println(err)
    }

    // Print out the pointer to our client
    // for now so it doesn't throw errors
    fmt.Printf("%+v\n", client)

}

We should now be able to run this using the same go run ./... command that we used earlier. If our credentials have been set properly, we should see our account details being printed out in the client!

Sending Tweets

Ok, so now that we’ve got a client that works, we can try sending a few test tweets out using one of the libraries’ existing functions.

Remove the final fmt.Printf() statement in your main() function and let’s try sending a simple tweet:

tweet, resp, err := client.Statuses.Update("A Test Tweet from a new Bot I'm building!", nil)
if err != nil {
    log.Println(err)
}
log.Printf("%+v\n", resp)
log.Printf("%+v\n", tweet)

And, if everything goes to plan, we should see that a new tweet from our account has popped up on our timelines:

Searching Tweets

Ok, so we’re able to send tweets, but how do we go about searching for tweets on a given topic?

To do that we could search for tweets using this handy function:

search, resp, err := client.Search.Tweets(&twitter.SearchTweetParams{
    Query: "Golang",
})

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

log.Printf("%+v\n", resp)
log.Printf("%+v\n", search)

And when we run this, we should be able to see a list of tweets that have Golang mentioned somewhere within them!

Now, imagine we swapped this out for Google, or Amazon or any other major brand and plugged in a sentiment analysis system. We could chart things like the general sentiment for a brand across twitter over time, which would be pretty awesome!

Conclusion

That’s us reached the end of our journey for this particular topic! I’m hoping this tutorial has given you a good enough foundation to go off and build really cool systems that interact with various social media accounts.

Note - If you want to keep track of when new Go articles are posted to the site, then please feel free to follow me on twitter for all the latest news: @Elliot_F.

If you enjoyed this tutorial, you might also enjoy the following tutorials: