Interactive bots: Create Github Issues bot.
This commit is contained in:
parent
e2ed990746
commit
1b58c13d91
25
contrib_bots/lib/github.py
Normal file
25
contrib_bots/lib/github.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# The purpose of this file is to handle all requests
|
||||||
|
# to the Github API, which will make sure that all
|
||||||
|
# requests are to the same account, and that all requests
|
||||||
|
# authenticated correctly and securely
|
||||||
|
# The sole purpose of this is to authenticate, and it will
|
||||||
|
# return the requests session that is properly authenticated
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import six.moves.configparser
|
||||||
|
|
||||||
|
CONFIG_FILE = '~/.github-issue-bot/github-issue-bot.conf'
|
||||||
|
|
||||||
|
def auth():
|
||||||
|
# Sets up the configParser to read the .conf file
|
||||||
|
config = six.moves.configparser.ConfigParser()
|
||||||
|
# Reads the config file
|
||||||
|
config.read([os.path.expanduser(CONFIG_FILE)])
|
||||||
|
# Gets the properties from the config file
|
||||||
|
oauth_token = config.get('github', 'github_token')
|
||||||
|
username = config.get('github', 'github_username')
|
||||||
|
# Creates and authorises a requests session
|
||||||
|
session = requests.session()
|
||||||
|
session.auth = (username, oauth_token)
|
||||||
|
# Returns the session
|
||||||
|
return session
|
125
contrib_bots/lib/github_issues.py
Normal file
125
contrib_bots/lib/github_issues.py
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from future import standard_library
|
||||||
|
standard_library.install_aliases()
|
||||||
|
from . import github
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import six.moves.configparser
|
||||||
|
import urllib.request, urllib.error, urllib.parse
|
||||||
|
|
||||||
|
class IssueHandler(object):
|
||||||
|
'''
|
||||||
|
This plugin facilitates sending issues to github, when
|
||||||
|
an item is prefixed with '@issue' or '@bug'
|
||||||
|
|
||||||
|
It will also write items to the issues stream, as well
|
||||||
|
as reporting it to github
|
||||||
|
'''
|
||||||
|
|
||||||
|
URL = 'https://api.github.com/repos/{}/{}/issues'
|
||||||
|
CHARACTER_LIMIT = 70
|
||||||
|
CONFIG_FILE = '~/.github-issue-bot/github-issue-bot.conf'
|
||||||
|
REPO_NAME = ''
|
||||||
|
REPO_OWNER = ''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# gets token from config file
|
||||||
|
# Token at CONFIG_FILE address
|
||||||
|
config = six.moves.configparser.ConfigParser()
|
||||||
|
config.read([os.path.expanduser(self.CONFIG_FILE)])
|
||||||
|
|
||||||
|
self.REPO_NAME = config.get('github', 'github_repo')
|
||||||
|
self.REPO_OWNER = config.get('github', 'github_repo_owner')
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
return '''
|
||||||
|
This plugin will allow users to flag messages
|
||||||
|
as being issues with Zulip by using te prefix '@issue'
|
||||||
|
|
||||||
|
Before running this, make sure to create a stream
|
||||||
|
called "issues" that your API user can send to.
|
||||||
|
|
||||||
|
Also, make sure that the credentials of the github bot have
|
||||||
|
been typed in correctly, that there is a personal access token
|
||||||
|
with access to public repositories ONLY,
|
||||||
|
and that the repository name is entered correctly.
|
||||||
|
|
||||||
|
Check ~/.github-issue-bot/github-issue-bot.conf, and make sure there are
|
||||||
|
github_repo (The name of the repo to post to)
|
||||||
|
github_repo_owner (The owner of the repo to post to)
|
||||||
|
github_username (The username of the github bot)
|
||||||
|
github_token (The personal access token for the github bot)
|
||||||
|
'''
|
||||||
|
|
||||||
|
def triage_message(self, message):
|
||||||
|
# return True if we want to (possibly) respond to this message
|
||||||
|
original_content = message['content']
|
||||||
|
# This next line of code is defensive, as we
|
||||||
|
# never want to get into an infinite loop of posting follow
|
||||||
|
# ups for own follow ups!
|
||||||
|
if message['display_recipient'] == 'issue':
|
||||||
|
return False
|
||||||
|
is_issue = original_content.startswith('@issue')
|
||||||
|
return is_issue
|
||||||
|
|
||||||
|
def handle_message(self, message, client, state_handler):
|
||||||
|
|
||||||
|
original_content = message['content']
|
||||||
|
original_sender = message['sender_email']
|
||||||
|
|
||||||
|
new_content = original_content.replace('@issue', 'by {}:'.format(original_sender,))
|
||||||
|
# gets the repo url
|
||||||
|
url_new = self.URL.format(self.REPO_OWNER, self.REPO_NAME)
|
||||||
|
|
||||||
|
# signs into github using the provided username and password
|
||||||
|
session = github.auth()
|
||||||
|
|
||||||
|
# Gets rid of the @issue in the issue title
|
||||||
|
issue_title = message['content'].replace('@issue', '').strip()
|
||||||
|
issue_content = ''
|
||||||
|
new_issue_title = ''
|
||||||
|
for part_of_title in issue_title.split():
|
||||||
|
if len(new_issue_title) < self.CHARACTER_LIMIT:
|
||||||
|
new_issue_title += '{} '.format(part_of_title)
|
||||||
|
else:
|
||||||
|
issue_content += '{} '.format(part_of_title)
|
||||||
|
|
||||||
|
new_issue_title = new_issue_title.strip()
|
||||||
|
issue_content = issue_content.strip()
|
||||||
|
new_issue_title += '...'
|
||||||
|
|
||||||
|
# Creates the issue json, that is transmitted to the github api servers
|
||||||
|
issue = {
|
||||||
|
'title': new_issue_title,
|
||||||
|
'body': '{} **Sent by [{}](https://chat.zulip.org/#) from zulip**'.format(issue_content, original_sender),
|
||||||
|
'assignee': '',
|
||||||
|
'milestone': 'none',
|
||||||
|
'labels': [''],
|
||||||
|
}
|
||||||
|
# Sends the HTTP post request
|
||||||
|
r = session.post(url_new, json.dumps(issue))
|
||||||
|
|
||||||
|
if r.ok:
|
||||||
|
# sends the message onto the 'issues' stream so it can be seen by zulip users
|
||||||
|
client.send_message(dict(
|
||||||
|
type='stream',
|
||||||
|
to='issues',
|
||||||
|
subject=message['sender_email'],
|
||||||
|
# Adds a check mark so that the user can verify if it has been sent
|
||||||
|
content='{} :heavy_check_mark:'.format(new_content),
|
||||||
|
))
|
||||||
|
return
|
||||||
|
# This means that the issue has not been sent
|
||||||
|
# sends the message onto the 'issues' stream so it can be seen by zulip users
|
||||||
|
client.send_message(dict(
|
||||||
|
type='stream',
|
||||||
|
to='issues',
|
||||||
|
subject=message['sender_email'],
|
||||||
|
# Adds a cross so that the user can see that it has failed, and provides a link to a
|
||||||
|
# google search that can (hopefully) direct them to the error
|
||||||
|
content='{} :x: Code: [{}](https://www.google.com/search?q=Github HTTP {} Error {})'
|
||||||
|
.format(new_content, r.status_code, r.status_code, r.content),
|
||||||
|
))
|
||||||
|
|
||||||
|
handler_class = IssueHandler
|
Loading…
Reference in a new issue