Skip to content
This repository has been archived by the owner on Sep 18, 2019. It is now read-only.

Commit

Permalink
Add a github commit notification bot.
Browse files Browse the repository at this point in the history
  • Loading branch information
cespare committed Jan 10, 2013
1 parent 7edd5bb commit 5c9fdf3
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 3 deletions.
7 changes: 7 additions & 0 deletions deploy.sh
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

set -e

glp build
scp pratbot pratbot.ctrl-c.us:~/servers/pratbot
rm pratbot
11 changes: 8 additions & 3 deletions pratbot.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ var (
type newBotFunc func(*connection.Conn) bot.Bot type newBotFunc func(*connection.Conn) bot.Bot


var ( var (
botNameToFunc = map[string]newBotFunc{"echo": bot.NewEcho} botNameToFunc = map[string]newBotFunc{
bots = make(map[string]newBotFunc) "echo": bot.NewEcho,
disp = dispatcher.New() "commit": bot.NewCommit,
}
bots = make(map[string]newBotFunc)
disp = dispatcher.New()
) )


func init() { func init() {
Expand Down Expand Up @@ -83,6 +86,8 @@ func main() {
disp.Register(f(conn)) disp.Register(f(conn))
} }


log.Println("Bots started.")

// Send 'connected' message // Send 'connected' message
connectedMsg := &bot.Event{ connectedMsg := &bot.Event{
Type: bot.EventConnect, Type: bot.EventConnect,
Expand Down
1 change: 1 addition & 0 deletions src/bot/bot.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bot
type EventType int type EventType int


const ( const (
_ = iota // So that the uninitialized message isn't accidentally valid
EventConnect EventType = iota EventConnect EventType = iota
EventPublishMessage EventPublishMessage
) )
Expand Down
133 changes: 133 additions & 0 deletions src/bot/commit.go
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,133 @@
package bot

// A bot that listens for github commit notifications and posts them to channels.

import (
"bytes"
"connection"
"encoding/json"
"log"
"net/http"
"strings"
"text/template"
)

const (
addr = "localhost:9898"
)

// TODO: configuration for bots should probably be in config files
var repoToChans = map[string][]string{
"oochat": {"general", "oochat"},
"barkeep": {"barkeep"},
"pratbot": {"pratbot", "bot-test"},
}
var chans = make(map[string]struct{})
var templ *template.Template

func init() {
// Get all unique channels
for _, cs := range repoToChans {
for _, c := range cs {
chans[c] = struct{}{}
}
}

// Set up template
funcMap := template.FuncMap{
"shortenSha": shortenSha,
"shortenMessage": shortenMessage,
}
var err error
templ, err = template.New("message").Funcs(funcMap).Parse(messageTemplate)
if err != nil {
log.Fatal(err)
}
}

// Set up server that gets github post-receive hook POST requests.

// Only the fields we care about
type GithubNotification struct {
Repository struct {
Name string
Url string
}
Commits []struct {
Id string
Message string
Url string
Author struct {
Name string
Username string
}
}
}

func shortenMessage(msg string) string {
subject := strings.SplitN(msg, "\n", 2)[0]
if len(subject) > 80 {
return subject[:77] + "..."
}
return subject
}

func shortenSha(sha string) string {
return sha[:8]
}

var messageTemplate = `
{{$repo := .Repository}}
{{range .Commits}}
**[CommitBot]** [{{.Author.Name}}](https://github.com/{{.Author.Username}}) authored [{{.Id | shortenSha}}]({{.Url}}) in [{{$repo.Name}}]({{$repo.Url}}): "{{.Message | shortenMessage}}"
{{end}}
`

func NotificationHandler(conn *connection.Conn) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
payload := r.Form["payload"]
if len(payload) < 1 || payload[0] == "" {
return
}
var notification GithubNotification
if err := json.Unmarshal([]byte(payload[0]), &notification); err != nil {
log.Println(err)
log.Println("CommitBot warning: couldn't parse payload:", payload)
return
}
var buf bytes.Buffer
if err := templ.Execute(&buf, &notification); err != nil {
log.Println("CommitBot warning: couldn't construct message", err)
return
}
message := strings.TrimSpace(buf.String())
for _, c := range repoToChans[notification.Repository.Name] {
conn.SendMessage(c, message)
}
}
}

type Commit struct {
conn *connection.Conn
}

func NewCommit(conn *connection.Conn) Bot {
return &Commit{conn}
}

func (b *Commit) Handle(e *Event) {
switch e.Type {
case EventConnect:
b.conn.Leave("general") // quick hack until there's an API for current channels
// We don't really need to join these channels, but whatever.
for c, _ := range chans {
b.conn.Join(c)
}

// Start server
mux := http.NewServeMux()
mux.HandleFunc("/", NotificationHandler(b.conn))
go http.ListenAndServe(addr, mux)
}
}
1 change: 1 addition & 0 deletions src/bot/echo.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func NewEcho(conn *connection.Conn) Bot {
func (b *Echo) Handle(e *Event) { func (b *Echo) Handle(e *Event) {
switch e.Type { switch e.Type {
case EventConnect: case EventConnect:
b.conn.Leave("general") // quick hack until there's an API for current channels
for _, c := range channels { for _, c := range channels {
b.conn.Join(c) b.conn.Join(c)
} }
Expand Down
9 changes: 9 additions & 0 deletions src/connection/connection.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ func (c *Conn) Join(channel string) error {
return c.sendJsonData(m) return c.sendJsonData(m)
} }


func (c *Conn) Leave(channel string) error {
data := map[string]string{"channel": channel}
m := &Message{
Action: "leave_channel",
Data: data,
}
return c.sendJsonData(m)
}

func Connect(addrString, apiKey, secret string) (*Conn, error) { func Connect(addrString, apiKey, secret string) (*Conn, error) {
origin := "http://localhost/" origin := "http://localhost/"
config, err := websocket.NewConfig(authutil.ConnectionString(addrString, apiKey, secret), origin) config, err := websocket.NewConfig(authutil.ConnectionString(addrString, apiKey, secret), origin)
Expand Down
3 changes: 3 additions & 0 deletions src/dispatcher/dispatcher.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func (d *Dispatcher) SendRaw(msg string) {
} }
event.Type = bot.EventPublishMessage event.Type = bot.EventPublishMessage
event.Payload = *m event.Payload = *m
// Ignore these message types for now
case "pong", "join_channel", "leave_channel", "user_active", "user_offline":
return
default: default:
log.Println("Received unhandled message type:", typ.Type) log.Println("Received unhandled message type:", typ.Type)
return return
Expand Down

0 comments on commit 5c9fdf3

Please sign in to comment.