Skip to content

Instantly share code, notes, and snippets.

@stojg stojg/slowpoke.go
Created Dec 16, 2017

Embed
What would you like to do?
go program to run after a slow process, which sends you a text so that you know when it's finished
// This golang program is a remix/parody of https://gist.github.com/mrmorphic/7cb86c7b1a5e3da9d8664d6e8a1e3518
// that notifies the user that notifies a user after a slow process have finished, but instead of using the computers
// speech synthesiser or notification system, sends you a text instead. This means that you can leave the safe confines
// of your desk while scavenging office supplies, stalk colleagues or have a quiet smoko. I.e. it's the 2020 version of
// https://xkcd.com/303/. Ideal for those dependency fetching / compiling steps.
//
// To make this work you need to at least setup a `https://www.twilio.com` trial account and define a couple ENV
// variables:
//
// # Get this from your twilio account
// SLOWPOKE_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// # Get this from your twilio account
// SLOWPOKE_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// # Get this from your twilio account, you will need to create a number
// SLOWPOKE_FROM_NUMBER="+18xxxxxxxxx"
// # this is where slowpoke will send the finishes / failed message, don't forget the country code
// SLOWPOKE_TO_NUMBER="+64xxxxxxx"
//
// chuck these in your terminal profile startup
//
// go build / install this program and execute like so:
// slowpoke composer install
// or
// slowpoke npm install
package main
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"strings"
"syscall"
)
var ErrUsage = errors.New("Usage: slowpoke <command>")
var (
accountSid string
authToken string
toNumber string
fromNumber string
)
func main() {
// Get this from your twilio account
accountSid = os.Getenv("SLOWPOKE_ACCOUNT_SID")
// Get this from your twilio account
authToken = os.Getenv("SLOWPOKE_AUTH_TOKEN")
// Get this from your twilio account
fromNumber = os.Getenv("SLOWPOKE_FROM_NUMBER")
// this is where slowpoke will send the finished / failed message
toNumber = os.Getenv("SLOWPOKE_TO_NUMBER")
if accountSid == "" || authToken == "" || fromNumber == "" || toNumber == "" {
fmt.Println("Can't find all the required environment variables to use twilio")
os.Exit(0)
}
exitStatus, err := run()
if err != nil {
fmt.Println(ErrUsage)
os.Exit(1)
}
os.Exit(exitStatus)
}
func run() (int, error) {
if len(os.Args) < 2 {
return 1, ErrUsage
}
cmd := exec.Command(os.Args[1:][0])
cmd.Args = os.Args[1:]
// Ensure that the stdout and stderr from the command get piped into stdout and stderr
err := pipeCommandOutput(cmd)
if err != nil {
return 1, err
}
// generic failure that is sort of unexpected
if err := cmd.Start(); err != nil {
return 1, err
}
if err := cmd.Wait(); err != nil {
// try to get the underlying exit code on error
if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
exitCode := status.ExitStatus()
sendText(os.Args[1], exitCode)
return exitCode, nil
}
}
sendText(os.Args[1], 1)
return 1, err
}
sendText(os.Args[1], 0)
return 0, nil
}
func sendText(cmd string, status int) {
urlStr := "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/Messages.json"
msgData := url.Values{}
msgData.Set("To", toNumber)
msgData.Set("From", fromNumber)
if status == 0 {
msgData.Set("Body", fmt.Sprintf("command '%s' finished", cmd))
} else {
msgData.Set("Body", fmt.Sprintf("commadn '%s' failed", cmd))
}
msgDataReader := *strings.NewReader(msgData.Encode())
client := &http.Client{}
req, err := http.NewRequest("POST", urlStr, &msgDataReader)
if err != nil {
fmt.Println(ErrUsage)
os.Exit(1)
}
req.SetBasicAuth(accountSid, authToken)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("failed sending text with twilio: %v", err)
return
}
// text sent ok
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return
} else {
fmt.Printf("slowpoke error: twilio text sending failed with status: %s\n", resp.Status)
}
}
// PipeCommandOutput ensures that the commands output gets piped to slackers output
func pipeCommandOutput(cmd *exec.Cmd) (err error) {
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return err
}
go io.Copy(os.Stdout, stdout)
go io.Copy(os.Stderr, stderr)
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.