| Index: third_party/buildbot_7_12/buildbot/status/words.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/status/words.py b/third_party/buildbot_7_12/buildbot/status/words.py
|
| deleted file mode 100644
|
| index daeb56d826b4fd86ab6d3f1546c1b3d49ae56227..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/status/words.py
|
| +++ /dev/null
|
| @@ -1,902 +0,0 @@
|
| -
|
| -# code to deliver build status through twisted.words (instant messaging
|
| -# protocols: irc, etc)
|
| -
|
| -import re, shlex
|
| -
|
| -from zope.interface import Interface, implements
|
| -from twisted.internet import protocol, reactor
|
| -from twisted.words.protocols import irc
|
| -from twisted.python import log, failure
|
| -from twisted.application import internet
|
| -
|
| -from buildbot import interfaces, util
|
| -from buildbot import version
|
| -from buildbot.sourcestamp import SourceStamp
|
| -from buildbot.process.base import BuildRequest
|
| -from buildbot.status import base
|
| -from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
|
| -from buildbot.scripts.runner import ForceOptions
|
| -
|
| -from string import join, capitalize, lower
|
| -
|
| -class UsageError(ValueError):
|
| - def __init__(self, string = "Invalid usage", *more):
|
| - ValueError.__init__(self, string, *more)
|
| -
|
| -class IrcBuildRequest:
|
| - hasStarted = False
|
| - timer = None
|
| -
|
| - def __init__(self, parent):
|
| - self.parent = parent
|
| - self.timer = reactor.callLater(5, self.soon)
|
| -
|
| - def soon(self):
|
| - del self.timer
|
| - if not self.hasStarted:
|
| - self.parent.send("The build has been queued, I'll give a shout"
|
| - " when it starts")
|
| -
|
| - def started(self, c):
|
| - self.hasStarted = True
|
| - if self.timer:
|
| - self.timer.cancel()
|
| - del self.timer
|
| - s = c.getStatus()
|
| - eta = s.getETA()
|
| - response = "build #%d forced" % s.getNumber()
|
| - if eta is not None:
|
| - response = "build forced [ETA %s]" % self.parent.convertTime(eta)
|
| - self.parent.send(response)
|
| - self.parent.send("I'll give a shout when the build finishes")
|
| - d = s.waitUntilFinished()
|
| - d.addCallback(self.parent.watchedBuildFinished)
|
| -
|
| -class Contact:
|
| - """I hold the state for a single user's interaction with the buildbot.
|
| -
|
| - This base class provides all the basic behavior (the queries and
|
| - responses). Subclasses for each channel type (IRC, different IM
|
| - protocols) are expected to provide the lower-level send/receive methods.
|
| -
|
| - There will be one instance of me for each user who interacts personally
|
| - with the buildbot. There will be an additional instance for each
|
| - 'broadcast contact' (chat rooms, IRC channels as a whole).
|
| - """
|
| -
|
| - def __init__(self, channel):
|
| - self.channel = channel
|
| - self.notify_events = {}
|
| - self.subscribed = 0
|
| - self.add_notification_events(channel.notify_events)
|
| -
|
| - silly = {
|
| - "What happen ?": "Somebody set up us the bomb.",
|
| - "It's You !!": ["How are you gentlemen !!",
|
| - "All your base are belong to us.",
|
| - "You are on the way to destruction."],
|
| - "What you say !!": ["You have no chance to survive make your time.",
|
| - "HA HA HA HA ...."],
|
| - }
|
| -
|
| - def getCommandMethod(self, command):
|
| - meth = getattr(self, 'command_' + command.upper(), None)
|
| - return meth
|
| -
|
| - def getBuilder(self, which):
|
| - try:
|
| - b = self.channel.status.getBuilder(which)
|
| - except KeyError:
|
| - raise UsageError, "no such builder '%s'" % which
|
| - return b
|
| -
|
| - def getControl(self, which):
|
| - if not self.channel.control:
|
| - raise UsageError("builder control is not enabled")
|
| - try:
|
| - bc = self.channel.control.getBuilder(which)
|
| - except KeyError:
|
| - raise UsageError("no such builder '%s'" % which)
|
| - return bc
|
| -
|
| - def getAllBuilders(self):
|
| - """
|
| - @rtype: list of L{buildbot.process.builder.Builder}
|
| - """
|
| - names = self.channel.status.getBuilderNames(categories=self.channel.categories)
|
| - names.sort()
|
| - builders = [self.channel.status.getBuilder(n) for n in names]
|
| - return builders
|
| -
|
| - def convertTime(self, seconds):
|
| - if seconds < 60:
|
| - return "%d seconds" % seconds
|
| - minutes = int(seconds / 60)
|
| - seconds = seconds - 60*minutes
|
| - if minutes < 60:
|
| - return "%dm%02ds" % (minutes, seconds)
|
| - hours = int(minutes / 60)
|
| - minutes = minutes - 60*hours
|
| - return "%dh%02dm%02ds" % (hours, minutes, seconds)
|
| -
|
| - def doSilly(self, message):
|
| - response = self.silly[message]
|
| - if type(response) != type([]):
|
| - response = [response]
|
| - when = 0.5
|
| - for r in response:
|
| - reactor.callLater(when, self.send, r)
|
| - when += 2.5
|
| -
|
| - def command_HELLO(self, args, who):
|
| - self.send("yes?")
|
| -
|
| - def command_VERSION(self, args, who):
|
| - self.send("buildbot-%s at your service" % version)
|
| -
|
| - def command_LIST(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) == 0:
|
| - raise UsageError, "try 'list builders'"
|
| - if args[0] == 'builders':
|
| - builders = self.getAllBuilders()
|
| - str = "Configured builders: "
|
| - for b in builders:
|
| - str += b.name
|
| - state = b.getState()[0]
|
| - if state == 'offline':
|
| - str += "[offline]"
|
| - str += " "
|
| - str.rstrip()
|
| - self.send(str)
|
| - return
|
| - command_LIST.usage = "list builders - List configured builders"
|
| -
|
| - def command_STATUS(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) == 0:
|
| - which = "all"
|
| - elif len(args) == 1:
|
| - which = args[0]
|
| - else:
|
| - raise UsageError, "try 'status <builder>'"
|
| - if which == "all":
|
| - builders = self.getAllBuilders()
|
| - for b in builders:
|
| - self.emit_status(b.name)
|
| - return
|
| - self.emit_status(which)
|
| - command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)"
|
| -
|
| - def validate_notification_event(self, event):
|
| - if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event):
|
| - raise UsageError("try 'notify on|off <EVENT>'")
|
| -
|
| - def list_notified_events(self):
|
| - self.send( "The following events are being notified: %r" % self.notify_events.keys() )
|
| -
|
| - def notify_for(self, *events):
|
| - for event in events:
|
| - if self.notify_events.has_key(event):
|
| - return 1
|
| - return 0
|
| -
|
| - def subscribe_to_build_events(self):
|
| - self.channel.status.subscribe(self)
|
| - self.subscribed = 1
|
| -
|
| - def unsubscribe_from_build_events(self):
|
| - self.channel.status.unsubscribe(self)
|
| - self.subscribed = 0
|
| -
|
| - def add_notification_events(self, events):
|
| - for event in events:
|
| - self.validate_notification_event(event)
|
| - self.notify_events[event] = 1
|
| -
|
| - if not self.subscribed:
|
| - self.subscribe_to_build_events()
|
| -
|
| - def remove_notification_events(self, events):
|
| - for event in events:
|
| - self.validate_notification_event(event)
|
| - del self.notify_events[event]
|
| -
|
| - if len(self.notify_events) == 0 and self.subscribed:
|
| - self.unsubscribe_from_build_events()
|
| -
|
| - def remove_all_notification_events(self):
|
| - self.notify_events = {}
|
| -
|
| - if self.subscribed:
|
| - self.unsubscribe_from_build_events()
|
| -
|
| - def command_NOTIFY(self, args, who):
|
| - args = shlex.split(args)
|
| -
|
| - if not args:
|
| - raise UsageError("try 'notify on|off|list <EVENT>'")
|
| - action = args.pop(0)
|
| - events = args
|
| -
|
| - if action == "on":
|
| - if not events: events = ('started','finished')
|
| - self.add_notification_events(events)
|
| -
|
| - self.list_notified_events()
|
| -
|
| - elif action == "off":
|
| - if events:
|
| - self.remove_notification_events(events)
|
| - else:
|
| - self.remove_all_notification_events()
|
| -
|
| - self.list_notified_events()
|
| -
|
| - elif action == "list":
|
| - self.list_notified_events()
|
| - return
|
| -
|
| - else:
|
| - raise UsageError("try 'notify on|off <EVENT>'")
|
| -
|
| - command_NOTIFY.usage = "notify on|off|list [<EVENT>] ... - Notify me about build events. event should be one or more of: 'started', 'finished', 'failure', 'success', 'exception' or 'xToY' (where x and Y are one of success, warnings, failure, exception, but Y is capitalized)"
|
| -
|
| - def command_WATCH(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) != 1:
|
| - raise UsageError("try 'watch <builder>'")
|
| - which = args[0]
|
| - b = self.getBuilder(which)
|
| - builds = b.getCurrentBuilds()
|
| - if not builds:
|
| - self.send("there are no builds currently running")
|
| - return
|
| - for build in builds:
|
| - assert not build.isFinished()
|
| - d = build.waitUntilFinished()
|
| - d.addCallback(self.watchedBuildFinished)
|
| - r = "watching build %s #%d until it finishes" \
|
| - % (which, build.getNumber())
|
| - eta = build.getETA()
|
| - if eta is not None:
|
| - r += " [%s]" % self.convertTime(eta)
|
| - r += ".."
|
| - self.send(r)
|
| - command_WATCH.usage = "watch <which> - announce the completion of an active build"
|
| -
|
| - def buildsetSubmitted(self, buildset):
|
| - log.msg('[Contact] Buildset %s added' % (buildset))
|
| -
|
| - def builderAdded(self, builderName, builder):
|
| - log.msg('[Contact] Builder %s added' % (builder))
|
| - builder.subscribe(self)
|
| -
|
| - def builderChangedState(self, builderName, state):
|
| - log.msg('[Contact] Builder %s changed state to %s' % (builderName, state))
|
| -
|
| - def requestSubmitted(self, brstatus):
|
| - log.msg('[Contact] BuildRequest for %s submitted to Builder %s' %
|
| - (brstatus.getSourceStamp(), brstatus.builderName))
|
| -
|
| - def requestCancelled(self, brstatus):
|
| - # nothing happens with this notification right now
|
| - pass
|
| -
|
| - def builderRemoved(self, builderName):
|
| - log.msg('[Contact] Builder %s removed' % (builderName))
|
| -
|
| - def buildStarted(self, builderName, build):
|
| - builder = build.getBuilder()
|
| - log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category))
|
| -
|
| - # only notify about builders we are interested in
|
| -
|
| - if (self.channel.categories != None and
|
| - builder.category not in self.channel.categories):
|
| - log.msg('Not notifying for a build in the wrong category')
|
| - return
|
| -
|
| - if not self.notify_for('started'):
|
| - log.msg('Not notifying for a build when started-notification disabled')
|
| - return
|
| -
|
| - r = "build #%d of %s started" % \
|
| - (build.getNumber(),
|
| - builder.getName())
|
| -
|
| - r += " including [" + ", ".join(map(lambda c: repr(c.revision), build.getChanges())) + "]"
|
| -
|
| - self.send(r)
|
| -
|
| - results_descriptions = {
|
| - SUCCESS: "Success",
|
| - WARNINGS: "Warnings",
|
| - FAILURE: "Failure",
|
| - EXCEPTION: "Exception",
|
| - }
|
| -
|
| - def buildFinished(self, builderName, build, results):
|
| - builder = build.getBuilder()
|
| -
|
| - # only notify about builders we are interested in
|
| - log.msg('[Contact] builder %r in category %s finished' % (builder, builder.category))
|
| -
|
| - if (self.channel.categories != None and
|
| - builder.category not in self.channel.categories):
|
| - return
|
| -
|
| - if not self.notify_for_finished(build):
|
| - return
|
| -
|
| - r = "build #%d of %s is complete: %s" % \
|
| - (build.getNumber(),
|
| - builder.getName(),
|
| - self.results_descriptions.get(build.getResults(), "??"))
|
| - r += " [%s]" % " ".join(build.getText())
|
| - buildurl = self.channel.status.getURLForThing(build)
|
| - if buildurl:
|
| - r += " Build details are at %s" % buildurl
|
| -
|
| - if self.channel.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0:
|
| - r += ' blamelist: ' + ', '.join([c.who for c in build.changes])
|
| -
|
| - self.send(r)
|
| -
|
| - def notify_for_finished(self, build):
|
| - results = build.getResults()
|
| -
|
| - if self.notify_for('finished'):
|
| - return True
|
| -
|
| - if self.notify_for(lower(self.results_descriptions.get(results))):
|
| - return True
|
| -
|
| - prevBuild = build.getPreviousBuild()
|
| - if prevBuild:
|
| - prevResult = prevBuild.getResults()
|
| -
|
| - required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)), \
|
| - 'To', \
|
| - capitalize(self.results_descriptions.get(results))), \
|
| - '')
|
| -
|
| - if (self.notify_for(required_notification_control_string)):
|
| - return True
|
| -
|
| - return False
|
| -
|
| - def watchedBuildFinished(self, b):
|
| -
|
| - # only notify about builders we are interested in
|
| - builder = b.getBuilder()
|
| - log.msg('builder %r in category %s finished' % (builder,
|
| - builder.category))
|
| - if (self.channel.categories != None and
|
| - builder.category not in self.channel.categories):
|
| - return
|
| -
|
| - r = "Hey! build %s #%d is complete: %s" % \
|
| - (b.getBuilder().getName(),
|
| - b.getNumber(),
|
| - self.results_descriptions.get(b.getResults(), "??"))
|
| - r += " [%s]" % " ".join(b.getText())
|
| - self.send(r)
|
| - buildurl = self.channel.status.getURLForThing(b)
|
| - if buildurl:
|
| - self.send("Build details are at %s" % buildurl)
|
| -
|
| - def command_FORCE(self, args, who):
|
| - args = shlex.split(args) # TODO: this requires python2.3 or newer
|
| - if not args:
|
| - raise UsageError("try 'force build WHICH <REASON>'")
|
| - what = args.pop(0)
|
| - if what != "build":
|
| - raise UsageError("try 'force build WHICH <REASON>'")
|
| - opts = ForceOptions()
|
| - opts.parseOptions(args)
|
| -
|
| - which = opts['builder']
|
| - branch = opts['branch']
|
| - revision = opts['revision']
|
| - reason = opts['reason']
|
| -
|
| - if which is None:
|
| - raise UsageError("you must provide a Builder, "
|
| - "try 'force build WHICH <REASON>'")
|
| -
|
| - # keep weird stuff out of the branch and revision strings. TODO:
|
| - # centralize this somewhere.
|
| - if branch and not re.match(r'^[\w\.\-\/]*$', branch):
|
| - log.msg("bad branch '%s'" % branch)
|
| - self.send("sorry, bad branch '%s'" % branch)
|
| - return
|
| - if revision and not re.match(r'^[\w\.\-\/]*$', revision):
|
| - log.msg("bad revision '%s'" % revision)
|
| - self.send("sorry, bad revision '%s'" % revision)
|
| - return
|
| -
|
| - bc = self.getControl(which)
|
| -
|
| - r = "forced: by %s: %s" % (self.describeUser(who), reason)
|
| - # TODO: maybe give certain users the ability to request builds of
|
| - # certain branches
|
| - s = SourceStamp(branch=branch, revision=revision)
|
| - req = BuildRequest(r, s, which)
|
| - try:
|
| - bc.requestBuildSoon(req)
|
| - except interfaces.NoSlaveError:
|
| - self.send("sorry, I can't force a build: all slaves are offline")
|
| - return
|
| - ireq = IrcBuildRequest(self)
|
| - req.subscribe(ireq.started)
|
| -
|
| -
|
| - command_FORCE.usage = "force build <which> <reason> - Force a build"
|
| -
|
| - def command_STOP(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) < 3 or args[0] != 'build':
|
| - raise UsageError, "try 'stop build WHICH <REASON>'"
|
| - which = args[1]
|
| - reason = args[2]
|
| -
|
| - buildercontrol = self.getControl(which)
|
| -
|
| - r = "stopped: by %s: %s" % (self.describeUser(who), reason)
|
| -
|
| - # find an in-progress build
|
| - builderstatus = self.getBuilder(which)
|
| - builds = builderstatus.getCurrentBuilds()
|
| - if not builds:
|
| - self.send("sorry, no build is currently running")
|
| - return
|
| - for build in builds:
|
| - num = build.getNumber()
|
| -
|
| - # obtain the BuildControl object
|
| - buildcontrol = buildercontrol.getBuild(num)
|
| -
|
| - # make it stop
|
| - buildcontrol.stopBuild(r)
|
| -
|
| - self.send("build %d interrupted" % num)
|
| -
|
| - command_STOP.usage = "stop build <which> <reason> - Stop a running build"
|
| -
|
| - def emit_status(self, which):
|
| - b = self.getBuilder(which)
|
| - str = "%s: " % which
|
| - state, builds = b.getState()
|
| - str += state
|
| - if state == "idle":
|
| - last = b.getLastFinishedBuild()
|
| - if last:
|
| - start,finished = last.getTimes()
|
| - str += ", last build %s ago: %s" % \
|
| - (self.convertTime(int(util.now() - finished)), " ".join(last.getText()))
|
| - if state == "building":
|
| - t = []
|
| - for build in builds:
|
| - step = build.getCurrentStep()
|
| - if step:
|
| - s = "(%s)" % " ".join(step.getText())
|
| - else:
|
| - s = "(no current step)"
|
| - ETA = build.getETA()
|
| - if ETA is not None:
|
| - s += " [ETA %s]" % self.convertTime(ETA)
|
| - t.append(s)
|
| - str += ", ".join(t)
|
| - self.send(str)
|
| -
|
| - def emit_last(self, which):
|
| - last = self.getBuilder(which).getLastFinishedBuild()
|
| - if not last:
|
| - str = "(no builds run since last restart)"
|
| - else:
|
| - start,finish = last.getTimes()
|
| - str = "%s ago: " % (self.convertTime(int(util.now() - finish)))
|
| - str += " ".join(last.getText())
|
| - self.send("last build [%s]: %s" % (which, str))
|
| -
|
| - def command_LAST(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) == 0:
|
| - which = "all"
|
| - elif len(args) == 1:
|
| - which = args[0]
|
| - else:
|
| - raise UsageError, "try 'last <builder>'"
|
| - if which == "all":
|
| - builders = self.getAllBuilders()
|
| - for b in builders:
|
| - self.emit_last(b.name)
|
| - return
|
| - self.emit_last(which)
|
| - command_LAST.usage = "last <which> - list last build status for builder <which>"
|
| -
|
| - def build_commands(self):
|
| - commands = []
|
| - for k in dir(self):
|
| - if k.startswith('command_'):
|
| - commands.append(k[8:].lower())
|
| - commands.sort()
|
| - return commands
|
| -
|
| - def command_HELP(self, args, who):
|
| - args = shlex.split(args)
|
| - if len(args) == 0:
|
| - self.send("Get help on what? (try 'help <foo>', or 'commands' for a command list)")
|
| - return
|
| - command = args[0]
|
| - meth = self.getCommandMethod(command)
|
| - if not meth:
|
| - raise UsageError, "no such command '%s'" % command
|
| - usage = getattr(meth, 'usage', None)
|
| - if usage:
|
| - self.send("Usage: %s" % usage)
|
| - else:
|
| - self.send("No usage info for '%s'" % command)
|
| - command_HELP.usage = "help <command> - Give help for <command>"
|
| -
|
| - def command_SOURCE(self, args, who):
|
| - banner = "My source can be found at http://buildbot.net/"
|
| - self.send(banner)
|
| -
|
| - def command_COMMANDS(self, args, who):
|
| - commands = self.build_commands()
|
| - str = "buildbot commands: " + ", ".join(commands)
|
| - self.send(str)
|
| - command_COMMANDS.usage = "commands - List available commands"
|
| -
|
| - def command_DESTROY(self, args, who):
|
| - self.act("readies phasers")
|
| -
|
| - def command_DANCE(self, args, who):
|
| - reactor.callLater(1.0, self.send, "0-<")
|
| - reactor.callLater(3.0, self.send, "0-/")
|
| - reactor.callLater(3.5, self.send, "0-\\")
|
| -
|
| - def command_EXCITED(self, args, who):
|
| - # like 'buildbot: destroy the sun!'
|
| - self.send("What you say!")
|
| -
|
| - def handleAction(self, data, user):
|
| - # this is sent when somebody performs an action that mentions the
|
| - # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of
|
| - # the person who performed the action, so if their action provokes a
|
| - # response, they can be named.
|
| - if not data.endswith("s buildbot"):
|
| - return
|
| - words = data.split()
|
| - verb = words[-2]
|
| - timeout = 4
|
| - if verb == "kicks":
|
| - response = "%s back" % verb
|
| - timeout = 1
|
| - else:
|
| - response = "%s %s too" % (verb, user)
|
| - reactor.callLater(timeout, self.act, response)
|
| -
|
| -class IRCContact(Contact):
|
| - # this is the IRC-specific subclass of Contact
|
| -
|
| - def __init__(self, channel, dest):
|
| - Contact.__init__(self, channel)
|
| - # when people send us public messages ("buildbot: command"),
|
| - # self.dest is the name of the channel ("#twisted"). When they send
|
| - # us private messages (/msg buildbot command), self.dest is their
|
| - # username.
|
| - self.dest = dest
|
| -
|
| - def describeUser(self, user):
|
| - if self.dest[0] == '#':
|
| - return "IRC user <%s> on channel %s" % (user, self.dest)
|
| - return "IRC user <%s> (privmsg)" % user
|
| -
|
| - # userJoined(self, user, channel)
|
| -
|
| - def send(self, message):
|
| - self.channel.msgOrNotice(self.dest, message.encode("ascii", "replace"))
|
| -
|
| - def act(self, action):
|
| - self.channel.me(self.dest, action.encode("ascii", "replace"))
|
| -
|
| - def command_JOIN(self, args, who):
|
| - args = shlex.split(args)
|
| - to_join = args[0]
|
| - self.channel.join(to_join)
|
| - self.send("Joined %s" % to_join)
|
| - command_JOIN.usage = "join channel - Join another channel"
|
| -
|
| - def command_LEAVE(self, args, who):
|
| - args = shlex.split(args)
|
| - to_leave = args[0]
|
| - self.send("Buildbot has been told to leave %s" % to_leave)
|
| - self.channel.part(to_leave)
|
| - command_LEAVE.usage = "leave channel - Leave a channel"
|
| -
|
| -
|
| - def handleMessage(self, message, who):
|
| - # a message has arrived from 'who'. For broadcast contacts (i.e. when
|
| - # people do an irc 'buildbot: command'), this will be a string
|
| - # describing the sender of the message in some useful-to-log way, and
|
| - # a single Contact may see messages from a variety of users. For
|
| - # unicast contacts (i.e. when people do an irc '/msg buildbot
|
| - # command'), a single Contact will only ever see messages from a
|
| - # single user.
|
| - message = message.lstrip()
|
| - if self.silly.has_key(message):
|
| - return self.doSilly(message)
|
| -
|
| - parts = message.split(' ', 1)
|
| - if len(parts) == 1:
|
| - parts = parts + ['']
|
| - cmd, args = parts
|
| - log.msg("irc command", cmd)
|
| -
|
| - meth = self.getCommandMethod(cmd)
|
| - if not meth and message[-1] == '!':
|
| - meth = self.command_EXCITED
|
| -
|
| - error = None
|
| - try:
|
| - if meth:
|
| - meth(args.strip(), who)
|
| - except UsageError, e:
|
| - self.send(str(e))
|
| - except:
|
| - f = failure.Failure()
|
| - log.err(f)
|
| - error = "Something bad happened (see logs): %s" % f.type
|
| -
|
| - if error:
|
| - try:
|
| - self.send(error)
|
| - except:
|
| - log.err()
|
| -
|
| - #self.say(channel, "count %d" % self.counter)
|
| - self.channel.counter += 1
|
| -
|
| -class IChannel(Interface):
|
| - """I represent the buildbot's presence in a particular IM scheme.
|
| -
|
| - This provides the connection to the IRC server, or represents the
|
| - buildbot's account with an IM service. Each Channel will have zero or
|
| - more Contacts associated with it.
|
| - """
|
| -
|
| -class IrcStatusBot(irc.IRCClient):
|
| - """I represent the buildbot to an IRC server.
|
| - """
|
| - implements(IChannel)
|
| - contactClass = IRCContact
|
| -
|
| - def __init__(self, nickname, password, channels, status, categories, notify_events, noticeOnChannel = False, showBlameList = False):
|
| - """
|
| - @type nickname: string
|
| - @param nickname: the nickname by which this bot should be known
|
| - @type password: string
|
| - @param password: the password to use for identifying with Nickserv
|
| - @type channels: list of strings
|
| - @param channels: the bot will maintain a presence in these channels
|
| - @type status: L{buildbot.status.builder.Status}
|
| - @param status: the build master's Status object, through which the
|
| - bot retrieves all status information
|
| - """
|
| - self.nickname = nickname
|
| - self.channels = channels
|
| - self.password = password
|
| - self.status = status
|
| - self.categories = categories
|
| - self.notify_events = notify_events
|
| - self.counter = 0
|
| - self.hasQuit = 0
|
| - self.contacts = {}
|
| - self.noticeOnChannel = noticeOnChannel
|
| - self.showBlameList = showBlameList
|
| -
|
| - def msgOrNotice(self, dest, message):
|
| - if self.noticeOnChannel and dest[0] == '#':
|
| - self.notice(dest, message)
|
| - else:
|
| - self.msg(dest, message)
|
| -
|
| - def addContact(self, name, contact):
|
| - self.contacts[name] = contact
|
| -
|
| - def getContact(self, name):
|
| - if name in self.contacts:
|
| - return self.contacts[name]
|
| - new_contact = self.contactClass(self, name)
|
| - self.contacts[name] = new_contact
|
| - return new_contact
|
| -
|
| - def deleteContact(self, contact):
|
| - name = contact.getName()
|
| - if name in self.contacts:
|
| - assert self.contacts[name] == contact
|
| - del self.contacts[name]
|
| -
|
| - def log(self, msg):
|
| - log.msg("%s: %s" % (self, msg))
|
| -
|
| -
|
| - # the following irc.IRCClient methods are called when we have input
|
| -
|
| - def privmsg(self, user, channel, message):
|
| - user = user.split('!', 1)[0] # rest is ~user@hostname
|
| - # channel is '#twisted' or 'buildbot' (for private messages)
|
| - channel = channel.lower()
|
| - #print "privmsg:", user, channel, message
|
| - if channel == self.nickname:
|
| - # private message
|
| - contact = self.getContact(user)
|
| - contact.handleMessage(message, user)
|
| - return
|
| - # else it's a broadcast message, maybe for us, maybe not. 'channel'
|
| - # is '#twisted' or the like.
|
| - contact = self.getContact(channel)
|
| - if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname):
|
| - message = message[len("%s:" % self.nickname):]
|
| - contact.handleMessage(message, user)
|
| - # to track users comings and goings, add code here
|
| -
|
| - def action(self, user, channel, data):
|
| - #log.msg("action: %s,%s,%s" % (user, channel, data))
|
| - user = user.split('!', 1)[0] # rest is ~user@hostname
|
| - # somebody did an action (/me actions) in the broadcast channel
|
| - contact = self.getContact(channel)
|
| - if "buildbot" in data:
|
| - contact.handleAction(data, user)
|
| -
|
| -
|
| -
|
| - def signedOn(self):
|
| - if self.password:
|
| - self.msg("Nickserv", "IDENTIFY " + self.password)
|
| - for c in self.channels:
|
| - self.join(c)
|
| -
|
| - def joined(self, channel):
|
| - self.log("I have joined %s" % (channel,))
|
| - # trigger contact contructor, which in turn subscribes to notify events
|
| - self.getContact(channel)
|
| -
|
| - def left(self, channel):
|
| - self.log("I have left %s" % (channel,))
|
| - def kickedFrom(self, channel, kicker, message):
|
| - self.log("I have been kicked from %s by %s: %s" % (channel,
|
| - kicker,
|
| - message))
|
| -
|
| - # we can using the following irc.IRCClient methods to send output. Most
|
| - # of these are used by the IRCContact class.
|
| - #
|
| - # self.say(channel, message) # broadcast
|
| - # self.msg(user, message) # unicast
|
| - # self.me(channel, action) # send action
|
| - # self.away(message='')
|
| - # self.quit(message='')
|
| -
|
| -class ThrottledClientFactory(protocol.ClientFactory):
|
| - lostDelay = 2
|
| - failedDelay = 60
|
| - def clientConnectionLost(self, connector, reason):
|
| - reactor.callLater(self.lostDelay, connector.connect)
|
| - def clientConnectionFailed(self, connector, reason):
|
| - reactor.callLater(self.failedDelay, connector.connect)
|
| -
|
| -class IrcStatusFactory(ThrottledClientFactory):
|
| - protocol = IrcStatusBot
|
| -
|
| - status = None
|
| - control = None
|
| - shuttingDown = False
|
| - p = None
|
| -
|
| - def __init__(self, nickname, password, channels, categories, notify_events, noticeOnChannel = False, showBlameList = False):
|
| - #ThrottledClientFactory.__init__(self) # doesn't exist
|
| - self.status = None
|
| - self.nickname = nickname
|
| - self.password = password
|
| - self.channels = channels
|
| - self.categories = categories
|
| - self.notify_events = notify_events
|
| - self.noticeOnChannel = noticeOnChannel
|
| - self.showBlameList = showBlameList
|
| -
|
| - def __getstate__(self):
|
| - d = self.__dict__.copy()
|
| - del d['p']
|
| - return d
|
| -
|
| - def shutdown(self):
|
| - self.shuttingDown = True
|
| - if self.p:
|
| - self.p.quit("buildmaster reconfigured: bot disconnecting")
|
| -
|
| - def buildProtocol(self, address):
|
| - p = self.protocol(self.nickname, self.password,
|
| - self.channels, self.status,
|
| - self.categories, self.notify_events,
|
| - noticeOnChannel = self.noticeOnChannel,
|
| - showBlameList = self.showBlameList)
|
| - p.factory = self
|
| - p.status = self.status
|
| - p.control = self.control
|
| - self.p = p
|
| - return p
|
| -
|
| - # TODO: I think a shutdown that occurs while the connection is being
|
| - # established will make this explode
|
| -
|
| - def clientConnectionLost(self, connector, reason):
|
| - if self.shuttingDown:
|
| - log.msg("not scheduling reconnection attempt")
|
| - return
|
| - ThrottledClientFactory.clientConnectionLost(self, connector, reason)
|
| -
|
| - def clientConnectionFailed(self, connector, reason):
|
| - if self.shuttingDown:
|
| - log.msg("not scheduling reconnection attempt")
|
| - return
|
| - ThrottledClientFactory.clientConnectionFailed(self, connector, reason)
|
| -
|
| -
|
| -class IRC(base.StatusReceiverMultiService):
|
| - """I am an IRC bot which can be queried for status information. I
|
| - connect to a single IRC server and am known by a single nickname on that
|
| - server, however I can join multiple channels."""
|
| -
|
| - compare_attrs = ["host", "port", "nick", "password",
|
| - "channels", "allowForce",
|
| - "categories"]
|
| -
|
| - def __init__(self, host, nick, channels, port=6667, allowForce=True,
|
| - categories=None, password=None, notify_events={},
|
| - noticeOnChannel = False, showBlameList = True):
|
| - base.StatusReceiverMultiService.__init__(self)
|
| -
|
| - assert allowForce in (True, False) # TODO: implement others
|
| -
|
| - # need to stash these so we can detect changes later
|
| - self.host = host
|
| - self.port = port
|
| - self.nick = nick
|
| - self.channels = channels
|
| - self.password = password
|
| - self.allowForce = allowForce
|
| - self.categories = categories
|
| - self.notify_events = notify_events
|
| - log.msg('Notify events %s' % notify_events)
|
| - self.f = IrcStatusFactory(self.nick, self.password,
|
| - self.channels, self.categories, self.notify_events,
|
| - noticeOnChannel = noticeOnChannel,
|
| - showBlameList = showBlameList)
|
| - c = internet.TCPClient(self.host, self.port, self.f)
|
| - c.setServiceParent(self)
|
| -
|
| - def setServiceParent(self, parent):
|
| - base.StatusReceiverMultiService.setServiceParent(self, parent)
|
| - self.f.status = parent.getStatus()
|
| - if self.allowForce:
|
| - self.f.control = interfaces.IControl(parent)
|
| -
|
| - def stopService(self):
|
| - # make sure the factory will stop reconnecting
|
| - self.f.shutdown()
|
| - return base.StatusReceiverMultiService.stopService(self)
|
| -
|
| -
|
| -## buildbot: list builders
|
| -# buildbot: watch quick
|
| -# print notification when current build in 'quick' finishes
|
| -## buildbot: status
|
| -## buildbot: status full-2.3
|
| -## building, not, % complete, ETA
|
| -## buildbot: force build full-2.3 "reason"
|
| -
|
|
|