def hooklistener(): """Listen for the "issues" webhook event. By default, we return a 403 HTTP response. """ # webcompat/webcompat-tests/issues if not is_github_hook(request): return make_response('Nothing to see here', 401) payload = json.loads(request.data) event_type = request.headers.get('X-GitHub-Event') # Treating events related to issues if event_type == 'issues': webhook_issue = WebHookIssue.from_dict(payload) # we process the action return webhook_issue.process_issue_action() elif event_type == 'ping': return make_response('pong', 200) # If nothing worked as expected, the default response is 403. return make_response('Not an interesting hook', 403)
def process_issue_action(self): """Route the actions and provide different responses. There are two possible known scopes: * public repo * private repo Currently the actions we are handling are (for now): * opened (public repo only) Aka newly issues created and need to be assigned labels and milestones * milestoned When the issue is moved to needscontact When the issue is being moderated with a milestone: accepted """ source_repo = self.repository_url scope = repo_scope(source_repo) # We do not process further in case # we don't know what we are dealing with if scope == 'unknown': return make_response('Wrong repository', 403) if self.action == 'opened' and scope == 'public': # we are setting labels on each new open issues try: self.tag_as_public() except HTTPError as e: msg_log(f'public:opened labels failed ({e})', self.number) return oops() else: return make_response('gracias, amigo.', 200) elif (self.action == 'milestoned' and scope == 'public' and self.milestoned_with == 'needscontact'): # add a comment with a link to outreach template generator # when issue is moved to needscontact try: self.comment_outreach_generator_uri() except HTTPError as e: msg_log(f'comment failed ({e})', self.number) return oops() else: return make_response('outreach generator url added', 200) elif self.action == 'opened' and scope == 'private': # webcompat-bot needs to comment public URL of the issue # and we try to classify the issue using bugbug try: self.comment_public_uri() except HTTPError as e: msg_log(f'comment failed ({e})', self.number) return oops() try: self.classify() except (HTTPError, ConnectionError) as e: msg_log(f'classification failed ({e})', self.number) return oops() return make_response('public url added and issue classified', 200) elif (self.action == 'milestoned' and scope == 'private' and self.milestoned_with == 'accepted'): # private issue have been moderated and we will make it public try: self.moderate_private_issue() except HTTPError as e: msg_log('private:moving to public failed', self.number) return oops() else: # we didn't get exceptions, so it's safe to close it self.close_private_issue() return make_response('Moderated issue accepted', 200) elif (self.action == 'milestoned' and scope == 'private' and self.milestoned_with == 'accepted: incomplete'): # The private issue has been set to the "accepted: incomplete" # milestone. This will close the public and private issues, and # leave a message for the incomplete issue. try: self.close_public_issue(reason='incomplete') except HTTPError as e: msg_log( 'private:closing public issue as incomplete failed', self.number) return oops() else: # we didn't get exceptions, so it's safe to comment why # it was closed as incomplete, and close it. self.comment_closed_reason(reason='incomplete') self.close_private_issue() return make_response('Moderated issue closed as incomplete', 200) elif (self.action == 'milestoned' and scope == 'private' and self.milestoned_with == 'accepted: invalid'): # The private issue has been set to the "accepted: invalid" # milestone. This will close the public and private issues, and # leave a message for the invalid issue. try: self.close_public_issue(reason='invalid') except HTTPError as e: msg_log( 'private:closing public issue as invalid failed', self.number) return oops() else: # we didn't get exceptions, so it's safe to comment why # it was closed as invalid, and close it. self.comment_closed_reason(reason='invalid') self.close_private_issue() return make_response('Moderated issue closed as invalid', 200) elif (self.action == 'milestoned' and scope == 'private' and self.milestoned_with == 'ml-autoclosed'): # The private issue has been automatically moved to ml-autoclosed # milestone. This will close the public and private issues, and # will replace the content of the public issue with placeholder. try: self.close_public_issue(reason='autoclosed') except HTTPError as e: msg_log( 'private:closing public issue as invalid by ml-bot failed', self.number) return oops() else: # we didn't get exceptions, so it's safe to # close the private issue. self.close_private_issue() return make_response('Issue closed as invalid by ml bot', 200) elif (scope == 'private' and self.action == 'closed' and self.milestone == 'unmoderated'): # The private issue has been closed. It is rejected and the # private and public issues will be closed with a rejected message. try: self.close_public_issue(reason='rejected') except HTTPError as e: msg_log('public rejection failed', self.number) return oops() else: # we didn't get exceptions, so it's safe to close it self.close_private_issue() return make_response('Moderated issue rejected', 200) else: return make_response('Not an interesting hook', 403)