forked from zulip/zulip
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
686acbc
commit ec4276c
Showing
4 changed files
with
52 additions
and
81 deletions.
There are no files selected for viewing
Empty file.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,83 +12,59 @@ class GithubHandler(object): | |
referenced in the chat. | ||
''' | ||
|
||
CONFIG_PATH = os.path.expanduser('~/.contrib_bots/github_detail.ini') | ||
GITHUB_ISSUE_URL_TEMPLATE = 'https://api.github.com/repos/{owner}/{repo}/issues/{id}' | ||
GITHUB_PULL_URL_TEMPLATE = 'https://api.github.com/repos/{owner}/{repo}/pulls/{id}' | ||
HANDLE_MESSAGE_REGEX = re.compile("(?:([\w-]+)\/)?([\w-]+)?#(\d+)") | ||
MAX_LENGTH_OF_MESSAGE = 200 | ||
|
||
def __init__(self): | ||
config = six.moves.configparser.ConfigParser() | ||
with open(self.CONFIG_PATH) as config_file: | ||
config.readfp(config_file) | ||
if config.get('GitHub', 'owner'): | ||
self.owner = config.get('GitHub', 'owner') | ||
else: | ||
# Allowing undefined default repos would require multiple triage_message regexs. | ||
# It's simpler to require them to be defined. | ||
sys.exit('Default owner not defined') | ||
|
||
if config.get('GitHub', 'repo'): | ||
self.repo = config.get('GitHub', 'repo') | ||
else: | ||
sys.exit('Default repo not defined') | ||
def initialize(self, bot_handler): | ||
self.config_info = bot_handler.get_config_info('github_detail', optional=True) | ||
self.owner = self.config_info.get("owner", False) | ||
self.repo = self.config_info.get("repo", False) | ||
|
||
def usage(self): | ||
# type: () -> None | ||
return ("This plugin displays details on github issues and pull requests. " | ||
"To reference an issue or pull request usename mention the bot then " | ||
"anytime in the message type its id, for example:\n" | ||
"@**Github detail** #3212 zulip/#3212 zulip/zulip#3212\n" | ||
"@**Github detail** #3212 zulip#3212 zulip/zulip#3212\n" | ||
"The default owner is {} and the default repo is {}.".format(self.owner, self.repo)) | ||
|
||
def triage_message(self, message, client): | ||
# type: () -> bool | ||
# Check the message contains a username mention, an issue idi | ||
# or 'help', and that we're not replying to another bot. | ||
regex = "(?:@(?:\*\*){}).+?(?:#\d+)|(?:help)".format(re.escape(client.full_name)) | ||
return re.search(regex, message['content']) and not message['sender_email'].endswith('[email protected]') | ||
|
||
def format_message(self, details): | ||
# type: (Dict[Text, Union[Text, int, bool]]) -> Text | ||
number = details['number'] | ||
title = details['title'] | ||
link = details['html_url'] | ||
# Truncate if longer than 200 characters. | ||
ellipsis = '...' | ||
author = details['user']['login'] | ||
owner = details['owner'] | ||
repo = details['repo'] | ||
|
||
if len(details['body']) > self.MAX_LENGTH_OF_MESSAGE + len(ellipsis): | ||
description = "{}{}".format(details['body'][:self.MAX_LENGTH_OF_MESSAGE], ellipsis) | ||
else: | ||
description = details['body'] | ||
description = details['body'] | ||
status = details['state'].title() | ||
|
||
return '**[{id} | {title}]({link})** - **{status}**\n```quote\n{description}\n```'\ | ||
.format(id=number, title=title, link=link, status=status, description=description) | ||
message_string = ('**[{owner}/{repo}#{id}]'.format(owner=owner, repo=repo, id=number), | ||
'({link}) - {title}**\n'.format(title=title, link=link), | ||
'Created by **[{author}](https://github.com/{author})**\n'.format(author=author), | ||
'Status - **{status}**\n```quote\n{description}\n```'.format(status=status, description=description)) | ||
return ''.join(message_string) | ||
|
||
def get_details_from_github(self, owner, repo, number): | ||
# type: (Text, Text, Text) -> Dict[Text, Union[Text, Int, Bool]] | ||
# Gets the details of an issues or pull request | ||
|
||
# Try to get an issue, try to get a pull if that fails | ||
try: | ||
r = requests.get( | ||
self.GITHUB_ISSUE_URL_TEMPLATE.format(owner=owner, repo=repo, id=number)) | ||
except requests.exceptions.RequestException as e: | ||
logging.exception(e) | ||
return | ||
|
||
if r.status_code == 404: | ||
try: | ||
r = requests.get( | ||
self.GITHUB_PULL_URL_TEMPLATE.format(owner=owner, repo=repo, id=number)) | ||
except requests.exceptions.RequestException as e: | ||
logging.exception(e) | ||
return | ||
|
||
if r.status_code != requests.codes.ok: | ||
return | ||
|
||
return r.json() | ||
|
||
def get_owner_and_repo(self, issue_pr): | ||
|
@@ -100,11 +76,11 @@ def get_owner_and_repo(self, issue_pr): | |
repo = self.repo | ||
return (owner, repo) | ||
|
||
def handle_message(self, message, client, state_handler): | ||
def handle_message(self, message, bot_handler, state_handler): | ||
# type: () -> None | ||
# Send help message | ||
if message['content'] == '@**{}** help'.format(client.full_name): | ||
client.send_message(dict( | ||
if message['content'] == '@**{}** help'.format(bot_handler.full_name): | ||
bot_handler.send_message(dict( | ||
type='stream', | ||
to=message['display_recipient'], | ||
subject=message['subject'], | ||
|
@@ -114,22 +90,23 @@ def handle_message(self, message, client, state_handler): | |
# Capture owner, repo, id | ||
issue_prs = re.finditer( | ||
self.HANDLE_MESSAGE_REGEX, message['content']) | ||
|
||
bot_messages = [] | ||
for issue_pr in issue_prs: | ||
owner, repo = self.get_owner_and_repo(issue_pr) | ||
details = self.get_details_from_github(owner, repo, issue_pr.group(3)) | ||
if details is not None: | ||
bot_messages.append(self.format_message(details)) | ||
if owner and repo: | ||
details = self.get_details_from_github(owner, repo, issue_pr.group(3)) | ||
if details is not None: | ||
details['owner'] = owner | ||
details['repo'] = repo | ||
bot_messages.append(self.format_message(details)) | ||
else: | ||
bot_messages.append("Failed to find issue/pr: {owner}/{repo}#{id}" | ||
.format(owner=owner, repo=repo, id=issue_pr.group(3))) | ||
else: | ||
bot_messages.append("Failed to find issue/pr: {owner}/{repo}#{id}".format(owner=owner, repo=repo, id=issue_pr.group(3))) | ||
bot_messages.append("Failed to detect owner and repository name.") | ||
if len(bot_messages) == 0: | ||
bot_messages.append("Failed to find any issue or PR.") | ||
bot_message = '\n'.join(bot_messages) | ||
|
||
client.send_message(dict( | ||
type='stream', | ||
to=message['display_recipient'], | ||
subject=message['subject'], | ||
content=bot_message, | ||
)) | ||
bot_handler.send_reply(message, bot_message) | ||
|
||
handler_class = GithubHandler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# GitHub detail bot | ||
|
||
This bot links and details issues and pull requests. | ||
To use it @-mention the bot then type an id: | ||
Ids can be specified in three different forms: | ||
- Id only: `#2000` | ||
- Repository and id: `zulip#2000` | ||
- Owner, repository and id `zulip/zulip#2000` | ||
|
||
The id can occur at any time in the message. You | ||
can also mention multiple ids in a single message. For example: | ||
|
||
`@**GitHub Detail Bot** find me #5176 and zulip/zulip#4534 .` | ||
|
||
You can configure a default owner and repository. | ||
The configuration file should be located at `api/bots/github_detail/github_detail.conf`. | ||
It should look like this: | ||
```ini | ||
[github_detail] | ||
owner = <repository owner> | ||
repo = <repository name> | ||
``` |