diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 038a804c..5958d15f 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -10,11 +10,16 @@ class PRAgent: self.installation_id = installation_id async def handle_request(self, pr_url, request): - if 'please review' in request.lower(): + if 'please review' in request.lower() or 'review' == request.lower().strip() or len(request) == 0: reviewer = PRReviewer(pr_url, self.installation_id) await reviewer.review() - elif 'please answer' in request.lower(): - question = re.split(r'(?i)please answer', request)[1].strip() + else: + if "please answer" in request.lower(): + question = re.split(r'(?i)please answer', request)[1].strip() + elif request.lower().strip().startswith("answer"): + question = re.split(r'(?i)answer', request)[1].strip() + else: + question = request answerer = PRQuestions(pr_url, question, self.installation_id) await answerer.answer() diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index f2c0296f..a03d0bee 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -24,6 +24,7 @@ class GithubProvider: self.repo = None self.pr_num = None self.pr = None + self.github_user_id = None if pr_url: self.set_pr(pr_url) @@ -42,6 +43,8 @@ class GithubProvider: def publish_comment(self, pr_comment: str, is_temporary: bool = False): response = self.pr.create_issue_comment(pr_comment) + if hasattr(response, "user") and hasattr(response.user, "login"): + self.github_user_id = response.user.login response.is_temporary = is_temporary if not hasattr(self.pr, 'comments_list'): self.pr.comments_list = [] @@ -109,6 +112,14 @@ class GithubProvider: def get_pr_branch(self): return self.pr.head.ref + def get_user_id(self): + if not self.github_user_id: + try: + self.github_user_id = self.github_client.get_user().login + except Exception as e: + logging.exception(f"Failed to get user id, error: {e}") + return self.github_user_id + def get_notifications(self, since: datetime): deployment_type = settings.get("GITHUB.DEPLOYMENT_TYPE", "user") diff --git a/pr_agent/servers/github_app_webhook.py b/pr_agent/servers/github_app_webhook.py index 5634406f..6dc5782b 100644 --- a/pr_agent/servers/github_app_webhook.py +++ b/pr_agent/servers/github_app_webhook.py @@ -40,7 +40,7 @@ async def handle_request(body): if "comment" not in body: return {} comment_body = body.get("comment", {}).get("body", None) - if "says 'Please" in comment_body: + if 'sender' in body and 'login' in body['sender'] and 'bot' in body['sender']['login']: return {} if "issue" not in body and "pull_request" not in body["issue"]: return {} diff --git a/pr_agent/servers/github_polling.py b/pr_agent/servers/github_polling.py index 56e8173e..e1e6fa84 100644 --- a/pr_agent/servers/github_polling.py +++ b/pr_agent/servers/github_polling.py @@ -7,6 +7,7 @@ import aiohttp from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import settings +from pr_agent.git_providers import get_git_provider logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) NOTIFICATION_URL = "https://api.github.com/notifications" @@ -21,6 +22,8 @@ def now() -> str: async def polling_loop(): since = [now()] last_modified = [None] + git_provider = get_git_provider()() + user_id = git_provider.get_user_id() try: deployment_type = settings.github.deployment_type token = settings.github.user_token @@ -58,12 +61,18 @@ async def polling_loop(): async with session.get(latest_comment, headers=headers) as comment_response: if comment_response.status == 200: comment = await comment_response.json() + if 'user' in comment and 'login' in comment['user']: + if comment['user']['login'] == user_id: + continue comment_body = comment['body'] if 'body' in comment else '' commenter_github_user = comment['user']['login'] if 'user' in comment else '' logging.info(f"Commenter: {commenter_github_user}\nComment: {comment_body}") - if comment_body.strip().startswith("@"): - agent = PRAgent() - await agent.handle_request(pr_url, comment_body) + user_tag = "@" + user_id + if user_tag not in comment_body: + continue + rest_of_comment = comment_body.split(user_tag)[1].strip() + agent = PRAgent() + await agent.handle_request(pr_url, rest_of_comment) elif response.status != 304: print(f"Failed to fetch notifications. Status code: {response.status}") diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 4e2fbab3..a7ebc2ef 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -90,9 +90,18 @@ class PRReviewer: data['PR Analysis']['Security concerns'] = val markdown_text = convert_to_markdown(data) - markdown_text += "\n## How to use\n\n" - markdown_text += "```\nMention '@pr-agent' in a pr comment to get another review.\n" - markdown_text += "Mention '@pr-agent ' in a pr comment to ask a question about this PR.\n```\n" + user = self.git_provider.get_user_id() + markdown_text += "\n### How to use\n" + if user and '[bot]' not in user: + markdown_text += f"> Tag me in a comment '@{user}' to ask for a new review after you update the PR.\n" + markdown_text += "> You can also tag me and ask any question, " \ + f"for example '@{user} is the PR ready for merge?'" + else: + markdown_text += "> Add a comment that says 'review' to ask for a new review " \ + "after you update the PR.\n" + markdown_text += "> You can also add a comment that says 'answer QUESTION', " \ + "for example 'answer is the PR ready for merge?'" + if settings.config.verbosity_level >= 2: logging.info(f"Markdown response:\n{markdown_text}") return markdown_text