Reddit CLI Built With Golang

Photo by Chinmay B on Unsplash

Reddit CLI Built With Golang

Overview: You will lean how to get json data form the reddit api and open the Reddit post in your default web browser.

Prerequisites:

  • Go run time

To download the go runtime visit this link.

Getting started

Start by creating a new directory for your project

In the command line mkdir RedditCLI

Next let enable dependency tracking by generating a go.mod file.

run go mod init redditcli/reddit

In development the module path will typically be the place you store your source code for me it would be github.com/goredditcli the above is ok for now.

now create a main.go file.

In the command line touch main.go.

In the main.go file write the following code.

package main

import "fmt"

func main () {
  fmt.Println("hello, world")
}
  • In the code above we declare a package name this is go's way of grouping functions in a file.
  • we also import import the "fmt" package, this has functions for formatting text, as well as ways of printing to the console.
  • finally, the main Println function to prints "hello, world" to the console.

To run go code run go run main.go in the command line.

Create a Struct for our Reddit data

write the following code

package main

import "fmt"

type RedditPostData struct {
    Data struct {
        After    string `json:"after"`
        Children []struct {
            Kind string `json:"kind"`
            Data struct {
                Title     string `json:"title"`
                Permalink string `json:"permalink"`
            } `json:"data,omitempty"`
        } `json:"children"`
    } `json:"data"`
}


func main () {
  fmt.Println("hello, world")
}

The struct is a type that simply holds a collection of fields. I know the json to be returned will be in this format, to view the json data for yourself visit this link https://www.reddit.com/.json. To structure any json to a valid struct type use this tool.

Fetching json data from reddit

replace import "fmt" with the following:

import (
  "fmt"
  "net/http"
  "log"
  "math/rand"
  "encoding/json"
)
  • The net/http adds go package to allow us to make a get request.
  • The log package will be used to for logging.
  • math/rand will be used to generate a random number.
  • encoding/json allows us to implement encoding and decoding of JSON.

Delete everything within the main function and write the following code

func main() {
    resp, err := http.Get("https://www.reddit.com/.json")
    if err != nil {
        log.Fatalln(err)
    }

    defer resp.Body.Close()
    bodyBytes, _ := io.ReadAll(resp.Body)

    // Convert response body to RedditPostData struct
    var data RedditPostData
    err = json.Unmarshal([]byte(bodyBytes), &data)
    if err != nil {
        fmt.Println(err)
        return
    }
}

randomIndex := rand.Intn(len(data.Data.Children))

The http.Get() code above allows to make a HTTP GET request for the reddit post data, it either returns a response with the the JSON data or returns an error which is why resp, err is there. If the err variable is not nil then we log the error.

The next line defer resp.Body.Close() closes the response body when we're finished using it which is why the defer keyword is used.

io.ReadAll(resp.Body) reads the data from the response body and returns it.

err = json.Unmarshal([]byte(bodyBytes), &data) decodes the json data and point the decoded json to the data RedditPostData struct.

Set up a CLI flag

To your imports add "flag" package

    printPtr := flag.Bool("print", false, "a bool")
    flag.Parse()

Here we declare a print flag boolean with a default value "print" and a short description. The flag.Bool function returns a boolean pointer not a boolean value.

Finally open the post in your web browser or print the title and url to the console

To your imports add "github.com/pkg/browser" package this package will allow us to open a file in your default web browser.

The full package list should now look like

import (
    "encoding/json"
    "flag"
    "fmt"
    "io"
    "log"
    "math/rand"
    "net/http"

    "github.com/pkg/browser"
)
    // if -print flag is passed log the reddit post title and URL
    if *printPtr {
        fmt.Printf("The Reddit Post Title is:  %v\n The permalink is https://reddit.com%v\n", data.Data.Children[randomIndex].Data.Title, data.Data.Children[randomIndex].Data.Permalink)
    } else {
        randomPostPermaLink := data.Data.Children[randomIndex].Data.Permalink
        postURL := fmt.Sprintf("%s%s", "https://reddit.com", randomPostPermaLink)
        browser.OpenURL(postURL)
    }

The above code checks the the printPtr if its true it prints the reddit post title and url. Otherwise it constructs the post URL using the Sprintf function and uses the OpenURL function to open the Reddit post in your default web browser.

Final steps

run go mod tidy

run go run main.go

All done! View the full source code here.