Skip to content

Instantly share code, notes, and snippets.

@nojima
Last active June 17, 2016 10:55
Show Gist options
  • Save nojima/abe58cb445621b4d5ead to your computer and use it in GitHub Desktop.
Save nojima/abe58cb445621b4d5ead to your computer and use it in GitHub Desktop.
Slack のロガー。Real Time Messaging API で流れてきた JSON をそのまま標準出力に吐き出す。
// slack-logger/main.go
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"time"
"golang.org/x/net/websocket"
)
type Config struct {
PingInterval int
}
const (
apiRtmStart = "https://slack.com/api/rtm.start"
webSocketOrigin = "https://slack.com"
)
var (
config Config
)
func startRtm(token string) (string, error) {
resp, err := http.Get(apiRtmStart + "?token=" + url.QueryEscape(token))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", fmt.Errorf("rtm.start API failed: %v", resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read body of rtm.start: %v", err)
}
type Result struct {
Ok bool
Url string
}
var result Result
err = json.Unmarshal(body, &result)
if err != nil {
return "", fmt.Errorf("failed to unmarshal the body: %v", string(body))
}
if !result.Ok {
return "", fmt.Errorf("rtm.start API failed: %v", body)
}
return result.Url, nil
}
func receiveMessages(ws *websocket.Conn, ch chan<- error) {
for {
var message string
err := websocket.Message.Receive(ws, &message)
if err == io.EOF {
ch <- nil
return
}
if err != nil {
ch <- err
return
}
// Filter "pong" out
type Record struct{ Type string }
var record Record
if err = json.Unmarshal([]byte(message), &record); err == nil {
if record.Type == "pong" {
continue
}
}
fmt.Println(message)
}
}
func ping(ws *websocket.Conn, ch chan<- error) {
for id := 0; ; id++ {
message := fmt.Sprintf(`{"id":%v, "type": "ping"}`, id)
err := websocket.Message.Send(ws, message)
if err != nil {
ch <- err
return
}
time.Sleep(time.Duration(config.PingInterval) * time.Second)
}
}
func interact(ws *websocket.Conn) error {
ch1 := make(chan error)
ch2 := make(chan error)
go receiveMessages(ws, ch1)
go ping(ws, ch2)
select {
case err1 := <-ch1:
return err1
case err2 := <-ch2:
return err2
}
}
func run(token string) error {
url, err := startRtm(token)
if err != nil {
return err
}
ws, err := websocket.Dial(url, "", webSocketOrigin)
if err != nil {
return err
}
defer ws.Close()
log.Println("Connected")
return interact(ws)
}
func main() {
flag.IntVar(&config.PingInterval, "ping-interval", 5, "interval between successive pings in seconds")
flag.Parse()
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Usage: slack-logger [options] TOKEN\n")
fmt.Fprintf(os.Stderr, "Options:\n")
flag.PrintDefaults()
os.Exit(1)
}
token := flag.Arg(0)
err := run(token)
if err == nil {
log.Fatal(err)
}
log.Println("Exit")
}
# /etc/init/slack-logger.conf
# Sample Upstart configuration file. (only in Ubuntu)
# You need to create /var/log/slack owned by www-data in advance.
description "Slack Logger"
author "Yusuke Nojima"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
chdir /
respawn
respawn limit 5 60
exec /home/nojima/go/bin/slack-logger xoxb-XXXXXX-XXXXXXXXXXXXXXXXXXXXXX | rotatelogs /var/log/slack/%Y-%m-%d.log 86400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment