605 lines
25 KiB
Python
605 lines
25 KiB
Python
# -*- Encoding: utf-8 -*-
|
|
###
|
|
# Copyright (c) 2005-2007 Dennis Kaarsemaker
|
|
# Copyright (c) 2008-2010 Terence Simpson
|
|
# Copyright (c) 2017- Krytarik Raido
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of version 2 of the GNU General Public License as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
###
|
|
|
|
from supybot.commands import *
|
|
import supybot.utils as utils
|
|
import supybot.ircutils as ircutils
|
|
import supybot.ircdb as ircdb
|
|
import supybot.callbacks as callbacks
|
|
import supybot.conf as conf
|
|
import supybot.registry as registry
|
|
import supybot.log as supylog
|
|
|
|
import re, time
|
|
|
|
from .config import registerBugtracker, default_bugtrackers
|
|
from .trackers import defined_bugtrackers
|
|
from . import trackers
|
|
|
|
def defaultIgnored(hostmask):
|
|
if not conf.supybot.defaultIgnore():
|
|
return False
|
|
try:
|
|
user = ircdb.users.getUser(hostmask)
|
|
except KeyError:
|
|
return True
|
|
return False
|
|
|
|
def checkIgnored(hostmask, channel):
|
|
try:
|
|
user = ircdb.users.getUser(hostmask)
|
|
try:
|
|
if user._checkCapability('trusted'):
|
|
return False
|
|
except KeyError:
|
|
pass
|
|
if user.ignore:
|
|
return True
|
|
except KeyError:
|
|
pass
|
|
if ircdb.ignores.checkIgnored(hostmask):
|
|
return True
|
|
if channel:
|
|
c = ircdb.channels.getChannel(channel)
|
|
if c.checkIgnored(hostmask):
|
|
return True
|
|
return False
|
|
|
|
def checkAddressed(text, channel):
|
|
if channel:
|
|
if text[0] in str(conf.supybot.reply.whenAddressedBy.chars.get(channel)):
|
|
return True
|
|
elif text[0] in conf.supybot.reply.whenAddressedBy.chars():
|
|
return True
|
|
return False
|
|
|
|
class Bugtracker(callbacks.PluginRegexp):
|
|
"""Show a link to a bug report with a brief description"""
|
|
threaded = True
|
|
callBefore = ('URL')
|
|
regexps = ('bugSnarfer', 'bugUrlSnarfer', 'commitSnarfer', 'commitUrlSnarfer', 'cveSnarfer', 'oopsSnarfer')
|
|
|
|
def __init__(self, irc):
|
|
self.__parent = super(Bugtracker, self)
|
|
self.__parent.__init__(irc)
|
|
self.set_trackers()
|
|
self.shown = {}
|
|
|
|
def set_trackers(self):
|
|
self.db = ircutils.IrcDict()
|
|
self.aliases = {}
|
|
trackers = self.registryValue('bugtrackers')
|
|
if not trackers:
|
|
for (name, (url, description, trackertype, aliases)) in list(default_bugtrackers.items()):
|
|
registerBugtracker(name, url, description, trackertype, aliases)
|
|
for name in trackers:
|
|
tracker = self.registryValue('bugtrackers', value=False).get(name)
|
|
if tracker.trackertype() in defined_bugtrackers:
|
|
self.db[name] = defined_bugtrackers[tracker.trackertype()](name, tracker.url(),
|
|
tracker.description(), tracker.trackertype(), tracker.aliases())
|
|
for a in tracker.aliases():
|
|
self.aliases[a] = name
|
|
else:
|
|
supylog.warning("Bugtracker: Unknown trackertype '%s' (%s)" % (trackers[name].trackertype(), name))
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
|
|
def is_ok(self, channel, network, tracker, bugid):
|
|
"""Flood/repeat protection"""
|
|
now = time.time()
|
|
for k in list(self.shown.keys()):
|
|
if self.shown[k] < now - self.registryValue('repeatdelay', channel, network):
|
|
self.shown.pop(k)
|
|
if (channel, tracker, bugid) not in self.shown:
|
|
self.shown[(channel, tracker, bugid)] = now
|
|
return True
|
|
return False
|
|
|
|
def add(self, irc, msg, args, name, trackertype, url, description):
|
|
"""<name> <type> <url> [<description>]
|
|
|
|
Add a bugtracker to the list of defined bugtrackers. Currently supported types are
|
|
Launchpad, Debbugs, Bugzilla, SourceForge, GitHub, GitLab, Gitea, CGit, Mantis, and Trac.
|
|
<name> will be used to reference the bugtracker in all commands.
|
|
Unambiguous abbreviations of it will also be accepted.
|
|
<description> will be used to reference the bugtracker in the
|
|
query result. If not given, it defaults to <name>.
|
|
"""
|
|
name = name.lower()
|
|
if not description:
|
|
description = name
|
|
if url[-1] == '/':
|
|
url = url[:-1]
|
|
trackertype = trackertype.lower()
|
|
if trackertype in defined_bugtrackers:
|
|
self.db[name] = defined_bugtrackers[trackertype](name, url, description, trackertype)
|
|
else:
|
|
irc.error("Bugtrackers of type '%s' are not understood" % trackertype)
|
|
return
|
|
registerBugtracker(name, url, description, trackertype)
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
irc.replySuccess()
|
|
add = wrap(add, [('checkCapability', 'admin'), 'something', 'something', 'url', additional('text')])
|
|
|
|
def remove(self, irc, msg, args, name):
|
|
"""<abbreviation>
|
|
|
|
Remove the bugtracker associated with <abbreviation> from the list of
|
|
defined bugtrackers.
|
|
"""
|
|
try:
|
|
name = self.shorthand[name.lower()]
|
|
for a in self.db[name].aliases:
|
|
del self.aliases[a]
|
|
del self.db[name]
|
|
trackers = self.registryValue('bugtrackers')
|
|
if name in trackers:
|
|
trackers.remove(name)
|
|
self.registryValue('bugtrackers', value=False).unregister(name)
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
irc.replySuccess()
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % name)
|
|
remove = wrap(remove, [('checkCapability', 'admin'), 'text'])
|
|
|
|
def rename(self, irc, msg, args, oldname, newname, newdesc):
|
|
"""<oldname> <newname> [<newdescription>]
|
|
|
|
Rename the bugtracker associated with <oldname> to <newname>,
|
|
optionally with <newdescription>.
|
|
"""
|
|
try:
|
|
oldname = self.shorthand[oldname.lower()]
|
|
newname = newname.lower()
|
|
tracker = self.db[oldname]
|
|
d = tracker.description
|
|
if newdesc:
|
|
d = newdesc
|
|
self.db[newname] = defined_bugtrackers[tracker.trackertype](newname, tracker.url, d, tracker.aliases)
|
|
registerBugtracker(newname, tracker.url, d, tracker.trackertype, tracker.aliases)
|
|
del self.db[oldname]
|
|
trackers = self.registryValue('bugtrackers')
|
|
if oldname in trackers:
|
|
trackers.remove(oldname)
|
|
self.registryValue('bugtrackers', value=False).unregister(oldname)
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
for a in tracker.aliases:
|
|
self.aliases[a] = newname
|
|
irc.replySuccess()
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % oldname)
|
|
rename = wrap(rename, [('checkCapability', 'admin'), 'something', 'something', additional('text')])
|
|
|
|
def alias(self, irc, msg, args, name, alias):
|
|
"""<name> [<alias>]
|
|
|
|
Add an alias to a defined bugtracker, or list any set ones.
|
|
"""
|
|
try:
|
|
name = self.shorthand[name.lower()]
|
|
if not alias:
|
|
a = self.db[name].aliases
|
|
irc.reply('%s: %s' % (name, utils.str.commaAndify(sorted(a)) if a else 'No aliases set'))
|
|
return
|
|
alias = alias.lower()
|
|
self.db[name].aliases.add(alias)
|
|
trackers = self.registryValue('bugtrackers')
|
|
if name in trackers:
|
|
self.registryValue('bugtrackers', value=False).get(name).aliases().add(alias)
|
|
else:
|
|
tracker = self.db[name]
|
|
registerBugtracker(tracker.name, tracker.url, tracker.description, tracker.trackertype, tracker.aliases)
|
|
self.aliases[alias] = name
|
|
irc.replySuccess()
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % name)
|
|
alias = wrap(alias, [('checkCapability', 'admin'), 'something', additional('text')])
|
|
|
|
def unalias(self, irc, msg, args, name, alias):
|
|
"""<name> <alias>
|
|
|
|
Remove an alias from a defined bugtracker.
|
|
"""
|
|
try:
|
|
name = self.shorthand[name.lower()]
|
|
alias = alias.lower()
|
|
try:
|
|
self.db[name].aliases.remove(alias)
|
|
trackers = self.registryValue('bugtrackers')
|
|
if name in trackers:
|
|
self.registryValue('bugtrackers', value=False).get(name).aliases().remove(alias)
|
|
del self.aliases[alias]
|
|
irc.replySuccess()
|
|
except ValueError:
|
|
irc.error("Bugtracker '%s' has no alias '%s' set" % (name, alias))
|
|
return
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % name)
|
|
unalias = wrap(unalias, [('checkCapability', 'admin'), 'something', 'something'])
|
|
|
|
def list(self, irc, msg, args, name):
|
|
"""[<abbreviation>]
|
|
|
|
List defined bugtrackers. If <abbreviation> is specified, list the
|
|
information for that bugtracker.
|
|
"""
|
|
if name:
|
|
try:
|
|
name = self.shorthand[name.lower()]
|
|
tracker = self.db[name]
|
|
irc.reply('%s%s: %s, %s [%s]' % (name, ' (%s)' % ', '.join(sorted(tracker.aliases)) if tracker.aliases else '',
|
|
tracker.description, tracker.url, tracker.__class__.__name__))
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % name)
|
|
else:
|
|
if self.db:
|
|
irc.reply(utils.str.commaAndify(sorted(list(self.db.keys()))))
|
|
else:
|
|
irc.reply('I have no defined bugtrackers.')
|
|
list = wrap(list, [additional('text')])
|
|
|
|
def reset(self, irc, msg, args, name):
|
|
"""[<abbreviation>]
|
|
|
|
Reset defined bugtrackers to defaults. If <abbreviation> is specified,
|
|
reset only that bugtracker.
|
|
"""
|
|
if name:
|
|
try:
|
|
name = self.shorthand[name.lower()]
|
|
for a in self.db[name].aliases:
|
|
del self.aliases[a]
|
|
del self.db[name]
|
|
trackers = self.registryValue('bugtrackers')
|
|
if name in trackers:
|
|
trackers.remove(name)
|
|
self.registryValue('bugtrackers', value=False).unregister(name)
|
|
if name in default_bugtrackers:
|
|
(url, description, trackertype, aliases) = default_bugtrackers[name]
|
|
if trackertype in defined_bugtrackers:
|
|
self.db[name] = defined_bugtrackers[trackertype](name, url, description, trackertype, aliases)
|
|
for a in aliases:
|
|
self.aliases[a] = name
|
|
registerBugtracker(name, url, description, trackertype, aliases)
|
|
else:
|
|
supylog.warning("Bugtracker: Unknown trackertype '%s' (%s)" % (trackers[name].trackertype(), name))
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
except KeyError:
|
|
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
|
|
irc.error(s % name)
|
|
return
|
|
else:
|
|
trackers = self.registryValue('bugtrackers')
|
|
for name in list(trackers)[:]:
|
|
trackers.remove(name)
|
|
self.registryValue('bugtrackers', value=False).unregister(name)
|
|
self.set_trackers()
|
|
irc.replySuccess()
|
|
reset = wrap(reset, [('checkCapability', 'admin'), additional('text')])
|
|
|
|
def inFilter(self, irc, msg):
|
|
if not (msg.prefix and msg.args):
|
|
return msg
|
|
if not ircutils.isUserHostmask(msg.prefix):
|
|
return msg
|
|
if not defaultIgnored(msg.prefix):
|
|
return msg
|
|
if checkIgnored(msg.prefix, msg.channel):
|
|
return msg
|
|
if msg.command == 'PRIVMSG':
|
|
self.doPrivmsg(irc, msg)
|
|
return msg
|
|
|
|
def bugSnarfer(self, irc, msg, match):
|
|
r"(?P<bt>[a-z][^\s:]*(\s+(bug|ticket|issue|pull|pr|merge|mr)('?s)?)?)(:+\s*#?|\s+#?|\s*#)(?P<bug>\d+(?!\d*[-.]\d+)(\s*([,\s]+|[,\s]*(and|und|en|et|ir|[&+]+))\s*#?\d+(?!\d*[-.]\d+))*)"
|
|
self.termSnarfer(irc, msg, match, 'bug')
|
|
|
|
def commitSnarfer(self, irc, msg, match):
|
|
r"(?P<bt>[a-z][^\s:]*(\s+(commit)('?s)?)?)(:+\s*#?|\s+#?|\s*#)(?P<bug>[a-f0-9]{7,}(?![a-f0-9]*[-.][a-f0-9]{7,})(\s*([,\s]+|[,\s]*(and|und|en|et|ir|[&+]+))\s*#?[a-f0-9]{7,}(?![a-f0-9]*[-.][a-f0-9]{7,}))*)"
|
|
self.termSnarfer(irc, msg, match, 'commit')
|
|
|
|
def termSnarfer(self, irc, msg, match, termtype):
|
|
channel = msg.channel
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not self.registryValue('{}Snarfer'.format(termtype), channel):
|
|
return
|
|
nbugs = msg.tagged('nbugs') or 0
|
|
if nbugs >= 5:
|
|
return
|
|
|
|
if termtype != 'commit':
|
|
bugids = re.findall(r'[0-9]+', match.group('bug'))[:5-nbugs]
|
|
else:
|
|
bugids = re.findall(r'[a-f0-9]{7,}', match.group('bug'))[:5-nbugs]
|
|
|
|
# Begin HACK
|
|
# Strings like "Ubuntu 1004" and "Ubuntu 1610" are false triggers for us
|
|
if match.group('bt').lower() == 'ubuntu' and termtype == 'bug':
|
|
bugids = [x for x in bugids if not re.match(r'^([4-9]|[12][0-9])(04|10)$', x)]
|
|
# End HACK
|
|
|
|
# Get tracker name
|
|
bt = [x.lower() for x in match.group('bt').split()]
|
|
if termtype != 'commit':
|
|
sure_bug = re.match(r"^(?P<type>bug|ticket|issue|pull|pr|merge|mr)('?s)?$", bt[-1])
|
|
else:
|
|
sure_bug = re.match(r"^(?P<type>commit)('?s)?$", bt[-1])
|
|
|
|
# Get bug type
|
|
if sure_bug:
|
|
bugtype = sure_bug.group('type')
|
|
else:
|
|
bugtype = ''
|
|
|
|
bugids = list(set(bugids)) # remove dupes
|
|
|
|
if not sure_bug and termtype == 'bug':
|
|
bugids = [x for x in bugids if int(x) > 100]
|
|
|
|
msg.tag('nbugs', nbugs + len(bugids))
|
|
|
|
name = ''
|
|
showTracker = True
|
|
if len(bt) == 1 and not sure_bug:
|
|
try:
|
|
name = bt[0]
|
|
if name in list(self.aliases.keys()):
|
|
name = self.aliases[name]
|
|
tracker = self.db[name]
|
|
except:
|
|
return
|
|
elif len(bt) == 2:
|
|
try:
|
|
name = bt[0]
|
|
if name in list(self.aliases.keys()):
|
|
name = self.aliases[name]
|
|
tracker = self.db[name]
|
|
except:
|
|
name = ''
|
|
|
|
if not name:
|
|
showTracker = False
|
|
snarfTarget = self.registryValue('snarfTarget', channel, irc.network)
|
|
if not snarfTarget:
|
|
supylog.warning("Bugtracker: No snarfTarget set")
|
|
return
|
|
try:
|
|
name = self.shorthand[snarfTarget.lower()]
|
|
tracker = self.db[name]
|
|
except:
|
|
s = self.registryValue('replyNoBugtracker', channel, irc.network)
|
|
irc.error(s % (name or snarfTarget))
|
|
return
|
|
|
|
for bugid in bugids:
|
|
try:
|
|
report = self.get_bug(channel or msg.nick, irc.network, tracker, bugtype, bugid,
|
|
self.registryValue('showassignee', channel, irc.network),
|
|
self.registryValue('extended', channel, irc.network),
|
|
do_tracker=showTracker)
|
|
except trackers.BugNotFoundError:
|
|
if self.registryValue('replyWhenNotFound'):
|
|
irc.error("Could not find %s %s %s" % (tracker.description, termtype, bugid))
|
|
except trackers.BugtrackerError as e:
|
|
if self.registryValue('replyWhenError') and sure_bug:
|
|
irc.error(str(e))
|
|
else:
|
|
if report:
|
|
irc.reply(report)
|
|
|
|
def bugUrlSnarfer(self, irc, msg, match):
|
|
r"(https?://)?((bugs\.debian\.org|pad\.lv)/|\S+/(show_bug\.cgi\?id=|bugreport\.cgi\?bug=|view\.php\?id=|bug=|bugs/|\+bug/|tickets?/|feature-requests/|patches/|todo/|issues/|pulls?/|merge_requests/))(?P<bug>\d+)"
|
|
self.urlSnarfer(irc, msg, match, 'bug')
|
|
|
|
def commitUrlSnarfer(self, irc, msg, match):
|
|
r"(https?://)?\S+/commits?/([^\s?]*\?([^\s?&]+&)?id=)?(?P<bug>[a-f0-9]{7,})"
|
|
self.urlSnarfer(irc, msg, match, 'commit')
|
|
|
|
def urlSnarfer(self, irc, msg, match, urltype):
|
|
channel = msg.channel
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not self.registryValue('{}Snarfer'.format(urltype), channel):
|
|
return
|
|
nbugs = msg.tagged('nbugs') or 0
|
|
if nbugs >= 5:
|
|
return
|
|
msg.tag('nbugs', nbugs+1)
|
|
url = match.group(0)
|
|
bugid = match.group('bug')
|
|
if '://' in url:
|
|
url = url[url.rfind('://')+3:]
|
|
try:
|
|
tracker = self.get_tracker(url, bugid)
|
|
if not tracker:
|
|
return
|
|
report = self.get_bug(channel or msg.nick, irc.network, tracker, 'url', bugid,
|
|
self.registryValue('showassignee', channel, irc.network),
|
|
self.registryValue('extended', channel, irc.network),
|
|
do_url=False)
|
|
except trackers.BugNotFoundError:
|
|
if self.registryValue('replyWhenNotFound'):
|
|
irc.error("Could not find %s %s %s" % (tracker.description, urltype, match.group('bug')))
|
|
except trackers.BugtrackerError as e:
|
|
if self.registryValue('replyWhenError'):
|
|
irc.error(str(e))
|
|
else:
|
|
if report:
|
|
irc.reply(report)
|
|
|
|
# Only useful to Launchpad developers
|
|
def oopsSnarfer(self, irc, msg, match):
|
|
r"(https?://\S+[=/])?OOPS-(?P<oopsid>[a-f0-9]{6,})"
|
|
channel = msg.channel
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not (self.registryValue('bugSnarfer', channel, irc.network) \
|
|
and self.registryValue('oopsSnarfer', channel, irc.network)):
|
|
return
|
|
oopsid = match.group('oopsid')
|
|
if not self.is_ok(channel or msg.nick, irc.network, 'lpoops', oopsid):
|
|
return
|
|
if not match.group(1):
|
|
irc.reply('https://oops.canonical.com/?oopsid=OOPS-%s' % oopsid)
|
|
|
|
def cveSnarfer(self, irc, msg, match):
|
|
r"(https?://\S+=)?CVE[- ](?P<cveid>\d{4}[- ]\d{4,})"
|
|
channel = msg.channel
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not (self.registryValue('bugSnarfer', channel, irc.network) \
|
|
and self.registryValue('cveSnarfer', channel, irc.network)):
|
|
return
|
|
cveid = match.group('cveid').replace(' ','-')
|
|
if not self.is_ok(channel or msg.nick, irc.network, 'cve', cveid):
|
|
return
|
|
if not match.group(1):
|
|
do_url = True
|
|
else:
|
|
do_url = False
|
|
try:
|
|
report = trackers.CVE().get_bug(cveid, do_url)
|
|
except trackers.BugNotFoundError:
|
|
if self.registryValue('replyWhenNotFound'):
|
|
irc.error("Could not find CVE %s" % cveid)
|
|
except trackers.BugtrackerError as e:
|
|
if self.registryValue('replyWhenError'):
|
|
irc.error(str(e))
|
|
else:
|
|
if report:
|
|
irc.reply(report)
|
|
|
|
#TODO: As we will depend on launchpadlib, we should consider using lazr.uri.URI to do URL parsing
|
|
def get_tracker(self, snarfurl, bugid):
|
|
# SourceForge short domain
|
|
snarfurl = snarfurl.replace('sf.net', 'sourceforge.net', 1)
|
|
|
|
# Launchpad URL shortening
|
|
snarfurl = re.sub(r'pad\.lv/(bug=)?(?P<bug>[0-9]+)', r'launchpad.net/bugs/\g<bug>', snarfurl)
|
|
|
|
for t in list(self.db.keys()):
|
|
tracker = self.db[t]
|
|
url = tracker.url[tracker.url.rfind('://')+3:]
|
|
if url in snarfurl and not (url == 'launchpad.net' \
|
|
and snarfurl.startswith('git.launchpad.net')):
|
|
return tracker
|
|
|
|
# No tracker found, bummer. Let's try and get one
|
|
if 'show_bug.cgi' in snarfurl:
|
|
tracker = trackers.Bugzilla().get_tracker(snarfurl)
|
|
elif 'sourceforge.net' in snarfurl:
|
|
tracker = trackers.SourceForge().get_tracker(snarfurl)
|
|
elif 'github.com' in snarfurl:
|
|
tracker = trackers.GitHub().get_tracker(snarfurl)
|
|
elif re.match(r'\S+/commit/[^\s?]*\?([^\s?&]+&)?id=', snarfurl):
|
|
tracker = trackers.CGit().get_tracker(snarfurl, bugid)
|
|
elif re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+)+/-/(issues|merge_requests|commits?)', snarfurl) \
|
|
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+)+/merge_requests', snarfurl) \
|
|
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+){2,}/(issues|commits?)', snarfurl):
|
|
tracker = trackers.GitLab().get_tracker(snarfurl, bugid)
|
|
elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/(issues|commits?)', snarfurl):
|
|
tracker = trackers.GitLab().get_tracker(snarfurl, bugid)
|
|
if not tracker:
|
|
tracker = trackers.Gitea().get_tracker(snarfurl, bugid)
|
|
elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/pulls', snarfurl):
|
|
tracker = trackers.Gitea().get_tracker(snarfurl, bugid)
|
|
elif 'view.php' in snarfurl:
|
|
tracker = trackers.Mantis().get_tracker(snarfurl)
|
|
elif '/ticket/' in snarfurl:
|
|
tracker = trackers.Trac().get_tracker(snarfurl)
|
|
else:
|
|
return
|
|
|
|
if tracker:
|
|
self.db[tracker.name] = tracker
|
|
if self.registryValue('saveDiscoveredTrackers'):
|
|
registerBugtracker(tracker.name, tracker.url, tracker.description, tracker.trackertype, tracker.aliases)
|
|
self.shorthand = utils.abbrev(list(self.db.keys()))
|
|
return tracker
|
|
|
|
def get_bug(self, channel, network, tracker, bugtype, bugid, do_assignee, do_extinfo, do_url=True, do_tracker=True):
|
|
if not self.is_ok(channel, network, tracker, bugid):
|
|
return
|
|
|
|
bugdata = tracker.get_bug(bugtype, bugid)
|
|
if not bugdata:
|
|
return
|
|
|
|
(bugid, product, title, severity, status, assignee, url, extinfo, duplicate) = bugdata
|
|
|
|
if duplicate and not self.is_ok(channel, network, tracker, bugid):
|
|
return
|
|
|
|
bugtype = re.match(r'\S+/(feature-)?(?P<type>request|patch|todo|issue|pull|merge|ticket|commit)(_requests)?(e?s)?/(\?id=)?[a-f0-9]+/?$', url)
|
|
if do_tracker and tracker.trackertype not in ('github', 'gitlab', 'gitea', 'cgit'):
|
|
if re.match(r'\S+/(bugs|feature-requests|patches|todo|issues|pulls?|merge_requests|tickets?|commits?)$', tracker.description):
|
|
report = '%s %s' % (tracker.description, bugid)
|
|
else:
|
|
if bugtype:
|
|
report = '%s %s %s' % (tracker.description, bugtype.group('type'), bugid)
|
|
else:
|
|
report = '%s bug %s' % (tracker.description, bugid)
|
|
else:
|
|
if bugtype:
|
|
report = '%s %s' % (bugtype.group('type').title(), bugid)
|
|
else:
|
|
report = 'Bug %s' % bugid
|
|
|
|
if product:
|
|
report += ' in %s' % product
|
|
|
|
report += ' "%s"' % title.replace('"', "'").strip()
|
|
|
|
if do_extinfo and extinfo:
|
|
report += ' (%s)' % ', '.join(extinfo)
|
|
|
|
if do_assignee and assignee:
|
|
report += ' (assigned: %s)' % assignee
|
|
|
|
severity_status = []
|
|
if severity:
|
|
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in severity.split()))
|
|
if status:
|
|
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in status.split()))
|
|
if severity_status:
|
|
report += ' [%s]' % ', '.join(severity_status)
|
|
|
|
if duplicate:
|
|
report += ' [duplicate: %s]' % duplicate[0]
|
|
|
|
if do_url:
|
|
report += ' %s' % url
|
|
|
|
message_max = 450 - len(channel)
|
|
if len(report) > message_max:
|
|
report_parts = report.split('"')
|
|
report_start = report_parts[0]
|
|
report_end = report_parts[-1]
|
|
report_title = '"'.join(report_parts[1:-1])
|
|
title_max = message_max - len(report_start) - len(report_end) - 5
|
|
report_title_cut = report_title[:title_max].rsplit(None, 1)[0] + '...'
|
|
report = '%s"%s"%s' % (report_start, report_title_cut, report_end)
|
|
|
|
return report
|
|
|
|
Class = Bugtracker
|