2019-06-24 13:52:37 +00:00
|
|
|
#--depends-on commands
|
|
|
|
#--depends-on config
|
|
|
|
|
2019-06-24 14:38:27 +00:00
|
|
|
import datetime, math, re
|
2019-06-24 13:52:37 +00:00
|
|
|
from src import EventManager, ModuleManager, utils
|
|
|
|
|
|
|
|
COLOR_BRANCH = utils.consts.ORANGE
|
|
|
|
COLOR_REPO = utils.consts.GREY
|
|
|
|
COLOR_POSITIVE = utils.consts.GREEN
|
|
|
|
COLOR_NEUTRAL = utils.consts.LIGHTGREY
|
|
|
|
COLOR_NEGATIVE = utils.consts.RED
|
|
|
|
COLOR_ID = utils.consts.PINK
|
|
|
|
|
2019-10-03 11:09:18 +00:00
|
|
|
REGEX_ISSUE = re.compile(
|
2019-06-26 13:37:26 +00:00
|
|
|
r"https?://github.com/([^/]+)/([^/]+)/(pull|issues)/(\d+)", re.I)
|
2019-10-03 11:09:18 +00:00
|
|
|
REGEX_ISSUE_REF = re.compile(r"(?:\S+(?:\/\S+)?)?#\d+")
|
|
|
|
REGEX_COMMIT_REF = re.compile(r"(?:\S+(?:\/\S+)?)?@[0-9a-fA-F]+")
|
2019-06-26 13:37:26 +00:00
|
|
|
|
2019-10-03 11:09:18 +00:00
|
|
|
API_COMMIT_URL = "https://api.github.com/repos/%s/%s/commits/%s"
|
2019-06-24 13:52:37 +00:00
|
|
|
API_ISSUE_URL = "https://api.github.com/repos/%s/%s/issues/%s"
|
|
|
|
API_PULL_URL = "https://api.github.com/repos/%s/%s/pulls/%s"
|
|
|
|
|
2019-06-28 22:16:05 +00:00
|
|
|
@utils.export("channelset", utils.Setting("github-default-repo",
|
|
|
|
"Set the default github repo for the current channel",
|
|
|
|
example="jesopo/bitbot"))
|
2019-06-28 22:26:53 +00:00
|
|
|
@utils.export("channelset", utils.BoolSetting("auto-github",
|
2019-06-28 22:16:05 +00:00
|
|
|
"Enable/disable automatically getting github issue/PR info"))
|
|
|
|
@utils.export("channelset", utils.IntSetting("auto-github-cooldown",
|
|
|
|
"Set amount of seconds between auto-github duplicates", example="300"))
|
2019-06-24 13:52:37 +00:00
|
|
|
class Module(ModuleManager.BaseModule):
|
2019-10-03 11:09:18 +00:00
|
|
|
def _parse_ref(self, channel, ref, sep):
|
|
|
|
repo, _, number = ref.rpartition(sep)
|
2019-06-24 13:52:37 +00:00
|
|
|
org, _, repo = repo.partition("/")
|
|
|
|
|
|
|
|
default_repo = channel.get_setting("github-default-repo", "")
|
|
|
|
default_org, _, default_repo = default_repo.partition("/")
|
|
|
|
|
|
|
|
if org and not repo:
|
|
|
|
repo = org or default_repo
|
|
|
|
org = default_org
|
|
|
|
else:
|
|
|
|
org = org or default_org
|
|
|
|
repo = repo or default_repo
|
|
|
|
|
|
|
|
if not org or not repo or not number:
|
|
|
|
raise utils.EventError("Please provide username/repo#number")
|
|
|
|
return org, repo, number
|
|
|
|
|
|
|
|
def _short_url(self, url):
|
|
|
|
try:
|
|
|
|
page = utils.http.request("https://git.io", method="POST",
|
|
|
|
post_data={"url": url})
|
|
|
|
return page.headers["Location"]
|
|
|
|
except utils.http.HTTPTimeoutException:
|
|
|
|
self.log.warn(
|
|
|
|
"HTTPTimeoutException while waiting for github short URL", [])
|
|
|
|
return url
|
|
|
|
|
|
|
|
def _change_count(self, n, symbol, color):
|
|
|
|
return utils.irc.color("%s%d" % (symbol, n), color)+utils.irc.bold("")
|
|
|
|
def _added(self, n):
|
|
|
|
return self._change_count(n, "+", COLOR_POSITIVE)
|
|
|
|
def _removed(self, n):
|
|
|
|
return self._change_count(n, "-", COLOR_NEGATIVE)
|
|
|
|
def _modified(self, n):
|
|
|
|
return self._change_count(n, "~", utils.consts.PURPLE)
|
|
|
|
|
2019-09-30 09:35:36 +00:00
|
|
|
def _get(self, url):
|
|
|
|
oauth2_token = self.bot.config.get("github-token", None)
|
|
|
|
headers = {}
|
|
|
|
if not oauth2_token == None:
|
|
|
|
headers["Authorization"] = "token %s" % oauth2_token
|
2019-11-26 13:41:40 +00:00
|
|
|
request = utils.http.Request(url, headers=headers)
|
2019-09-30 09:35:36 +00:00
|
|
|
return utils.http.request(request)
|
|
|
|
|
2019-10-03 11:09:18 +00:00
|
|
|
def _commit(self, username, repository, commit):
|
|
|
|
page = self._get(API_COMMIT_URL % (username, repository, commit))
|
|
|
|
if page and page.code == 200:
|
2019-11-26 13:41:40 +00:00
|
|
|
page = page.json()
|
2019-10-03 11:09:18 +00:00
|
|
|
repo = utils.irc.color("%s/%s" % (username, repository), COLOR_REPO)
|
2019-11-26 13:41:40 +00:00
|
|
|
sha = utils.irc.color(page["sha"][:8], COLOR_ID)
|
2019-10-03 11:09:18 +00:00
|
|
|
return "(%s@%s) %s - %s %s" % (repo, sha,
|
2019-11-26 13:41:40 +00:00
|
|
|
page["author"]["login"], page["commit"]["message"],
|
|
|
|
self._short_url(page["html_url"]))
|
2019-10-03 11:09:18 +00:00
|
|
|
def _parse_commit(self, target, ref):
|
|
|
|
username, repository, commit = self._parse_ref(target, ref, "@")
|
|
|
|
return self._commit(username, repository, commit)
|
|
|
|
|
|
|
|
@utils.hook("received.command.ghcommit")
|
|
|
|
@utils.kwarg("min_args", 1)
|
|
|
|
@utils.kwarg("help", "Get information for a given commit on github")
|
|
|
|
@utils.kwarg("usage", "<organsation>/<repo>@<commit>")
|
|
|
|
def github_commit(self, event):
|
|
|
|
out = self._parse_commit(event["target"], event["args_split"][0])
|
|
|
|
if not out == None:
|
|
|
|
event["stdout"].write(out)
|
|
|
|
else:
|
|
|
|
event["stderr"].write("Commit not found")
|
|
|
|
|
|
|
|
@utils.hook("command.regex")
|
|
|
|
@utils.kwarg("ignore_action", False)
|
|
|
|
@utils.kwarg("command", "github")
|
|
|
|
@utils.kwarg("pattern", REGEX_COMMIT_REF)
|
|
|
|
def commit_regex(self, event):
|
|
|
|
if event["target"].get_setting("auto-github", False):
|
|
|
|
event.eat()
|
|
|
|
ref = event["match"].group(0)
|
|
|
|
if self._auto_github_cooldown(event["target"], ref):
|
2019-10-14 13:03:47 +00:00
|
|
|
try:
|
|
|
|
out = self._parse_commit(event["target"], ref)
|
|
|
|
except utils.EventError:
|
|
|
|
return
|
|
|
|
|
2019-10-03 11:09:18 +00:00
|
|
|
if out:
|
|
|
|
event["stdout"].write(out)
|
|
|
|
|
2019-06-24 13:52:37 +00:00
|
|
|
def _parse_issue(self, page, username, repository, number):
|
|
|
|
repo = utils.irc.color("%s/%s" % (username, repository), COLOR_REPO)
|
|
|
|
number = utils.irc.color("#%s" % number, COLOR_ID)
|
2019-11-26 13:41:40 +00:00
|
|
|
labels = [label["name"] for label in page["labels"]]
|
2019-06-24 13:52:37 +00:00
|
|
|
labels_str = ""
|
|
|
|
if labels:
|
|
|
|
labels_str = "[%s] " % ", ".join(labels)
|
|
|
|
|
2019-11-26 13:41:40 +00:00
|
|
|
url = self._short_url(page["html_url"])
|
2019-06-24 13:52:37 +00:00
|
|
|
|
2019-11-26 13:41:40 +00:00
|
|
|
state = page["state"]
|
2019-06-24 13:52:37 +00:00
|
|
|
if state == "open":
|
|
|
|
state = utils.irc.color("open", COLOR_NEUTRAL)
|
|
|
|
elif state == "closed":
|
|
|
|
state = utils.irc.color("closed", COLOR_NEGATIVE)
|
|
|
|
|
|
|
|
return "(%s issue%s, %s) %s %s%s" % (
|
2019-11-26 13:41:40 +00:00
|
|
|
repo, number, state, page["title"], labels_str, url)
|
2019-06-24 13:52:37 +00:00
|
|
|
def _get_issue(self, username, repository, number):
|
2019-09-30 09:35:36 +00:00
|
|
|
return self._get(API_ISSUE_URL % (username, repository, number))
|
2019-06-24 13:52:37 +00:00
|
|
|
|
|
|
|
@utils.hook("received.command.ghissue", min_args=1)
|
|
|
|
def github_issue(self, event):
|
|
|
|
if event["target"].get_setting("github-hide-prefix", False):
|
2019-11-22 17:30:17 +00:00
|
|
|
event["stdout"].prefix = None
|
|
|
|
event["stderr"].prefix = None
|
2019-06-24 13:52:37 +00:00
|
|
|
|
|
|
|
username, repository, number = self._parse_ref(
|
2019-10-03 11:09:18 +00:00
|
|
|
event["target"], event["args_split"][0], "#")
|
|
|
|
if not number.isdigit():
|
|
|
|
raise utils.EventError("Issue number must be a number")
|
2019-06-24 13:52:37 +00:00
|
|
|
|
|
|
|
page = self._get_issue(username, repository, number)
|
|
|
|
if page and page.code == 200:
|
2019-11-26 13:41:40 +00:00
|
|
|
self._parse_issue(page.json(), username, repository, number)
|
2019-06-24 13:52:37 +00:00
|
|
|
else:
|
|
|
|
event["stderr"].write("Could not find issue")
|
|
|
|
|
|
|
|
def _parse_pull(self, page, username, repository, number):
|
|
|
|
repo = utils.irc.color("%s/%s" % (username, repository), COLOR_REPO)
|
|
|
|
number = utils.irc.color("#%s" % number, COLOR_ID)
|
2019-11-26 13:41:40 +00:00
|
|
|
branch_from = page["head"]["label"]
|
|
|
|
branch_to = page["base"]["label"]
|
|
|
|
added = self._added(page["additions"])
|
|
|
|
removed = self._removed(page["deletions"])
|
|
|
|
url = self._short_url(page["html_url"])
|
|
|
|
|
|
|
|
state = page["state"]
|
|
|
|
if page["merged"]:
|
2019-06-24 13:52:37 +00:00
|
|
|
state = utils.irc.color("merged", COLOR_POSITIVE)
|
|
|
|
elif state == "open":
|
|
|
|
state = utils.irc.color("open", COLOR_NEUTRAL)
|
|
|
|
elif state == "closed":
|
|
|
|
state = utils.irc.color("closed", COLOR_NEGATIVE)
|
|
|
|
|
|
|
|
return "(%s PR%s, %s) %s → %s [%s/%s] %s %s" % (
|
|
|
|
repo, number, state, branch_from, branch_to, added, removed,
|
2019-11-26 13:41:40 +00:00
|
|
|
page["title"], url)
|
2019-06-24 13:52:37 +00:00
|
|
|
def _get_pull(self, username, repository, number):
|
2019-09-30 09:35:36 +00:00
|
|
|
return self._get(API_PULL_URL % (username, repository, number))
|
2019-06-24 13:52:37 +00:00
|
|
|
@utils.hook("received.command.ghpull", min_args=1)
|
|
|
|
def github_pull(self, event):
|
|
|
|
if event["target"].get_setting("github-hide-prefix", False):
|
2019-11-22 17:30:17 +00:00
|
|
|
event["stdout"].prefix = None
|
|
|
|
event["stderr"].prefix = None
|
2019-06-24 13:52:37 +00:00
|
|
|
|
|
|
|
username, repository, number = self._parse_ref(
|
2019-10-03 11:09:18 +00:00
|
|
|
event["target"], event["args_split"][0], "#")
|
|
|
|
if not number.isdigit():
|
|
|
|
raise utils.EventError("PR number must be a number")
|
|
|
|
|
2019-06-24 13:52:37 +00:00
|
|
|
page = self._get_pull(username, repository, number)
|
|
|
|
|
|
|
|
if page and page.code == 200:
|
2019-11-26 13:41:40 +00:00
|
|
|
self._parse_pull(page.json(), username, repository, number)
|
2019-06-24 13:52:37 +00:00
|
|
|
else:
|
|
|
|
event["stderr"].write("Could not find pull request")
|
|
|
|
|
|
|
|
def _get_info(self, target, ref):
|
2019-10-03 11:09:18 +00:00
|
|
|
username, repository, number = self._parse_ref(target, ref, "#")
|
|
|
|
if not number.isdigit():
|
|
|
|
raise utils.EventError("PR number must be a number")
|
|
|
|
|
2019-06-24 13:52:37 +00:00
|
|
|
page = self._get_issue(username, repository, number)
|
|
|
|
if page and page.code == 200:
|
2019-11-26 13:41:40 +00:00
|
|
|
page = page.json()
|
|
|
|
if "pull_request" in page:
|
2019-06-24 13:52:37 +00:00
|
|
|
pull = self._get_pull(username, repository, number)
|
2019-11-26 13:41:40 +00:00
|
|
|
return self._parse_pull(pull.json(), username, repository,
|
|
|
|
number)
|
2019-06-24 13:52:37 +00:00
|
|
|
else:
|
|
|
|
return self._parse_issue(page, username, repository, number)
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@utils.hook("received.command.gh", alias_of="github")
|
|
|
|
@utils.hook("received.command.github", min_args=1)
|
|
|
|
def github(self, event):
|
|
|
|
if event["target"].get_setting("github-hide-prefix", False):
|
2019-11-22 17:30:17 +00:00
|
|
|
event["stdout"].prefix = None
|
|
|
|
event["stderr"].prefix = None
|
2019-06-24 13:52:37 +00:00
|
|
|
result = self._get_info(event["target"], event["args_split"][0])
|
|
|
|
if not result == None:
|
|
|
|
event["stdout"].write(result)
|
|
|
|
else:
|
|
|
|
event["stderr"].write("Issue/PR not found")
|
|
|
|
|
|
|
|
def _cache_ref(self, ref):
|
|
|
|
return "auto-github-%s" % ref.lower()
|
|
|
|
def _auto_github_cooldown(self, channel, ref):
|
|
|
|
cooldown = channel.get_setting("auto-github-cooldown", None)
|
|
|
|
if not cooldown == None:
|
|
|
|
cache = self._cache_ref(ref)
|
|
|
|
if not self.bot.cache.has_item(cache):
|
2019-09-02 13:07:26 +00:00
|
|
|
self.bot.cache.temporary_cache(cache, True, cooldown)
|
2019-06-24 13:52:37 +00:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
2019-06-26 13:37:26 +00:00
|
|
|
@utils.hook("command.regex")
|
|
|
|
@utils.kwarg("ignore_action", False)
|
|
|
|
@utils.kwarg("command", "github")
|
2019-10-03 11:09:18 +00:00
|
|
|
@utils.kwarg("pattern", REGEX_ISSUE)
|
2019-06-24 13:52:37 +00:00
|
|
|
def url_regex(self, event):
|
|
|
|
if event["target"].get_setting("auto-github", False):
|
|
|
|
event.eat()
|
|
|
|
ref = "%s/%s#%s" % (event["match"].group(1),
|
|
|
|
event["match"].group(2), event["match"].group(4))
|
|
|
|
if self._auto_github_cooldown(event["target"], ref):
|
|
|
|
try:
|
|
|
|
result = self._get_info(event["target"], ref)
|
|
|
|
except utils.EventError:
|
|
|
|
return
|
|
|
|
if result:
|
|
|
|
if event["target"].get_setting("github-hide-prefix", False):
|
2019-11-22 17:30:17 +00:00
|
|
|
event["stdout"].prefix = None
|
2019-06-24 13:52:37 +00:00
|
|
|
event["stdout"].write(result)
|
|
|
|
|
2019-06-26 13:37:26 +00:00
|
|
|
@utils.hook("command.regex")
|
|
|
|
@utils.kwarg("ignore_action", False)
|
|
|
|
@utils.kwarg("command", "github")
|
2019-10-03 11:09:18 +00:00
|
|
|
@utils.kwarg("pattern", REGEX_ISSUE_REF)
|
2019-06-24 13:52:37 +00:00
|
|
|
def ref_regex(self, event):
|
|
|
|
if event["target"].get_setting("auto-github", False):
|
|
|
|
event.eat()
|
|
|
|
ref = event["match"].group(0)
|
|
|
|
if self._auto_github_cooldown(event["target"], ref):
|
|
|
|
try:
|
|
|
|
result = self._get_info(event["target"],
|
|
|
|
event["match"].group(0))
|
|
|
|
except utils.EventError:
|
|
|
|
return
|
|
|
|
if result:
|
|
|
|
if event["target"].get_setting("github-hide-prefix", False):
|
2019-11-22 17:30:17 +00:00
|
|
|
event["stdout"].prefix = None
|
2019-06-24 13:52:37 +00:00
|
|
|
event["stdout"].write(result)
|