* Make tracker settings persist across restarts. * Separate the various parts more properly. * Update default trackers.
573 lines
23 KiB
Python
573 lines
23 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, recipient):
|
|
if not conf.supybot.defaultIgnore():
|
|
return False
|
|
if conf.version <= '0.83.4.1' \
|
|
and ircutils.isChannel(recipient):
|
|
return False
|
|
try:
|
|
user = ircdb.users.getUser(hostmask)
|
|
except KeyError:
|
|
return True
|
|
return False
|
|
|
|
def checkIgnored(hostmask, recipient):
|
|
try:
|
|
user = ircdb.users.getUser(hostmask)
|
|
if user._checkCapability('owner'):
|
|
return False
|
|
elif user.ignore:
|
|
return True
|
|
except KeyError:
|
|
pass
|
|
if ircdb.ignores.checkIgnored(hostmask):
|
|
return True
|
|
if ircutils.isChannel(recipient):
|
|
c = ircdb.channels.getChannel(recipient)
|
|
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 = ('turlSnarfer', 'bugSnarfer', '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, tracker, bug):
|
|
"""Flood/repeat protection"""
|
|
now = time.time()
|
|
for k in list(self.shown.keys()):
|
|
if self.shown[k] < now - self.registryValue('repeatdelay', channel):
|
|
self.shown.pop(k)
|
|
if (channel, tracker, bug) not in self.shown:
|
|
self.shown[(channel, tracker, bug)] = 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, 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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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.args[0] if ircutils.isChannel(msg.args[0]) else None)
|
|
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 defaultIgnored(msg.prefix, msg.args[0]):
|
|
return msg
|
|
if checkIgnored(msg.prefix, msg.args[0]):
|
|
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+#?(?P<bug>\d+(?!\d*[-.]\d+)(\s*([,\s]+|[,\s]*(and|und|en|et|ir|[&+]+))\s*#?\d+(?!\d*[-.]\d+))*)"
|
|
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not self.registryValue('bugSnarfer', channel):
|
|
return
|
|
nbugs = msg.tagged('nbugs') or 0
|
|
if nbugs >= 5:
|
|
return
|
|
|
|
bugids = re.split(r'[^\d]+', 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':
|
|
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()]
|
|
sure_bug = re.match(r"^(?P<type>bug|ticket|issue|pull|pr|merge|mr)('?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:
|
|
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)
|
|
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.error(s % (name or snarfTarget))
|
|
return
|
|
|
|
for bugid in bugids:
|
|
bugid = int(bugid)
|
|
try:
|
|
report = self.get_bug(channel or msg.nick, tracker, bugtype, bugid, self.registryValue('showassignee', channel),
|
|
self.registryValue('extended', channel), do_tracker=showTracker)
|
|
except trackers.BugNotFoundError:
|
|
if self.registryValue('replyWhenNotFound'):
|
|
irc.error("Could not find %s bug %d" % (tracker.description, bugid))
|
|
except trackers.BugtrackerError as e:
|
|
if self.registryValue('replyWhenError') and sure_bug:
|
|
irc.error(str(e))
|
|
else:
|
|
if report:
|
|
irc.reply(report)
|
|
|
|
def turlSnarfer(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/|ticket/|feature-requests/|patches/|todo/|issues/|pulls?/|merge_requests/))(?P<bug>\d+)/?"
|
|
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not self.registryValue('bugSnarfer', channel):
|
|
return
|
|
nbugs = msg.tagged('nbugs') or 0
|
|
if nbugs >= 5:
|
|
return
|
|
msg.tag('nbugs', nbugs+1)
|
|
url = match.group(0)
|
|
bugid = int(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, tracker, 'url', bugid, self.registryValue('showassignee', channel),
|
|
self.registryValue('extended', channel), do_url=False)
|
|
except trackers.BugNotFoundError:
|
|
if self.registryValue('replyWhenNotFound'):
|
|
irc.error("Could not find %s bug %s" % (tracker.description, 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>[\dA-Za-z]{6,})"
|
|
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not (self.registryValue('bugSnarfer', channel) and self.registryValue('oopsSnarfer', channel)):
|
|
return
|
|
oopsid = match.group('oopsid')
|
|
if not self.is_ok(channel or msg.nick, '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.args[0] if ircutils.isChannel(msg.args[0]) else None
|
|
if checkAddressed(msg.args[1].strip(), channel):
|
|
return
|
|
if not (self.registryValue('bugSnarfer', channel) and self.registryValue('cveSnarfer', channel)):
|
|
return
|
|
cveid = match.group('cveid').replace(' ','-')
|
|
if not self.is_ok(channel or msg.nick, '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:
|
|
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/]+/[^\s/]+(/[^\s/]+)+/-/(issues|merge_requests)', snarfurl) \
|
|
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+)+/merge_requests', snarfurl) \
|
|
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+){2,}/issues', snarfurl):
|
|
tracker = trackers.GitLab().get_tracker(snarfurl, bugid)
|
|
elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/issues', 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, tracker, bugtype, bugid, do_assignee, do_extinfo, do_url=True, do_tracker=True):
|
|
if not self.is_ok(channel, 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, tracker, bugid):
|
|
return
|
|
|
|
bugtype = re.match(r'.*/(feature-)?(?P<type>request|patch|todo|issue|pull|merge|ticket)(_requests)?(e?s)?/[0-9]+/?$', url)
|
|
if do_tracker and tracker.trackertype not in ('github', 'gitlab', 'gitea'):
|
|
if re.match(r'.*/(bugs|feature-requests|patches|todo|issues|pulls?|merge_requests|ticket)/?$', tracker.description):
|
|
report = '%s %d' % (tracker.description, bugid)
|
|
else:
|
|
if bugtype:
|
|
report = '%s %s %d' % (tracker.description, bugtype.group('type'), bugid)
|
|
else:
|
|
report = '%s bug %d' % (tracker.description, bugid)
|
|
else:
|
|
if bugtype:
|
|
report = '%s %d' % (bugtype.group('type').title(), bugid)
|
|
else:
|
|
report = 'Bug %d' % 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()))
|
|
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in status.split()))
|
|
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
|