bots: Fix github details bot.
This commit is contained in:
parent
9505778538
commit
b8b38bb1f7
0
bots/github_detail/__init__.py
Normal file
0
bots/github_detail/__init__.py
Normal file
|
@ -1,28 +0,0 @@
|
||||||
# GitHub detail bot
|
|
||||||
This bot links and details issues and pull requests.
|
|
||||||
To use it username 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`
|
|
||||||
|
|
||||||
Both the username mention and the id can occur at any time in the message. You
|
|
||||||
can also mention multiple ids in a single message.
|
|
||||||
|
|
||||||
The bot *requires* a default owner and repository to be configured.
|
|
||||||
The configuration file should be located at `~/.contrib_bots/github_detail.ini`.
|
|
||||||
It should look like this:
|
|
||||||
```ini
|
|
||||||
[GitHub]
|
|
||||||
owner = <repository_owner>
|
|
||||||
repo = <repository>
|
|
||||||
```
|
|
||||||
If you don't want a default repository you can define one that doesn't exist, the bot
|
|
||||||
will fail silently.
|
|
||||||
|
|
||||||
The bot won't reply to any other bots to avoid infinite loops.
|
|
||||||
|
|
||||||
Zulip also has a realm feature which will show information about linked sites.
|
|
||||||
Because of this the bot escapes it's links. Once issue
|
|
||||||
[#2968](https://github.com/zulip/zulip/issues/2968) is resolved this can be
|
|
||||||
changed.
|
|
|
@ -12,72 +12,50 @@ class GithubHandler(object):
|
||||||
referenced in the chat.
|
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_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+)")
|
HANDLE_MESSAGE_REGEX = re.compile("(?:([\w-]+)\/)?([\w-]+)?#(\d+)")
|
||||||
MAX_LENGTH_OF_MESSAGE = 200
|
|
||||||
|
|
||||||
def __init__(self):
|
def initialize(self, bot_handler):
|
||||||
config = six.moves.configparser.ConfigParser()
|
self.config_info = bot_handler.get_config_info('github_detail', optional=True)
|
||||||
with open(self.CONFIG_PATH) as config_file:
|
self.owner = self.config_info.get("owner", False)
|
||||||
config.readfp(config_file)
|
self.repo = self.config_info.get("repo", False)
|
||||||
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 usage(self):
|
def usage(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
return ("This plugin displays details on github issues and pull requests. "
|
return ("This plugin displays details on github issues and pull requests. "
|
||||||
"To reference an issue or pull request usename mention the bot then "
|
"To reference an issue or pull request usename mention the bot then "
|
||||||
"anytime in the message type its id, for example:\n"
|
"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))
|
"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('-bot@zulip.com')
|
|
||||||
|
|
||||||
def format_message(self, details):
|
def format_message(self, details):
|
||||||
# type: (Dict[Text, Union[Text, int, bool]]) -> Text
|
# type: (Dict[Text, Union[Text, int, bool]]) -> Text
|
||||||
number = details['number']
|
number = details['number']
|
||||||
title = details['title']
|
title = details['title']
|
||||||
link = details['html_url']
|
link = details['html_url']
|
||||||
# Truncate if longer than 200 characters.
|
author = details['user']['login']
|
||||||
ellipsis = '...'
|
owner = details['owner']
|
||||||
|
repo = details['repo']
|
||||||
|
|
||||||
if len(details['body']) > self.MAX_LENGTH_OF_MESSAGE + len(ellipsis):
|
description = details['body']
|
||||||
description = "{}{}".format(details['body'][:self.MAX_LENGTH_OF_MESSAGE], ellipsis)
|
|
||||||
else:
|
|
||||||
description = details['body']
|
|
||||||
status = details['state'].title()
|
status = details['state'].title()
|
||||||
|
|
||||||
return '**[{id} | {title}]({link})** - **{status}**\n```quote\n{description}\n```'\
|
message_string = ('**[{owner}/{repo}#{id}]'.format(owner=owner, repo=repo, id=number),
|
||||||
.format(id=number, title=title, link=link, status=status, description=description)
|
'({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):
|
def get_details_from_github(self, owner, repo, number):
|
||||||
# type: (Text, Text, Text) -> Dict[Text, Union[Text, Int, Bool]]
|
# type: (Text, Text, Text) -> Dict[Text, Union[Text, Int, Bool]]
|
||||||
# Gets the details of an issues or pull request
|
# Gets the details of an issues or pull request
|
||||||
|
|
||||||
# Try to get an issue, try to get a pull if that fails
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
self.GITHUB_ISSUE_URL_TEMPLATE.format(owner=owner, repo=repo, id=number))
|
self.GITHUB_ISSUE_URL_TEMPLATE.format(owner=owner, repo=repo, id=number))
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
if r.status_code == 404:
|
if r.status_code == 404:
|
||||||
try:
|
try:
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
|
@ -85,10 +63,8 @@ class GithubHandler(object):
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
if r.status_code != requests.codes.ok:
|
if r.status_code != requests.codes.ok:
|
||||||
return
|
return
|
||||||
|
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
def get_owner_and_repo(self, issue_pr):
|
def get_owner_and_repo(self, issue_pr):
|
||||||
|
@ -100,11 +76,11 @@ class GithubHandler(object):
|
||||||
repo = self.repo
|
repo = self.repo
|
||||||
return (owner, repo)
|
return (owner, repo)
|
||||||
|
|
||||||
def handle_message(self, message, client, state_handler):
|
def handle_message(self, message, bot_handler, state_handler):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
# Send help message
|
# Send help message
|
||||||
if message['content'] == '@**{}** help'.format(client.full_name):
|
if message['content'] == '@**{}** help'.format(bot_handler.full_name):
|
||||||
client.send_message(dict(
|
bot_handler.send_message(dict(
|
||||||
type='stream',
|
type='stream',
|
||||||
to=message['display_recipient'],
|
to=message['display_recipient'],
|
||||||
subject=message['subject'],
|
subject=message['subject'],
|
||||||
|
@ -114,22 +90,23 @@ class GithubHandler(object):
|
||||||
# Capture owner, repo, id
|
# Capture owner, repo, id
|
||||||
issue_prs = re.finditer(
|
issue_prs = re.finditer(
|
||||||
self.HANDLE_MESSAGE_REGEX, message['content'])
|
self.HANDLE_MESSAGE_REGEX, message['content'])
|
||||||
|
|
||||||
bot_messages = []
|
bot_messages = []
|
||||||
for issue_pr in issue_prs:
|
for issue_pr in issue_prs:
|
||||||
owner, repo = self.get_owner_and_repo(issue_pr)
|
owner, repo = self.get_owner_and_repo(issue_pr)
|
||||||
details = self.get_details_from_github(owner, repo, issue_pr.group(3))
|
if owner and repo:
|
||||||
if details is not None:
|
details = self.get_details_from_github(owner, repo, issue_pr.group(3))
|
||||||
bot_messages.append(self.format_message(details))
|
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:
|
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)
|
bot_message = '\n'.join(bot_messages)
|
||||||
|
bot_handler.send_reply(message, bot_message)
|
||||||
client.send_message(dict(
|
|
||||||
type='stream',
|
|
||||||
to=message['display_recipient'],
|
|
||||||
subject=message['subject'],
|
|
||||||
content=bot_message,
|
|
||||||
))
|
|
||||||
|
|
||||||
handler_class = GithubHandler
|
handler_class = GithubHandler
|
||||||
|
|
22
bots/github_detail/readme.md
Normal file
22
bots/github_detail/readme.md
Normal file
|
@ -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>
|
||||||
|
```
|
Loading…
Reference in a new issue