Trees | Indices | Help |
|
---|
|
1 # This file is part of Buildbot. Buildbot is free software: you can 2 # redistribute it and/or modify it under the terms of the GNU General Public 3 # License as published by the Free Software Foundation, version 2. 4 # 5 # This program is distributed in the hope that it will be useful, but WITHOUT 6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8 # details. 9 # 10 # You should have received a copy of the GNU General Public License along with 11 # this program; if not, write to the Free Software Foundation, Inc., 51 12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13 # 14 # Copyright Buildbot Team Members 15 16 17 # code to deliver build status through twisted.words (instant messaging 18 # protocols: irc, etc) 19 20 import re, shlex 21 from string import join, capitalize, lower 22 23 from zope.interface import Interface, implements 24 from twisted.internet import protocol, reactor 25 from twisted.words.protocols import irc 26 from twisted.python import log, failure 27 from twisted.application import internet 28 29 from buildbot import interfaces, util 30 from buildbot import version 31 from buildbot.interfaces import IStatusReceiver 32 from buildbot.sourcestamp import SourceStamp 33 from buildbot.status import base 34 from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, EXCEPTION 35 from buildbot.scripts.runner import ForceOptions 36 37 # twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing 38 try: 39 from twisted.internet import ssl 40 have_ssl = True 41 except ImportError: 42 have_ssl = False 434746 ValueError.__init__(self, string, *more)49 hasStarted = False 50 timer = None 51 557557 del self.timer 58 if not self.hasStarted: 59 self.parent.send("The build has been queued, I'll give a shout" 60 " when it starts")6163 self.hasStarted = True 64 if self.timer: 65 self.timer.cancel() 66 del self.timer 67 eta = s.getETA() 68 response = "build #%d forced" % s.getNumber() 69 if eta is not None: 70 response = "build forced [ETA %s]" % self.parent.convertTime(eta) 71 self.parent.send(response) 72 self.parent.send("I'll give a shout when the build finishes") 73 d = s.waitUntilFinished() 74 d.addCallback(self.parent.watchedBuildFinished)77 implements(IStatusReceiver) 78 """I hold the state for a single user's interaction with the buildbot. 79 80 This base class provides all the basic behavior (the queries and 81 responses). Subclasses for each channel type (IRC, different IM 82 protocols) are expected to provide the lower-level send/receive methods. 83 84 There will be one instance of me for each user who interacts personally 85 with the buildbot. There will be an additional instance for each 86 'broadcast contact' (chat rooms, IRC channels as a whole). 87 """ 88476 477 478 command_FORCE.usage = "force build [--branch=branch] [--revision=revision] <which> <reason> - Force a build" 47990 #StatusReceiver.__init__(self) doesn't exist 91 self.channel = channel 92 self.master = channel.master 93 self.notify_events = {} 94 self.subscribed = 0 95 self.muted = False 96 self.reported_builds = [] # tuples (when, buildername, buildnum) 97 self.add_notification_events(channel.notify_events)98 99 silly = { 100 "What happen ?": "Somebody set up us the bomb.", 101 "It's You !!": ["How are you gentlemen !!", 102 "All your base are belong to us.", 103 "You are on the way to destruction."], 104 "What you say !!": ["You have no chance to survive make your time.", 105 "HA HA HA HA ...."], 106 } 107 111113 try: 114 b = self.channel.status.getBuilder(which) 115 except KeyError: 116 raise UsageError, "no such builder '%s'" % which 117 return b118120 if not self.channel.control: 121 raise UsageError("builder control is not enabled") 122 try: 123 bc = self.channel.control.getBuilder(which) 124 except KeyError: 125 raise UsageError("no such builder '%s'" % which) 126 return bc127129 """ 130 @rtype: list of L{buildbot.process.builder.Builder} 131 """ 132 names = self.channel.status.getBuilderNames(categories=self.channel.categories) 133 names.sort() 134 builders = [self.channel.status.getBuilder(n) for n in names] 135 return builders136138 if seconds < 60: 139 return "%d seconds" % seconds 140 minutes = int(seconds / 60) 141 seconds = seconds - 60*minutes 142 if minutes < 60: 143 return "%dm%02ds" % (minutes, seconds) 144 hours = int(minutes / 60) 145 minutes = minutes - 60*hours 146 return "%dh%02dm%02ds" % (hours, minutes, seconds)147149 """Returns True if this build should be reported for this contact 150 (eliminating duplicates), and also records the report for later""" 151 for w, b, n in self.reported_builds: 152 if b == builder and n == buildnum: 153 return False 154 self.reported_builds.append([util.now(), builder, buildnum]) 155 156 # clean the reported builds 157 horizon = util.now() - 60 158 while self.reported_builds and self.reported_builds[0][0] < horizon: 159 self.reported_builds.pop(0) 160 161 # and return True, since this is a new one 162 return True163165 response = self.silly[message] 166 if type(response) != type([]): 167 response = [response] 168 when = 0.5 169 for r in response: 170 reactor.callLater(when, self.send, r) 171 when += 2.5172174 self.send("yes?")175 178180 args = shlex.split(args) 181 if len(args) == 0: 182 raise UsageError, "try 'list builders'" 183 if args[0] == 'builders': 184 builders = self.getAllBuilders() 185 str = "Configured builders: " 186 for b in builders: 187 str += b.name 188 state = b.getState()[0] 189 if state == 'offline': 190 str += "[offline]" 191 str += " " 192 str.rstrip() 193 self.send(str) 194 return195 command_LIST.usage = "list builders - List configured builders" 196198 args = shlex.split(args) 199 if len(args) == 0: 200 which = "all" 201 elif len(args) == 1: 202 which = args[0] 203 else: 204 raise UsageError, "try 'status <builder>'" 205 if which == "all": 206 builders = self.getAllBuilders() 207 for b in builders: 208 self.emit_status(b.name) 209 return 210 self.emit_status(which)211 command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" 212214 if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): 215 raise UsageError("try 'notify on|off <EVENT>'")216 219 225 229 233235 for event in events: 236 self.validate_notification_event(event) 237 self.notify_events[event] = 1 238 239 if not self.subscribed: 240 self.subscribe_to_build_events()241243 for event in events: 244 self.validate_notification_event(event) 245 del self.notify_events[event] 246 247 if len(self.notify_events) == 0 and self.subscribed: 248 self.unsubscribe_from_build_events()249 255257 args = shlex.split(args) 258 259 if not args: 260 raise UsageError("try 'notify on|off|list <EVENT>'") 261 action = args.pop(0) 262 events = args 263 264 if action == "on": 265 if not events: events = ('started','finished') 266 self.add_notification_events(events) 267 268 self.list_notified_events() 269 270 elif action == "off": 271 if events: 272 self.remove_notification_events(events) 273 else: 274 self.remove_all_notification_events() 275 276 self.list_notified_events() 277 278 elif action == "list": 279 self.list_notified_events() 280 return 281 282 else: 283 raise UsageError("try 'notify on|off <EVENT>'")284 285 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)" 286288 args = shlex.split(args) 289 if len(args) != 1: 290 raise UsageError("try 'watch <builder>'") 291 which = args[0] 292 b = self.getBuilder(which) 293 builds = b.getCurrentBuilds() 294 if not builds: 295 self.send("there are no builds currently running") 296 return 297 for build in builds: 298 assert not build.isFinished() 299 d = build.waitUntilFinished() 300 d.addCallback(self.watchedBuildFinished) 301 r = "watching build %s #%d until it finishes" \ 302 % (which, build.getNumber()) 303 eta = build.getETA() 304 if eta is not None: 305 r += " [%s]" % self.convertTime(eta) 306 r += ".." 307 self.send(r)308 command_WATCH.usage = "watch <which> - announce the completion of an active build" 309 312 316 319321 log.msg('[Contact] BuildRequest %d for %s submitted' % 322 (brstatus.brid, brstatus.getBuilderName()))323325 log.msg('[Contact] Builder %s removed' % (builderName))326328 builder = build.getBuilder() 329 log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) 330 331 # only notify about builders we are interested in 332 333 if (self.channel.categories != None and 334 builder.category not in self.channel.categories): 335 log.msg('Not notifying for a build in the wrong category') 336 return 337 338 if not self.notify_for('started'): 339 log.msg('Not notifying for a build when started-notification disabled') 340 return 341 342 r = "build #%d of %s started, including [%s]" % \ 343 (build.getNumber(), 344 builder.getName(), 345 ", ".join([str(c.revision) for c in build.getChanges()]) 346 ) 347 348 self.send(r)349 350 results_descriptions = { 351 SUCCESS: "Success", 352 WARNINGS: "Warnings", 353 FAILURE: "Failure", 354 EXCEPTION: "Exception", 355 } 356358 builder = build.getBuilder() 359 360 # only notify about builders we are interested in 361 log.msg('[Contact] builder %r in category %s finished' % (builder, builder.category)) 362 363 if (self.channel.categories != None and 364 builder.category not in self.channel.categories): 365 return 366 367 if not self.notify_for_finished(build): 368 return 369 370 builder_name = builder.getName() 371 buildnum = build.getNumber() 372 373 if self.reportBuild(builder_name, buildnum): 374 r = "build #%d of %s is complete: %s" % \ 375 (buildnum, 376 builder_name, 377 self.results_descriptions.get(build.getResults(), "??")) 378 r += " [%s]" % " ".join(build.getText()) 379 buildurl = self.channel.status.getURLForThing(build) 380 if buildurl: 381 r += " Build details are at %s" % buildurl 382 383 if self.channel.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: 384 r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) 385 386 self.send(r)387389 results = build.getResults() 390 391 if self.notify_for('finished'): 392 return True 393 394 if self.notify_for(lower(self.results_descriptions.get(results))): 395 return True 396 397 prevBuild = build.getPreviousBuild() 398 if prevBuild: 399 prevResult = prevBuild.getResults() 400 401 required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)), \ 402 'To', \ 403 capitalize(self.results_descriptions.get(results))), \ 404 '') 405 406 if (self.notify_for(required_notification_control_string)): 407 return True 408 409 return False410412 413 # only notify about builders we are interested in 414 builder = b.getBuilder() 415 log.msg('builder %r in category %s finished' % (builder, 416 builder.category)) 417 if (self.channel.categories != None and 418 builder.category not in self.channel.categories): 419 return 420 421 builder_name = b.getBuilder().getName() 422 buildnum = b.getNumber() 423 424 if self.reportBuild(builder_name, buildnum): 425 r = "Hey! build %s #%d is complete: %s" % \ 426 (builder_name, 427 buildnum, 428 self.results_descriptions.get(b.getResults(), "??")) 429 r += " [%s]" % " ".join(b.getText()) 430 self.send(r) 431 buildurl = self.channel.status.getURLForThing(b) 432 if buildurl: 433 self.send("Build details are at %s" % buildurl)434436 errReply = "try 'force build [--branch=BRANCH] [--revision=REVISION] <WHICH> <REASON>'" 437 args = shlex.split(args) 438 if not args: 439 raise UsageError(errReply) 440 what = args.pop(0) 441 if what != "build": 442 raise UsageError(errReply) 443 opts = ForceOptions() 444 opts.parseOptions(args) 445 446 which = opts['builder'] 447 branch = opts['branch'] 448 revision = opts['revision'] 449 reason = opts['reason'] 450 451 if which is None: 452 raise UsageError("you must provide a Builder, " + errReply) 453 454 # keep weird stuff out of the branch and revision strings. 455 branch_validate = self.master.config.validation['branch'] 456 revision_validate = self.master.config.validation['revision'] 457 if branch and not branch_validate.match(branch): 458 log.msg("bad branch '%s'" % branch) 459 self.send("sorry, bad branch '%s'" % branch) 460 return 461 if revision and not revision_validate.match(revision): 462 log.msg("bad revision '%s'" % revision) 463 self.send("sorry, bad revision '%s'" % revision) 464 return 465 466 bc = self.getControl(which) 467 468 reason = "forced: by %s: %s" % (self.describeUser(who), reason) 469 ss = SourceStamp(branch=branch, revision=revision) 470 d = bc.submitBuildRequest(ss, reason) 471 def subscribe(buildreq): 472 ireq = IrcBuildRequest(self) 473 buildreq.subscribe(ireq.started)474 d.addCallback(subscribe) 475 d.addErrback(log.err, "while forcing a build")481 args = shlex.split(args) 482 if len(args) < 3 or args[0] != 'build': 483 raise UsageError, "try 'stop build WHICH <REASON>'" 484 which = args[1] 485 reason = args[2] 486 487 buildercontrol = self.getControl(which) 488 489 r = "stopped: by %s: %s" % (self.describeUser(who), reason) 490 491 # find an in-progress build 492 builderstatus = self.getBuilder(which) 493 builds = builderstatus.getCurrentBuilds() 494 if not builds: 495 self.send("sorry, no build is currently running") 496 return 497 for build in builds: 498 num = build.getNumber() 499 500 # obtain the BuildControl object 501 buildcontrol = buildercontrol.getBuild(num) 502 503 # make it stop 504 buildcontrol.stopBuild(r) 505 506 self.send("build %d interrupted" % num)507 508 command_STOP.usage = "stop build <which> <reason> - Stop a running build" 509511 b = self.getBuilder(which) 512 str = "%s: " % which 513 state, builds = b.getState() 514 str += state 515 if state == "idle": 516 last = b.getLastFinishedBuild() 517 if last: 518 start,finished = last.getTimes() 519 str += ", last build %s ago: %s" % \ 520 (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) 521 if state == "building": 522 t = [] 523 for build in builds: 524 step = build.getCurrentStep() 525 if step: 526 s = "(%s)" % " ".join(step.getText()) 527 else: 528 s = "(no current step)" 529 ETA = build.getETA() 530 if ETA is not None: 531 s += " [ETA %s]" % self.convertTime(ETA) 532 t.append(s) 533 str += ", ".join(t) 534 self.send(str)535537 last = self.getBuilder(which).getLastFinishedBuild() 538 if not last: 539 str = "(no builds run since last restart)" 540 else: 541 start,finish = last.getTimes() 542 str = "%s ago: " % (self.convertTime(int(util.now() - finish))) 543 str += " ".join(last.getText()) 544 self.send("last build [%s]: %s" % (which, str))545547 args = shlex.split(args) 548 if len(args) == 0: 549 which = "all" 550 elif len(args) == 1: 551 which = args[0] 552 else: 553 raise UsageError, "try 'last <builder>'" 554 if which == "all": 555 builders = self.getAllBuilders() 556 for b in builders: 557 self.emit_last(b.name) 558 return 559 self.emit_last(which)560 command_LAST.usage = "last <which> - list last build status for builder <which>" 561563 # The order of these is important! ;) 564 self.send("Shutting up for now.") 565 self.muted = True566 command_MUTE.usage = "mute - suppress all messages until a corresponding 'unmute' is issued" 567569 if self.muted: 570 # The order of these is important! ;) 571 self.muted = False 572 self.send("I'm baaaaaaaaaaack!") 573 else: 574 self.send("You hadn't told me to be quiet, but it's the thought that counts, right?")575 command_MUTE.usage = "unmute - disable a previous 'mute'" 576578 commands = [] 579 for k in dir(self): 580 if k.startswith('command_'): 581 commands.append(k[8:].lower()) 582 commands.sort() 583 return commands584586 args = shlex.split(args) 587 if len(args) == 0: 588 self.send("Get help on what? (try 'help <foo>', or 'commands' for a command list)") 589 return 590 command = args[0] 591 meth = self.getCommandMethod(command) 592 if not meth: 593 raise UsageError, "no such command '%s'" % command 594 usage = getattr(meth, 'usage', None) 595 if usage: 596 self.send("Usage: %s" % usage) 597 else: 598 self.send("No usage info for '%s'" % command)599 command_HELP.usage = "help <command> - Give help for <command>" 600 604606 commands = self.build_commands() 607 str = "buildbot commands: " + ", ".join(commands) 608 self.send(str)609 command_COMMANDS.usage = "commands - List available commands" 610612 self.act("readies phasers")613615 reactor.callLater(1.0, self.send, "<(^.^<)") 616 reactor.callLater(2.0, self.send, "<(^.^)>") 617 reactor.callLater(3.0, self.send, "(>^.^)>") 618 reactor.callLater(3.5, self.send, "(7^.^)7") 619 reactor.callLater(5.0, self.send, "(>^.^<)")620 624626 # this is sent when somebody performs an action that mentions the 627 # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of 628 # the person who performed the action, so if their action provokes a 629 # response, they can be named. 630 if not data.endswith("s buildbot"): 631 return 632 words = data.split() 633 verb = words[-2] 634 timeout = 4 635 if verb == "kicks": 636 response = "%s back" % verb 637 timeout = 1 638 else: 639 response = "%s %s too" % (verb, user) 640 reactor.callLater(timeout, self.act, response)641643 implements(IStatusReceiver) 644 645 # this is the IRC-specific subclass of Contact 646711648 Contact.__init__(self, channel) 649 # when people send us public messages ("buildbot: command"), 650 # self.dest is the name of the channel ("#twisted"). When they send 651 # us private messages (/msg buildbot command), self.dest is their 652 # username. 653 self.dest = dest654656 if self.dest[0] == '#': 657 return "IRC user <%s> on channel %s" % (user, self.dest) 658 return "IRC user <%s> (privmsg)" % user659 660 # userJoined(self, user, channel) 661 665 669671 # a message has arrived from 'who'. For broadcast contacts (i.e. when 672 # people do an irc 'buildbot: command'), this will be a string 673 # describing the sender of the message in some useful-to-log way, and 674 # a single Contact may see messages from a variety of users. For 675 # unicast contacts (i.e. when people do an irc '/msg buildbot 676 # command'), a single Contact will only ever see messages from a 677 # single user. 678 message = message.lstrip() 679 if self.silly.has_key(message): 680 return self.doSilly(message) 681 682 parts = message.split(' ', 1) 683 if len(parts) == 1: 684 parts = parts + [''] 685 cmd, args = parts 686 log.msg("irc command", cmd) 687 688 meth = self.getCommandMethod(cmd) 689 if not meth and message[-1] == '!': 690 meth = self.command_EXCITED 691 692 error = None 693 try: 694 if meth: 695 meth(args.strip(), who) 696 except UsageError, e: 697 self.send(str(e)) 698 except: 699 f = failure.Failure() 700 log.err(f) 701 error = "Something bad happened (see logs): %s" % f.type 702 703 if error: 704 try: 705 self.send(error) 706 except: 707 log.err() 708 709 #self.say(channel, "count %d" % self.counter) 710 self.channel.counter += 1713 """I represent the buildbot's presence in a particular IM scheme. 714 715 This provides the connection to the IRC server, or represents the 716 buildbot's account with an IM service. Each Channel will have zero or 717 more Contacts associated with it. 718 """719721 """I represent the buildbot to an IRC server. 722 """ 723 implements(IChannel) 724 contactClass = IRCContact 725832 833 # we can using the following irc.IRCClient methods to send output. Most 834 # of these are used by the IRCContact class. 835 # 836 # self.say(channel, message) # broadcast 837 # self.msg(user, message) # unicast 838 # self.me(channel, action) # send action 839 # self.away(message='') 840 # self.quit(message='') 841 849726 - def __init__(self, nickname, password, channels, status, categories, notify_events, noticeOnChannel = False, showBlameList = False):727 """ 728 @type nickname: string 729 @param nickname: the nickname by which this bot should be known 730 @type password: string 731 @param password: the password to use for identifying with Nickserv 732 @type channels: list of dictionaries 733 @param channels: the bot will maintain a presence in these channels 734 @type status: L{buildbot.status.builder.Status} 735 @param status: the build master's Status object, through which the 736 bot retrieves all status information 737 @type noticeOnChannel: boolean 738 @param noticeOnChannel: Defaults to False. If True, error messages 739 for bot commands will be sent to the channel 740 as notices. Otherwise they are sent as a msg. 741 """ 742 self.nickname = nickname 743 self.channels = channels 744 self.password = password 745 self.status = status 746 self.master = status.master 747 self.categories = categories 748 self.notify_events = notify_events 749 self.counter = 0 750 self.hasQuit = 0 751 self.contacts = {} 752 self.noticeOnChannel = noticeOnChannel 753 self.showBlameList = showBlameList754756 if self.noticeOnChannel and dest[0] == '#': 757 self.notice(dest, message) 758 else: 759 self.msg(dest, message)760762 self.contacts[name] = contact763765 if name in self.contacts: 766 return self.contacts[name] 767 new_contact = self.contactClass(self, name) 768 self.contacts[name] = new_contact 769 return new_contact770772 name = contact.getName() 773 if name in self.contacts: 774 assert self.contacts[name] == contact 775 del self.contacts[name]776778 log.msg("%s: %s" % (self, msg))779 780 781 # the following irc.IRCClient methods are called when we have input 782784 user = user.split('!', 1)[0] # rest is ~user@hostname 785 # channel is '#twisted' or 'buildbot' (for private messages) 786 channel = channel.lower() 787 #print "privmsg:", user, channel, message 788 if channel == self.nickname: 789 # private message 790 contact = self.getContact(user) 791 contact.handleMessage(message, user) 792 return 793 # else it's a broadcast message, maybe for us, maybe not. 'channel' 794 # is '#twisted' or the like. 795 contact = self.getContact(channel) 796 if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): 797 message = message[len("%s:" % self.nickname):] 798 contact.handleMessage(message, user)799 # to track users comings and goings, add code here 800802 #log.msg("action: %s,%s,%s" % (user, channel, data)) 803 user = user.split('!', 1)[0] # rest is ~user@hostname 804 # somebody did an action (/me actions) in the broadcast channel 805 contact = self.getContact(channel) 806 if "buildbot" in data: 807 contact.handleAction(data, user)808810 if self.password: 811 self.msg("Nickserv", "IDENTIFY " + self.password) 812 for c in self.channels: 813 if isinstance(c, dict): 814 channel = c.get('channel', None) 815 password = c.get('password', None) 816 else: 817 channel = c 818 password = None 819 self.join(channel=channel, key=password)820822 self.log("I have joined %s" % (channel,)) 823 # trigger contact contructor, which in turn subscribes to notify events 824 self.getContact(channel)825827 self.log("I have left %s" % (channel,))851 protocol = IrcStatusBot 852 853 status = None 854 control = None 855 shuttingDown = False 856 p = None 857905 906858 - def __init__(self, nickname, password, channels, categories, notify_events, noticeOnChannel = False, showBlameList = False):859 #ThrottledClientFactory.__init__(self) # doesn't exist 860 self.status = None 861 self.nickname = nickname 862 self.password = password 863 self.channels = channels 864 self.categories = categories 865 self.notify_events = notify_events 866 self.noticeOnChannel = noticeOnChannel 867 self.showBlameList = showBlameList868 873875 self.shuttingDown = True 876 if self.p: 877 self.p.quit("buildmaster reconfigured: bot disconnecting")878880 p = self.protocol(self.nickname, self.password, 881 self.channels, self.status, 882 self.categories, self.notify_events, 883 noticeOnChannel = self.noticeOnChannel, 884 showBlameList = self.showBlameList) 885 p.factory = self 886 p.status = self.status 887 p.control = self.control 888 self.p = p 889 return p890 891 # TODO: I think a shutdown that occurs while the connection is being 892 # established will make this explode 893895 if self.shuttingDown: 896 log.msg("not scheduling reconnection attempt") 897 return 898 ThrottledClientFactory.clientConnectionLost(self, connector, reason)899901 if self.shuttingDown: 902 log.msg("not scheduling reconnection attempt") 903 return 904 ThrottledClientFactory.clientConnectionFailed(self, connector, reason)908 implements(IStatusReceiver) 909 """I am an IRC bot which can be queried for status information. I 910 connect to a single IRC server and am known by a single nickname on that 911 server, however I can join multiple channels.""" 912 913 in_test_harness = False 914 915 compare_attrs = ["host", "port", "nick", "password", 916 "channels", "allowForce", "useSSL", 917 "categories"] 918967 968 969 ## buildbot: list builders 970 # buildbot: watch quick 971 # print notification when current build in 'quick' finishes 972 ## buildbot: status 973 ## buildbot: status full-2.3 974 ## building, not, % complete, ETA 975 ## buildbot: force build full-2.3 "reason" 976919 - def __init__(self, host, nick, channels, port=6667, allowForce=False, 920 categories=None, password=None, notify_events={}, 921 noticeOnChannel = False, showBlameList = True, 922 useSSL=False):923 base.StatusReceiverMultiService.__init__(self) 924 925 assert allowForce in (True, False) # TODO: implement others 926 927 # need to stash these so we can detect changes later 928 self.host = host 929 self.port = port 930 self.nick = nick 931 self.channels = channels 932 self.password = password 933 self.allowForce = allowForce 934 self.categories = categories 935 self.notify_events = notify_events 936 log.msg('Notify events %s' % notify_events) 937 self.f = IrcStatusFactory(self.nick, self.password, 938 self.channels, self.categories, self.notify_events, 939 noticeOnChannel = noticeOnChannel, 940 showBlameList = showBlameList) 941 942 # don't set up an actual ClientContextFactory if we're running tests. 943 if self.in_test_harness: 944 return 945 946 if useSSL: 947 # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo 948 if not have_ssl: 949 raise RuntimeError("useSSL requires PyOpenSSL") 950 cf = ssl.ClientContextFactory() 951 c = internet.SSLClient(self.host, self.port, self.f, cf) 952 else: 953 c = internet.TCPClient(self.host, self.port, self.f) 954 955 c.setServiceParent(self)956958 base.StatusReceiverMultiService.setServiceParent(self, parent) 959 self.f.status = parent.getStatus() 960 if self.allowForce: 961 self.f.control = interfaces.IControl(parent)962964 # make sure the factory will stop reconnecting 965 self.f.shutdown() 966 return base.StatusReceiverMultiService.stopService(self)
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sun Jul 17 13:45:36 2011 | http://epydoc.sourceforge.net |