| @ -0,0 +1 @@ | |||
| slackbot | |||
| @ -0,0 +1,4 @@ | |||
| slackbot | |||
| ======== | |||
| A slackbot written in go. | |||
| @ -0,0 +1,23 @@ | |||
| package main | |||
| import ( | |||
| "flag" | |||
| "log" | |||
| "github.com/brettlangdon/slackbot/plugins/sqwiggle" | |||
| "github.com/brettlangdon/slackbot/server" | |||
| ) | |||
| var bind string | |||
| func main() { | |||
| flag.StringVar(&bind, "bind", "127.0.0.1:8000", "which address to bind to [defualt: 127.0.0.1:8000]") | |||
| flag.Parse() | |||
| s := server.NewServer() | |||
| s.AddPlugin(sqwiggle.NewSqwiggle()) | |||
| s.SetListenAddress(bind) | |||
| log.Fatal(s.Start()) | |||
| } | |||
| @ -0,0 +1,8 @@ | |||
| package plugins | |||
| import "github.com/brettlangdon/slackbot/slack" | |||
| type Plugin interface { | |||
| GetName() string | |||
| HandleRequest(slack.APIRequest, []string) (slack.APIResponse, error) | |||
| } | |||
| @ -0,0 +1,111 @@ | |||
| package sqwiggle | |||
| import ( | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "net/http" | |||
| "github.com/brettlangdon/slackbot/slack" | |||
| ) | |||
| type SqwiggleResponse struct { | |||
| Snapshot string `json:"snapshot"` | |||
| Status string `json:"status"` | |||
| Name string `json:"name"` | |||
| EMail string `json:"email"` | |||
| TimeZone string `json:"time_zone"` | |||
| Avatar string `json:"avatar"` | |||
| Message string `json:"message"` | |||
| SnapshotInterval int `json:"snapshot_interval"` | |||
| } | |||
| type Sqwiggle struct { | |||
| name string | |||
| } | |||
| func NewSqwiggle() Sqwiggle { | |||
| return Sqwiggle{ | |||
| name: "sqwiggle", | |||
| } | |||
| } | |||
| func (this Sqwiggle) GetName() string { | |||
| return this.name | |||
| } | |||
| func (this Sqwiggle) SetName(name string) { | |||
| this.name = name | |||
| } | |||
| func (this Sqwiggle) callSqwiggle(token string) (response []SqwiggleResponse, err error) { | |||
| client := &http.Client{} | |||
| request, err := http.NewRequest("GET", "https://api.sqwiggle.com/users", nil) | |||
| if err != nil { | |||
| return response, err | |||
| } | |||
| request.SetBasicAuth(token, "slackbot") | |||
| sqwiggleResponse, err := client.Do(request) | |||
| if err != nil || sqwiggleResponse.StatusCode != 200 { | |||
| err = errors.New(fmt.Sprintf("API call to sqwiggle with token %s failed", token)) | |||
| return response, err | |||
| } | |||
| defer sqwiggleResponse.Body.Close() | |||
| contents, err := ioutil.ReadAll(sqwiggleResponse.Body) | |||
| if err != nil { | |||
| err = errors.New("Could not read sqwiggle api response") | |||
| return response, err | |||
| } | |||
| err = json.Unmarshal(contents, &response) | |||
| if err != nil { | |||
| err = errors.New(fmt.Sprintf("Could not parse sqwiggle api response: %s", string(contents))) | |||
| return response, err | |||
| } | |||
| return response, nil | |||
| } | |||
| func (this Sqwiggle) HandleRequest(request slack.APIRequest, args []string) (response slack.APIResponse, err error) { | |||
| if request.Text != "#sqwiggle" { | |||
| err := errors.New(fmt.Sprintf("Incorrect trigger text: %s", request.Text)) | |||
| return response, err | |||
| } | |||
| if len(args) <= 1 { | |||
| err := errors.New("Missing sqwiggle token argument") | |||
| return response, err | |||
| } | |||
| users, err := this.callSqwiggle(args[1]) | |||
| if err != nil { | |||
| return response, err | |||
| } | |||
| response.Text = "Sqwiggle Time" | |||
| for _, user := range users { | |||
| attachment := slack.Attachment{} | |||
| switch user.Status { | |||
| case "offline": | |||
| attachment.Color = "#000000" | |||
| case "available": | |||
| attachment.Color = "#00CC33" | |||
| case "busy": | |||
| attachment.Color = "#FF3333" | |||
| } | |||
| attachment.Title = user.Name | |||
| attachment.TitleLink = user.Snapshot | |||
| attachment.ImageUrl = user.Snapshot | |||
| attachment.Fallback = user.Snapshot + "#.jpg" | |||
| response.Attachments = append(response.Attachments, attachment) | |||
| } | |||
| return response, err | |||
| } | |||
| @ -0,0 +1,81 @@ | |||
| package server | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "log" | |||
| "net/http" | |||
| "strings" | |||
| "github.com/brettlangdon/slackbot/plugins" | |||
| "github.com/brettlangdon/slackbot/slack" | |||
| ) | |||
| type Server struct { | |||
| plugs map[string]plugins.Plugin | |||
| address string | |||
| server *http.Server | |||
| } | |||
| func NewServer() *Server { | |||
| server := &Server{ | |||
| address: "0.0.0.0:80", | |||
| } | |||
| server.plugs = make(map[string]plugins.Plugin) | |||
| return server | |||
| } | |||
| func (this *Server) SetListenAddress(address string) { | |||
| this.address = address | |||
| } | |||
| func (this *Server) AddPlugin(plug plugins.Plugin) { | |||
| this.plugs[strings.ToLower(plug.GetName())] = plug | |||
| } | |||
| func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| // we always want to write back something | |||
| defer fmt.Fprintf(w, "") | |||
| path := strings.Trim(r.URL.Path, "/") | |||
| if path == "" { | |||
| log.Println("Empty path given") | |||
| return | |||
| } | |||
| args := strings.Split(path, "/") | |||
| name := strings.ToLower(args[0]) | |||
| var plug plugins.Plugin | |||
| var ok bool | |||
| if plug, ok = this.plugs[name]; !ok { | |||
| log.Println("No plugin gin found with name: ", name) | |||
| return | |||
| } | |||
| apiRequest := slack.ParseAPIRequest(r) | |||
| apiResponse, err := plug.HandleRequest(apiRequest, args) | |||
| if err != nil { | |||
| log.Println(err) | |||
| return | |||
| } | |||
| content, err := json.Marshal(apiResponse) | |||
| if err != nil { | |||
| log.Println(err) | |||
| return | |||
| } | |||
| fmt.Fprintf(w, "%s", content) | |||
| } | |||
| func (this *Server) Start() error { | |||
| this.server = &http.Server{ | |||
| Addr: this.address, | |||
| Handler: this, | |||
| } | |||
| log.Println("Listening to: ", this.address) | |||
| return this.server.ListenAndServe() | |||
| } | |||
| @ -0,0 +1,64 @@ | |||
| package slack | |||
| import ( | |||
| "net/http" | |||
| "strconv" | |||
| ) | |||
| type APIRequest struct { | |||
| Token string | |||
| TeamId string | |||
| TeamDomain string | |||
| ChannelId string | |||
| ChannelName string | |||
| Timestamp float64 | |||
| UserId string | |||
| UserName string | |||
| Text string | |||
| TriggerWord string | |||
| } | |||
| func ParseAPIRequest(httpReq *http.Request) (response APIRequest) { | |||
| response.Token = httpReq.PostFormValue("token") | |||
| response.TeamId = httpReq.PostFormValue("team_id") | |||
| response.TeamDomain = httpReq.PostFormValue("team_domain") | |||
| response.ChannelId = httpReq.PostFormValue("channel_id") | |||
| response.ChannelName = httpReq.PostFormValue("channel_name") | |||
| ts := httpReq.PostFormValue("timestamp") | |||
| response.Timestamp, _ = strconv.ParseFloat(ts, 32) | |||
| response.UserId = httpReq.PostFormValue("user_id") | |||
| response.UserName = httpReq.PostFormValue("user_name") | |||
| response.Text = httpReq.PostFormValue("text") | |||
| response.TriggerWord = httpReq.PostFormValue("trigger_word") | |||
| return response | |||
| } | |||
| type APIResponse struct { | |||
| Text string `json:"text,omitempty"` | |||
| Attachments []Attachment `json:"attachments,omitempty"` | |||
| UserName string `json:"user_name,omitempty"` | |||
| IconEmoji string `json:"icon_emoji,omitempty"` | |||
| Channel string `json:"channel,omitempty"` | |||
| Markdown bool `json:"mrkdwn,omitempty"` | |||
| } | |||
| type Attachment struct { | |||
| AuthorIcon string `json:"author_icon,omitempty"` | |||
| AuthorLink string `json:"author_link,omitempty"` | |||
| AuthorName string `json:"author_name,omitempty"` | |||
| Color string `json:"color,omitempty"` | |||
| Fallback string `json:"fallback,omitempty"` | |||
| Fields []Fields `json:"fields,omitempty"` | |||
| ImageUrl string `json:"image_url,omitempty"` | |||
| MarkdownIn string `json:"mrkdwn_in,omitempty"` | |||
| Pretext string `json:"pretext,omitempty"` | |||
| Title string `json:"title,omitempty"` | |||
| TitleLink string `json:"title_link,omitempty"` | |||
| } | |||
| type Fields struct { | |||
| Title string `json:"title,omitempty"` | |||
| Value string `json:"value,omitempty"` | |||
| Short bool `json:"short,omitempty"` | |||
| } | |||