github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/yuks/yuks.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package yuks 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "net/http" 24 "regexp" 25 26 "github.com/sirupsen/logrus" 27 28 "k8s.io/test-infra/prow/github" 29 "k8s.io/test-infra/prow/pluginhelp" 30 "k8s.io/test-infra/prow/plugins" 31 ) 32 33 var ( 34 match = regexp.MustCompile(`(?mi)^/joke\s*$`) 35 simple = regexp.MustCompile(`^[\w?'!., ]+$`) 36 ) 37 38 const ( 39 // Previously: https://tambal.azurewebsites.net/joke/random 40 jokeURL = realJoke("https://icanhazdadjoke.com") 41 pluginName = "yuks" 42 ) 43 44 func init() { 45 plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider) 46 } 47 48 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 49 // The Config field is omitted because this plugin is not configurable. 50 pluginHelp := &pluginhelp.PluginHelp{ 51 Description: "The yuks plugin comments with jokes in response to the `/joke` command.", 52 } 53 pluginHelp.AddCommand(pluginhelp.Command{ 54 Usage: "/joke", 55 Description: "Tells a joke.", 56 Featured: false, 57 WhoCanUse: "Anyone can use the `/joke` command.", 58 Examples: []string{"/joke"}, 59 }) 60 return pluginHelp, nil 61 } 62 63 type githubClient interface { 64 CreateComment(owner, repo string, number int, comment string) error 65 } 66 67 type joker interface { 68 readJoke() (string, error) 69 } 70 71 type realJoke string 72 73 var client = http.Client{} 74 75 type jokeResult struct { 76 Joke string `json:"joke"` 77 } 78 79 func (url realJoke) readJoke() (string, error) { 80 req, err := http.NewRequest("GET", string(url), nil) 81 if err != nil { 82 return "", fmt.Errorf("could not create request %s: %v", url, err) 83 } 84 req.Header.Add("Accept", "application/json") 85 resp, err := client.Do(req) 86 if err != nil { 87 return "", fmt.Errorf("could not read joke from %s: %v", url, err) 88 } 89 defer resp.Body.Close() 90 var a jokeResult 91 if err = json.NewDecoder(resp.Body).Decode(&a); err != nil { 92 return "", err 93 } 94 if a.Joke == "" { 95 return "", fmt.Errorf("result from %s did not contain a joke", url) 96 } 97 return a.Joke, nil 98 } 99 100 func handleGenericComment(pc plugins.Agent, e github.GenericCommentEvent) error { 101 return handle(pc.GitHubClient, pc.Logger, &e, jokeURL) 102 } 103 104 func handle(gc githubClient, log *logrus.Entry, e *github.GenericCommentEvent, j joker) error { 105 // Only consider new comments. 106 if e.Action != github.GenericCommentActionCreated { 107 return nil 108 } 109 // Make sure they are requesting a joke 110 if !match.MatchString(e.Body) { 111 return nil 112 } 113 114 org := e.Repo.Owner.Login 115 repo := e.Repo.Name 116 number := e.Number 117 118 for i := 0; i < 10; i++ { 119 resp, err := j.readJoke() 120 if err != nil { 121 return err 122 } 123 if simple.MatchString(resp) { 124 log.Infof("Commenting with \"%s\".", resp) 125 return gc.CreateComment(org, repo, number, plugins.FormatResponseRaw(e.Body, e.HTMLURL, e.User.Login, resp)) 126 } 127 128 log.Errorf("joke contains invalid characters: %v", resp) 129 } 130 131 return errors.New("all 10 jokes contain invalid character... such an unlucky day") 132 }