Merge pull request #28 from Awesome-Technologies/slack
Add support for Slack notifications
This commit is contained in:
commit
de2fd6afb9
4 changed files with 116 additions and 26 deletions
12
main.go
12
main.go
|
@ -49,6 +49,8 @@ type config struct {
|
||||||
GithubToken string `yaml:"github_token"`
|
GithubToken string `yaml:"github_token"`
|
||||||
|
|
||||||
GithubProjectMappings map[string]string `yaml:"github_project_mappings"`
|
GithubProjectMappings map[string]string `yaml:"github_project_mappings"`
|
||||||
|
|
||||||
|
SlackWebhookURL string `yaml:"slack_webhook_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func basicAuth(handler http.Handler, username, password, realm string) http.Handler {
|
func basicAuth(handler http.Handler, username, password, realm string) http.Handler {
|
||||||
|
@ -89,6 +91,14 @@ func main() {
|
||||||
ghClient = github.NewClient(tc)
|
ghClient = github.NewClient(tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var slack *slackClient
|
||||||
|
|
||||||
|
if cfg.SlackWebhookURL == "" {
|
||||||
|
fmt.Println("No slack_webhook_url configured. Reporting bugs to slack is disabled.")
|
||||||
|
} else {
|
||||||
|
slack = NewSlackClient(cfg.SlackWebhookURL)
|
||||||
|
}
|
||||||
|
|
||||||
apiPrefix := cfg.APIPrefix
|
apiPrefix := cfg.APIPrefix
|
||||||
if apiPrefix == "" {
|
if apiPrefix == "" {
|
||||||
_, port, err := net.SplitHostPort(*bindAddr)
|
_, port, err := net.SplitHostPort(*bindAddr)
|
||||||
|
@ -102,7 +112,7 @@ func main() {
|
||||||
}
|
}
|
||||||
log.Printf("Using %s/listing as public URI", apiPrefix)
|
log.Printf("Using %s/listing as public URI", apiPrefix)
|
||||||
|
|
||||||
http.Handle("/api/submit", &submitServer{ghClient, apiPrefix, cfg.GithubProjectMappings})
|
http.Handle("/api/submit", &submitServer{ghClient, apiPrefix, cfg.GithubProjectMappings, slack})
|
||||||
|
|
||||||
// Make sure bugs directory exists
|
// Make sure bugs directory exists
|
||||||
_ = os.Mkdir("bugs", os.ModePerm)
|
_ = os.Mkdir("bugs", os.ModePerm)
|
||||||
|
|
|
@ -16,3 +16,7 @@ github_token: secrettoken
|
||||||
# mappings from app name (as submitted in the API) to github repo for issue reporting.
|
# mappings from app name (as submitted in the API) to github repo for issue reporting.
|
||||||
github_project_mappings:
|
github_project_mappings:
|
||||||
my-app: octocat/HelloWorld
|
my-app: octocat/HelloWorld
|
||||||
|
|
||||||
|
# a Slack personal webhook URL (https://api.slack.com/incoming-webhooks), which
|
||||||
|
# will be used to post a notification on Slack for each report.
|
||||||
|
slack_webhook_url: https://hooks.slack.com/services/TTTTTTT/XXXXXXXXXX/YYYYYYYYYYY
|
||||||
|
|
48
slack.go
Normal file
48
slack.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type slackClient struct {
|
||||||
|
webHook string
|
||||||
|
name string
|
||||||
|
face string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSlackClient(webHook string) *slackClient {
|
||||||
|
return &slackClient{
|
||||||
|
webHook: webHook,
|
||||||
|
name: "Notifier",
|
||||||
|
face: "robot_face"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slack *slackClient) Name(name string) {
|
||||||
|
slack.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slack *slackClient) Face(face string) {
|
||||||
|
slack.face = face
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slack slackClient) Notify(text string) error {
|
||||||
|
json := buildRequest(text, slack)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", slack.webHook, strings.NewReader(json))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't connect to host %s: %s", slack.webHook, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
_, err = client.Do(req)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRequest(text string, slack slackClient) string {
|
||||||
|
return fmt.Sprintf(`{"text":"%s", "username": "%s", "icon_emoji": ":%s:"}`, text, slack.name, slack.face)
|
||||||
|
}
|
78
submit.go
78
submit.go
|
@ -51,6 +51,8 @@ type submitServer struct {
|
||||||
|
|
||||||
// mappings from application to github owner/project
|
// mappings from application to github owner/project
|
||||||
githubProjectMappings map[string]string
|
githubProjectMappings map[string]string
|
||||||
|
|
||||||
|
slack *slackClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// the type of payload which can be uploaded as JSON to the submit endpoint
|
// the type of payload which can be uploaded as JSON to the submit endpoint
|
||||||
|
@ -460,38 +462,64 @@ func (s *submitServer) saveReport(ctx context.Context, p parsedPayload, reportDi
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.ghClient == nil {
|
if err := s.submitGithubIssue(ctx, p, listingURL, &resp); err != nil {
|
||||||
// we're done here
|
|
||||||
log.Println("GH issue submission disabled")
|
|
||||||
return &resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// submit a github issue
|
|
||||||
ghProj := s.githubProjectMappings[p.AppName]
|
|
||||||
if ghProj == "" {
|
|
||||||
log.Println("Not creating GH issue for unknown app", p.AppName)
|
|
||||||
return &resp, nil
|
|
||||||
}
|
|
||||||
splits := strings.SplitN(ghProj, "/", 2)
|
|
||||||
if len(splits) < 2 {
|
|
||||||
log.Println("Can't create GH issue for invalid repo", ghProj)
|
|
||||||
}
|
|
||||||
owner, repo := splits[0], splits[1]
|
|
||||||
|
|
||||||
issueReq := buildGithubIssueRequest(p, listingURL)
|
|
||||||
|
|
||||||
issue, _, err := s.ghClient.Issues.Create(ctx, owner, repo, &issueReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Created issue:", *issue.HTMLURL)
|
if err := s.submitSlackNotification(p, listingURL); err != nil {
|
||||||
|
return nil, err
|
||||||
resp.ReportURL = *issue.HTMLURL
|
}
|
||||||
|
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *submitServer) submitGithubIssue(ctx context.Context, p parsedPayload, listingURL string, resp *submitResponse) error {
|
||||||
|
if s.ghClient == nil {
|
||||||
|
log.Println("GH issue submission disabled")
|
||||||
|
} else {
|
||||||
|
// submit a github issue
|
||||||
|
ghProj := s.githubProjectMappings[p.AppName]
|
||||||
|
if ghProj == "" {
|
||||||
|
log.Println("Not creating GH issue for unknown app", p.AppName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
splits := strings.SplitN(ghProj, "/", 2)
|
||||||
|
if len(splits) < 2 {
|
||||||
|
log.Println("Can't create GH issue for invalid repo", ghProj)
|
||||||
|
}
|
||||||
|
owner, repo := splits[0], splits[1]
|
||||||
|
|
||||||
|
issueReq := buildGithubIssueRequest(p, listingURL)
|
||||||
|
|
||||||
|
issue, _, err := s.ghClient.Issues.Create(ctx, owner, repo, &issueReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Created issue:", *issue.HTMLURL)
|
||||||
|
|
||||||
|
resp.ReportURL = *issue.HTMLURL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *submitServer) submitSlackNotification(p parsedPayload, listingURL string) error {
|
||||||
|
if s.slack == nil {
|
||||||
|
log.Println("Slack notifications disabled")
|
||||||
|
} else {
|
||||||
|
slackBuf := fmt.Sprintf(
|
||||||
|
"%s\nApplication: %s\nReport: %s",
|
||||||
|
p.UserText, p.AppName, listingURL,
|
||||||
|
)
|
||||||
|
|
||||||
|
err := s.slack.Notify(slackBuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func buildGithubIssueRequest(p parsedPayload, listingURL string) github.IssueRequest {
|
func buildGithubIssueRequest(p parsedPayload, listingURL string) github.IssueRequest {
|
||||||
// set the title to the first (non-empty) line of the user's report, if any
|
// set the title to the first (non-empty) line of the user's report, if any
|
||||||
var title string
|
var title string
|
||||||
|
|
Reference in a new issue