Skip to content

Instantly share code, notes, and snippets.

@bgreenlee
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bgreenlee/6b121c1b358535398b1b to your computer and use it in GitHub Desktop.
Save bgreenlee/6b121c1b358535398b1b to your computer and use it in GitHub Desktop.
Bare-bones slash command handler for Slack
package main
/**
* Bare-bones slash command handler for Slack
*
* Commands:
* /weather [city] - Return the current weather for the given city
*/
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"strings"
"time"
"github.com/bradfitz/latlong"
)
/**
* weatherHandler reports the weather for a given location using OpenWeatherMap
*/
func weatherHandler(resp http.ResponseWriter, req *http.Request) {
// Slack sends the text after the command in the "text" query param
location := req.FormValue("text")
// make our weather API call
params := url.Values{}
params.Add("q", location)
params.Add("units", "imperial") // suck it, rest of the world
res, err := http.Get("http://api.openweathermap.org/data/2.5/weather?" + params.Encode())
// read the response and parse the JSON
decoder := json.NewDecoder(res.Body)
var weatherData struct {
Coord *struct {
Lon float64
Lat float64
}
Sys *struct {
SunriseEpoch int64 `json:"sunrise"`
SunsetEpoch int64 `json:"sunset"`
}
Weather []*struct {
Description string
Icon string
}
Main *struct {
Temp float64
TempHigh float64 `json:"temp_max"`
TempLow float64 `json:"temp_min"`
}
Location string `json:"name"`
Message string // only returned in the case of an error
Code interface{} `json:"cod"` // this can be either an int or a string, ugh
}
err = decoder.Decode(&weatherData)
if err != nil {
log.Println(err)
fmt.Fprintf(resp, "Oops, got an error: %s", err)
return
}
if weatherData.Main == nil {
if weatherData.Code.(string) == "404" {
fmt.Fprintf(resp, "Sorry, I couldn't find anything for '%s'", location)
} else {
fmt.Fprintf(resp, "Oops, got an error: %s", weatherData.Message)
}
return
}
// build the conditions string
conditions := []string{}
conditionsStr := ""
for _, conditionData := range weatherData.Weather {
conditions = append(conditions, conditionData.Description)
}
if len(conditions) > 2 {
conditionsStr = strings.Join(conditions[:len(conditions)-1], ", ") + ", and " + conditions[len(conditions)-1]
} else {
conditionsStr = strings.Join(conditions, " and ")
}
// get sunrise/sunset in the local time zone
timezone := latlong.LookupZoneName(weatherData.Coord.Lat, weatherData.Coord.Lon)
tzLocation, _ := time.LoadLocation(timezone)
sunrise := time.Unix(weatherData.Sys.SunriseEpoch, 0).In(tzLocation)
sunset := time.Unix(weatherData.Sys.SunsetEpoch, 0).In(tzLocation)
// send our response
responseStr := fmt.Sprintf("Currently %d and %s. The high today is %d and the low is %d.\nSunrise is at %s and sunset is at %s",
int(weatherData.Main.Temp), conditionsStr,
int(weatherData.Main.TempHigh), int(weatherData.Main.TempLow),
sunrise.Format("3:04am"), sunset.Format("3:04pm"))
// send the response back to Slack
// if you instead want a private response, replace this with:
// fmt.Fprint(resp, responseStr)
postResponse(responseStr, weatherData.Location, weatherData.Weather[0].Icon)
}
/**
* postResponse uses an Incoming Webhook (https://hackarts.slack.com/services/new/incoming-webhook)
* to post a response back to Slack. This allows us to have a public response, rather
* than the private one we would get if we just output the response directly to the
* http.ResponseWriter
*/
func postResponse(responseText string, location string, icon string) {
// replace this with the url Slack gives you when you create an Incoming Webhook
webhookUrl := "https://hooks.slack.com/services/..."
type SlackResponse struct {
Text string `json:"text"`
Username string `json:"username"`
Icon string `json:"icon_url"`
}
response := SlackResponse{
Text: responseText,
Username: "Weather for " + location,
Icon: "http://openweathermap.org/img/w/" + icon + ".png",
}
responseJSON, err := json.Marshal(response)
if err != nil {
log.Println(err)
return
}
_, err = http.Post(webhookUrl, "application/json", bytes.NewReader(responseJSON))
if err != nil {
log.Println(err)
}
}
func main() {
http.HandleFunc("/weather", weatherHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment