| @ -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"` | |||||
| } | |||||