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 import re, shlex, random 17 from string import join, capitalize, lower 18 19 from zope.interface import implements 20 from twisted.internet import protocol, reactor 21 from twisted.words.protocols import irc 22 from twisted.python import log, failure 23 from twisted.application import internet 24 from twisted.internet import task 25 26 from buildbot import interfaces, util 27 from buildbot import version 28 from buildbot.interfaces import IStatusReceiver 29 from buildbot.sourcestamp import SourceStamp 30 from buildbot.status import base 31 from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, EXCEPTION, RETRY 32 from buildbot.scripts.runner import ForceOptions 33 from buildbot.process.properties import Properties 34 35 # twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing 36 try: 37 from twisted.internet import ssl 38 have_ssl = True 39 except ImportError: 40 have_ssl = False 4143 irc_colors = [ 44 'WHITE', 45 'BLACK', 46 'NAVY_BLUE', 47 'GREEN', 48 'RED', 49 'BROWN', 50 'PURPLE', 51 'OLIVE', 52 'YELLOW', 53 'LIME_GREEN', 54 'TEAL', 55 'AQUA_LIGHT', 56 'ROYAL_BLUE', 57 'HOT_PINK', 58 'DARK_GRAY', 59 'LIGHT_GRAY' 60 ] 61 62 if useColors: 63 return "%c%d%s%c" % (3, irc_colors.index(color), text, 3) 64 else: 65 return text667069 ValueError.__init__(self, string, *more)72 hasStarted = False 73 timer = None 7410376 self.parent = parent 77 self.useRevisions = useRevisions 78 self.useColors = useColors 79 self.timer = reactor.callLater(5, self.soon)8082 del self.timer 83 if not self.hasStarted: 84 self.parent.send("The build has been queued, I'll give a shout" 85 " when it starts")8688 self.hasStarted = True 89 if self.timer: 90 self.timer.cancel() 91 del self.timer 92 eta = s.getETA() 93 if self.useRevisions: 94 response = "build containing revision(s) [%s] forced" % s.getRevisions() 95 else: 96 response = "build #%d forced" % s.getNumber() 97 if eta is not None: 98 response = "build forced [ETA %s]" % self.parent.convertTime(eta) 99 self.parent.send(response) 100 self.parent.send("I'll give a shout when the build finishes") 101 d = s.waitUntilFinished() 102 d.addCallback(self.parent.watchedBuildFinished)105 implements(IStatusReceiver) 106 """I hold the state for a single user's interaction with the buildbot. 107 108 There will be one instance of me for each user who interacts personally 109 with the buildbot. There will be an additional instance for each 110 'broadcast contact' (chat rooms, IRC channels as a whole). 111 """ 112537 538 539 command_FORCE.usage = "force build [--branch=branch] [--revision=revision] [--props=prop1=val1,prop2=val2...] <which> <reason> - Force a build" 540114 self.bot = bot 115 self.master = bot.master 116 self.notify_events = {} 117 self.subscribed = 0 118 self.muted = False 119 self.useRevisions = bot.useRevisions 120 self.useColors = bot.useColors 121 self.reported_builds = [] # tuples (when, buildername, buildnum) 122 self.add_notification_events(bot.notify_events) 123 124 # when people send us public messages ("buildbot: command"), 125 # self.dest is the name of the channel ("#twisted"). When they send 126 # us private messages (/msg buildbot command), self.dest is their 127 # username. 128 self.dest = dest129 130 # silliness 131 132 silly = { 133 "What happen ?": [ "Somebody set up us the bomb." ], 134 "It's You !!": ["How are you gentlemen !!", 135 "All your base are belong to us.", 136 "You are on the way to destruction."], 137 "What you say !!": ["You have no chance to survive make your time.", 138 "HA HA HA HA ...."], 139 } 140142 response = self.silly[message] 143 when = 0.5 144 for r in response: 145 reactor.callLater(when, self.send, r) 146 when += 2.5147149 try: 150 b = self.bot.status.getBuilder(which) 151 except KeyError: 152 raise UsageError, "no such builder '%s'" % which 153 return b154156 if not self.bot.control: 157 raise UsageError("builder control is not enabled") 158 try: 159 bc = self.bot.control.getBuilder(which) 160 except KeyError: 161 raise UsageError("no such builder '%s'" % which) 162 return bc163165 """ 166 @rtype: list of L{buildbot.process.builder.Builder} 167 """ 168 names = self.bot.status.getBuilderNames(categories=self.bot.categories) 169 names.sort() 170 builders = [self.bot.status.getBuilder(n) for n in names] 171 return builders172174 if seconds < 60: 175 return "%d seconds" % seconds 176 minutes = int(seconds / 60) 177 seconds = seconds - 60*minutes 178 if minutes < 60: 179 return "%dm%02ds" % (minutes, seconds) 180 hours = int(minutes / 60) 181 minutes = minutes - 60*hours 182 return "%dh%02dm%02ds" % (hours, minutes, seconds)183185 """Returns True if this build should be reported for this contact 186 (eliminating duplicates), and also records the report for later""" 187 for w, b, n in self.reported_builds: 188 if b == builder and n == buildnum: 189 return False 190 self.reported_builds.append([util.now(), builder, buildnum]) 191 192 # clean the reported builds 193 horizon = util.now() - 60 194 while self.reported_builds and self.reported_builds[0][0] < horizon: 195 self.reported_builds.pop(0) 196 197 # and return True, since this is a new one 198 return True199201 self.send("yes?")202 205207 args = shlex.split(args) 208 if len(args) == 0: 209 raise UsageError, "try 'list builders'" 210 if args[0] == 'builders': 211 builders = self.getAllBuilders() 212 str = "Configured builders: " 213 for b in builders: 214 str += b.name 215 state = b.getState()[0] 216 if state == 'offline': 217 str += "[offline]" 218 str += " " 219 str.rstrip() 220 self.send(str) 221 return222 command_LIST.usage = "list builders - List configured builders" 223225 args = shlex.split(args) 226 if len(args) == 0: 227 which = "all" 228 elif len(args) == 1: 229 which = args[0] 230 else: 231 raise UsageError, "try 'status <builder>'" 232 if which == "all": 233 builders = self.getAllBuilders() 234 for b in builders: 235 self.emit_status(b.name) 236 return 237 self.emit_status(which)238 command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" 239241 if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): 242 raise UsageError("try 'notify on|off <EVENT>'")243 246 252 256 260262 for event in events: 263 self.validate_notification_event(event) 264 self.notify_events[event] = 1 265 266 if not self.subscribed: 267 self.subscribe_to_build_events()268270 for event in events: 271 self.validate_notification_event(event) 272 del self.notify_events[event] 273 274 if len(self.notify_events) == 0 and self.subscribed: 275 self.unsubscribe_from_build_events()276 282284 args = shlex.split(args) 285 286 if not args: 287 raise UsageError("try 'notify on|off|list <EVENT>'") 288 action = args.pop(0) 289 events = args 290 291 if action == "on": 292 if not events: events = ('started','finished') 293 self.add_notification_events(events) 294 295 self.list_notified_events() 296 297 elif action == "off": 298 if events: 299 self.remove_notification_events(events) 300 else: 301 self.remove_all_notification_events() 302 303 self.list_notified_events() 304 305 elif action == "list": 306 self.list_notified_events() 307 return 308 309 else: 310 raise UsageError("try 'notify on|off <EVENT>'")311 312 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)" 313315 args = shlex.split(args) 316 if len(args) != 1: 317 raise UsageError("try 'watch <builder>'") 318 which = args[0] 319 b = self.getBuilder(which) 320 builds = b.getCurrentBuilds() 321 if not builds: 322 self.send("there are no builds currently running") 323 return 324 for build in builds: 325 assert not build.isFinished() 326 d = build.waitUntilFinished() 327 d.addCallback(self.watchedBuildFinished) 328 if self.useRevisions: 329 r = "watching build %s containing revision(s) [%s] until it finishes" \ 330 % (which, build.getRevisions()) 331 else: 332 r = "watching build %s #%d until it finishes" \ 333 % (which, build.getNumber()) 334 eta = build.getETA() 335 if eta is not None: 336 r += " [%s]" % self.convertTime(eta) 337 r += ".." 338 self.send(r)339 command_WATCH.usage = "watch <which> - announce the completion of an active build" 340342 if (self.bot.categories != None and 343 builder.category not in self.bot.categories): 344 return 345 346 log.msg('[Contact] Builder %s added' % (builder)) 347 builder.subscribe(self)348350 log.msg('[Contact] Builder %s removed' % (builderName))351353 builder = build.getBuilder() 354 log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) 355 356 # only notify about builders we are interested in 357 358 if (self.bot.categories != None and 359 builder.category not in self.bot.categories): 360 log.msg('Not notifying for a build in the wrong category') 361 return 362 363 if not self.notify_for('started'): 364 return 365 366 if self.useRevisions: 367 r = "build containing revision(s) [%s] on %s started" % \ 368 (build.getRevisions(), builder.getName()) 369 else: 370 r = "build #%d of %s started, including [%s]" % \ 371 (build.getNumber(), 372 builder.getName(), 373 ", ".join([str(c.revision) for c in build.getChanges()]) 374 ) 375 376 self.send(r)377 378 results_descriptions = { 379 SUCCESS: ("Success", 'GREEN'), 380 WARNINGS: ("Warnings", 'YELLOW'), 381 FAILURE: ("Failure", 'RED'), 382 EXCEPTION: ("Exception", 'PURPLE'), 383 RETRY: ("Retry", 'AQUA_LIGHT'), 384 } 385 388390 builder = build.getBuilder() 391 392 if (self.bot.categories != None and 393 builder.category not in self.bot.categories): 394 return 395 396 if not self.notify_for_finished(build): 397 return 398 399 builder_name = builder.getName() 400 buildnum = build.getNumber() 401 buildrevs = build.getRevisions() 402 403 results = self.getResultsDescriptionAndColor(build.getResults()) 404 if self.reportBuild(builder_name, buildnum): 405 if self.useRevisions: 406 r = "build containing revision(s) [%s] on %s is complete: %s" % \ 407 (buildrevs, builder_name, results[0]) 408 else: 409 r = "build #%d of %s is complete: %s" % \ 410 (buildnum, builder_name, results[0]) 411 412 r += ' [%s]' % maybeColorize(" ".join(build.getText()), results[1], self.useColors) 413 buildurl = self.bot.status.getURLForThing(build) 414 if buildurl: 415 r += " Build details are at %s" % buildurl 416 417 if self.bot.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: 418 r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) 419 420 self.send(r)421423 results = build.getResults() 424 425 if self.notify_for('finished'): 426 return True 427 428 if self.notify_for(lower(self.results_descriptions.get(results)[0])): 429 return True 430 431 prevBuild = build.getPreviousBuild() 432 if prevBuild: 433 prevResult = prevBuild.getResults() 434 435 required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)[0]), \ 436 'To', \ 437 capitalize(self.results_descriptions.get(results)[0])), \ 438 '') 439 440 if (self.notify_for(required_notification_control_string)): 441 return True 442 443 return False444446 447 # only notify about builders we are interested in 448 builder = b.getBuilder() 449 if (self.bot.categories != None and 450 builder.category not in self.bot.categories): 451 return 452 453 builder_name = builder.getName() 454 buildnum = b.getNumber() 455 buildrevs = b.getRevisions() 456 457 results = self.getResultsDescriptionAndColor(b.getResults()) 458 if self.reportBuild(builder_name, buildnum): 459 if self.useRevisions: 460 r = "Hey! build %s containing revision(s) [%s] is complete: %s" % \ 461 (builder_name, buildrevs, results[0]) 462 else: 463 r = "Hey! build %s #%d is complete: %s" % \ 464 (builder_name, buildnum, results[0]) 465 466 r += ' [%s]' % maybeColorize(" ".join(b.getText()), results[1], self.useColors) 467 self.send(r) 468 buildurl = self.bot.status.getURLForThing(b) 469 if buildurl: 470 self.send("Build details are at %s" % buildurl)471473 errReply = "try 'force build [--branch=BRANCH] [--revision=REVISION] [--props=PROP1=VAL1,PROP2=VAL2...] <WHICH> <REASON>'" 474 args = shlex.split(args) 475 if not args: 476 raise UsageError(errReply) 477 what = args.pop(0) 478 if what != "build": 479 raise UsageError(errReply) 480 opts = ForceOptions() 481 opts.parseOptions(args) 482 483 which = opts['builder'] 484 branch = opts['branch'] 485 revision = opts['revision'] 486 reason = opts['reason'] 487 props = opts['props'] 488 489 if which is None: 490 raise UsageError("you must provide a Builder, " + errReply) 491 492 # keep weird stuff out of the branch, revision, and properties args. 493 branch_validate = self.master.config.validation['branch'] 494 revision_validate = self.master.config.validation['revision'] 495 pname_validate = self.master.config.validation['property_name'] 496 pval_validate = self.master.config.validation['property_value'] 497 if branch and not branch_validate.match(branch): 498 log.msg("bad branch '%s'" % branch) 499 self.send("sorry, bad branch '%s'" % branch) 500 return 501 if revision and not revision_validate.match(revision): 502 log.msg("bad revision '%s'" % revision) 503 self.send("sorry, bad revision '%s'" % revision) 504 return 505 506 properties = Properties() 507 if props: 508 # split props into name:value dict 509 pdict = {} 510 propertylist = props.split(",") 511 for i in range(0,len(propertylist)): 512 splitproperty = propertylist[i].split("=", 1) 513 pdict[splitproperty[0]] = splitproperty[1] 514 515 # set properties 516 for prop in pdict: 517 pname = prop 518 pvalue = pdict[prop] 519 if not pname_validate.match(pname) \ 520 or not pval_validate.match(pvalue): 521 log.msg("bad property name='%s', value='%s'" % (pname, pvalue)) 522 self.send("sorry, bad property name='%s', value='%s'" % 523 (pname, pvalue)) 524 return 525 properties.setProperty(pname, pvalue, "Force Build IRC") 526 527 bc = self.getControl(which) 528 529 reason = "forced: by %s: %s" % (self.describeUser(who), reason) 530 ss = SourceStamp(branch=branch, revision=revision) 531 d = bc.submitBuildRequest(ss, reason, props=properties.asDict()) 532 def subscribe(buildreq): 533 ireq = IrcBuildRequest(self, self.useRevisions) 534 buildreq.subscribe(ireq.started)535 d.addCallback(subscribe) 536 d.addErrback(log.err, "while forcing a build")542 args = shlex.split(args) 543 if len(args) < 3 or args[0] != 'build': 544 raise UsageError, "try 'stop build WHICH <REASON>'" 545 which = args[1] 546 reason = args[2] 547 548 buildercontrol = self.getControl(which) 549 550 r = "stopped: by %s: %s" % (self.describeUser(who), reason) 551 552 # find an in-progress build 553 builderstatus = self.getBuilder(which) 554 builds = builderstatus.getCurrentBuilds() 555 if not builds: 556 self.send("sorry, no build is currently running") 557 return 558 for build in builds: 559 num = build.getNumber() 560 revs = build.getRevisions() 561 562 # obtain the BuildControl object 563 buildcontrol = buildercontrol.getBuild(num) 564 565 # make it stop 566 buildcontrol.stopBuild(r) 567 568 if self.useRevisions: 569 response = "build containing revision(s) [%s] interrupted" % revs 570 else: 571 response = "build %d interrupted" % num 572 self.send(response)573 574 command_STOP.usage = "stop build <which> <reason> - Stop a running build" 575577 b = self.getBuilder(which) 578 str = "%s: " % which 579 state, builds = b.getState() 580 str += state 581 if state == "idle": 582 last = b.getLastFinishedBuild() 583 if last: 584 start,finished = last.getTimes() 585 str += ", last build %s ago: %s" % \ 586 (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) 587 if state == "building": 588 t = [] 589 for build in builds: 590 step = build.getCurrentStep() 591 if step: 592 s = "(%s)" % " ".join(step.getText()) 593 else: 594 s = "(no current step)" 595 ETA = build.getETA() 596 if ETA is not None: 597 s += " [ETA %s]" % self.convertTime(ETA) 598 t.append(s) 599 str += ", ".join(t) 600 self.send(str)601603 args = shlex.split(args) 604 605 if len(args) == 0: 606 which = "all" 607 elif len(args) == 1: 608 which = args[0] 609 else: 610 raise UsageError, "try 'last <builder>'" 611 612 def emit_last(which): 613 last = self.getBuilder(which).getLastFinishedBuild() 614 if not last: 615 str = "(no builds run since last restart)" 616 else: 617 start,finish = last.getTimes() 618 str = "%s ago: " % (self.convertTime(int(util.now() - finish))) 619 str += " ".join(last.getText()) 620 self.send("last build [%s]: %s" % (which, str))621 622 if which == "all": 623 builders = self.getAllBuilders() 624 for b in builders: 625 emit_last(b.name) 626 return 627 emit_last(which) 628 command_LAST.usage = "last <which> - list last build status for builder <which>" 629631 commands = [] 632 for k in dir(self): 633 if k.startswith('command_'): 634 commands.append(k[8:].lower()) 635 commands.sort() 636 return commands637639 if self.dest[0] == '#': 640 return "IRC user <%s> on channel %s" % (user, self.dest) 641 return "IRC user <%s> (privmsg)" % user642 643 # commands 644646 # The order of these is important! ;) 647 self.send("Shutting up for now.") 648 self.muted = True649 command_MUTE.usage = "mute - suppress all messages until a corresponding 'unmute' is issued" 650652 if self.muted: 653 # The order of these is important! ;) 654 self.muted = False 655 self.send("I'm baaaaaaaaaaack!") 656 else: 657 self.send("You hadn't told me to be quiet, but it's the thought that counts, right?")658 command_UNMUTE.usage = "unmute - disable a previous 'mute'" 659661 args = shlex.split(args) 662 if len(args) == 0: 663 self.send("Get help on what? (try 'help <foo>', " 664 "or 'commands' for a command list)") 665 return 666 command = args[0] 667 meth = self.getCommandMethod(command) 668 if not meth: 669 raise UsageError, "no such command '%s'" % command 670 usage = getattr(meth, 'usage', None) 671 if usage: 672 self.send("Usage: %s" % usage) 673 else: 674 self.send("No usage info for '%s'" % command)675 command_HELP.usage = "help <command> - Give help for <command>" 676 680 command_SOURCE.usage = "source - the source code for Buildbot" 681683 commands = self.build_commands() 684 str = "buildbot commands: " + ", ".join(commands) 685 self.send(str)686 command_COMMANDS.usage = "commands - List available commands" 687689 self.act("readies phasers")690692 reactor.callLater(1.0, self.send, "<(^.^<)") 693 reactor.callLater(2.0, self.send, "<(^.^)>") 694 reactor.callLater(3.0, self.send, "(>^.^)>") 695 reactor.callLater(3.5, self.send, "(7^.^)7") 696 reactor.callLater(5.0, self.send, "(>^.^<)")697 698 # communication with the user 699 703 707 708 # main dispatchers for incoming messages 709711 return getattr(self, 'command_' + command.upper(), None)712714 # a message has arrived from 'who'. For broadcast contacts (i.e. when 715 # people do an irc 'buildbot: command'), this will be a string 716 # describing the sender of the message in some useful-to-log way, and 717 # a single Contact may see messages from a variety of users. For 718 # unicast contacts (i.e. when people do an irc '/msg buildbot 719 # command'), a single Contact will only ever see messages from a 720 # single user. 721 message = message.lstrip() 722 if self.silly.has_key(message): 723 return self.doSilly(message) 724 725 parts = message.split(' ', 1) 726 if len(parts) == 1: 727 parts = parts + [''] 728 cmd, args = parts 729 log.msg("irc command", cmd) 730 731 meth = self.getCommandMethod(cmd) 732 if not meth and message[-1] == '!': 733 self.send("What you say!") 734 return 735 736 error = None 737 try: 738 if meth: 739 meth(args.strip(), who) 740 except UsageError, e: 741 self.send(str(e)) 742 except: 743 f = failure.Failure() 744 log.err(f) 745 error = "Something bad happened (see logs)" 746 747 if error: 748 try: 749 self.send(error) 750 except: 751 log.err()752754 # this is sent when somebody performs an action that mentions the 755 # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of 756 # the person who performed the action, so if their action provokes a 757 # response, they can be named. This is 100% silly. 758 if not data.endswith("s buildbot"): 759 return 760 words = data.split() 761 verb = words[-2] 762 if verb == "kicks": 763 response = "%s back" % verb 764 else: 765 response = "%s %s too" % (verb, user) 766 self.act(response)767 768770 """I represent the buildbot to an IRC server. 771 """ 772 contactClass = IRCContact 773870 871774 - def __init__(self, nickname, password, channels, pm_to_nicks, status, 775 categories, notify_events, noticeOnChannel=False, 776 useRevisions=False, showBlameList=False, useColors=True):777 self.nickname = nickname 778 self.channels = channels 779 self.pm_to_nicks = pm_to_nicks 780 self.password = password 781 self.status = status 782 self.master = status.master 783 self.categories = categories 784 self.notify_events = notify_events 785 self.hasQuit = 0 786 self.contacts = {} 787 self.noticeOnChannel = noticeOnChannel 788 self.useColors = useColors 789 self.useRevisions = useRevisions 790 self.showBlameList = showBlameList 791 self._keepAliveCall = task.LoopingCall(lambda: self.ping(self.nickname))792 796798 if self._keepAliveCall.running: 799 self._keepAliveCall.stop() 800 irc.IRCClient.connectionLost(self, reason)801803 if self.noticeOnChannel and dest[0] == '#': 804 self.notice(dest, message) 805 else: 806 self.msg(dest, message)807809 if name in self.contacts: 810 return self.contacts[name] 811 new_contact = self.contactClass(self, name) 812 self.contacts[name] = new_contact 813 return new_contact814816 log.msg("%s: %s" % (self, msg))817 818 819 # the following irc.IRCClient methods are called when we have input 820822 user = user.split('!', 1)[0] # rest is ~user@hostname 823 # channel is '#twisted' or 'buildbot' (for private messages) 824 channel = channel.lower() 825 if channel == self.nickname: 826 # private message 827 contact = self.getContact(user) 828 contact.handleMessage(message, user) 829 return 830 # else it's a broadcast message, maybe for us, maybe not. 'channel' 831 # is '#twisted' or the like. 832 contact = self.getContact(channel) 833 if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): 834 message = message[len("%s:" % self.nickname):] 835 contact.handleMessage(message, user)836838 user = user.split('!', 1)[0] # rest is ~user@hostname 839 # somebody did an action (/me actions) in the broadcast channel 840 contact = self.getContact(channel) 841 if "buildbot" in data: 842 contact.handleAction(data, user)843845 if self.password: 846 self.msg("Nickserv", "IDENTIFY " + self.password) 847 for c in self.channels: 848 if isinstance(c, dict): 849 channel = c.get('channel', None) 850 password = c.get('password', None) 851 else: 852 channel = c 853 password = None 854 self.join(channel=channel, key=password) 855 for c in self.pm_to_nicks: 856 self.getContact(c)857859 self.log("I have joined %s" % (channel,)) 860 # trigger contact contructor, which in turn subscribes to notify events 861 self.getContact(channel)862864 self.log("I have left %s" % (channel,))865873 lostDelay = random.randint(1, 5) 874 failedDelay = random.randint(45, 60) 875887 888877 if lostDelay is not None: 878 self.lostDelay = lostDelay 879 if failedDelay is not None: 880 self.failedDelay = failedDelay881 884890 protocol = IrcStatusBot 891 892 status = None 893 control = None 894 shuttingDown = False 895 p = None 896952 953897 - def __init__(self, nickname, password, channels, pm_to_nicks, categories, notify_events, 898 noticeOnChannel=False, useRevisions=False, showBlameList=False, 899 lostDelay=None, failedDelay=None, useColors=True):900 ThrottledClientFactory.__init__(self, lostDelay=lostDelay, 901 failedDelay=failedDelay) 902 self.status = None 903 self.nickname = nickname 904 self.password = password 905 self.channels = channels 906 self.pm_to_nicks = pm_to_nicks 907 self.categories = categories 908 self.notify_events = notify_events 909 self.noticeOnChannel = noticeOnChannel 910 self.useRevisions = useRevisions 911 self.showBlameList = showBlameList 912 self.useColors = useColors913 918920 self.shuttingDown = True 921 if self.p: 922 self.p.quit("buildmaster reconfigured: bot disconnecting")923925 p = self.protocol(self.nickname, self.password, 926 self.channels, self.pm_to_nicks, self.status, 927 self.categories, self.notify_events, 928 noticeOnChannel = self.noticeOnChannel, 929 useColors = self.useColors, 930 useRevisions = self.useRevisions, 931 showBlameList = self.showBlameList) 932 p.factory = self 933 p.status = self.status 934 p.control = self.control 935 self.p = p 936 return p937 938 # TODO: I think a shutdown that occurs while the connection is being 939 # established will make this explode 940942 if self.shuttingDown: 943 log.msg("not scheduling reconnection attempt") 944 return 945 ThrottledClientFactory.clientConnectionLost(self, connector, reason)946948 if self.shuttingDown: 949 log.msg("not scheduling reconnection attempt") 950 return 951 ThrottledClientFactory.clientConnectionFailed(self, connector, reason)955 implements(IStatusReceiver) 956 957 in_test_harness = False 958 959 compare_attrs = ["host", "port", "nick", "password", 960 "channels", "pm_to_nicks", "allowForce", "useSSL", 961 "useRevisions", "categories", "useColors", 962 "lostDelay", "failedDelay"] 9631015964 - def __init__(self, host, nick, channels, pm_to_nicks=[], port=6667, 965 allowForce=False, categories=None, password=None, notify_events={}, 966 noticeOnChannel = False, showBlameList = True, useRevisions=False, 967 useSSL=False, lostDelay=None, failedDelay=None, useColors=True):968 base.StatusReceiverMultiService.__init__(self) 969 970 assert allowForce in (True, False) # TODO: implement others 971 972 # need to stash these so we can detect changes later 973 self.host = host 974 self.port = port 975 self.nick = nick 976 self.channels = channels 977 self.pm_to_nicks = pm_to_nicks 978 self.password = password 979 self.allowForce = allowForce 980 self.useRevisions = useRevisions 981 self.categories = categories 982 self.notify_events = notify_events 983 984 self.f = IrcStatusFactory(self.nick, self.password, 985 self.channels, self.pm_to_nicks, 986 self.categories, self.notify_events, 987 noticeOnChannel = noticeOnChannel, 988 useRevisions = useRevisions, 989 showBlameList = showBlameList, 990 lostDelay = lostDelay, 991 failedDelay = failedDelay, 992 useColors = useColors) 993 994 if useSSL: 995 # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo 996 if not have_ssl: 997 raise RuntimeError("useSSL requires PyOpenSSL") 998 cf = ssl.ClientContextFactory() 999 c = internet.SSLClient(self.host, self.port, self.f, cf) 1000 else: 1001 c = internet.TCPClient(self.host, self.port, self.f) 1002 1003 c.setServiceParent(self)10041006 base.StatusReceiverMultiService.setServiceParent(self, parent) 1007 self.f.status = parent 1008 if self.allowForce: 1009 self.f.control = interfaces.IControl(self.master)10101012 # make sure the factory will stop reconnecting 1013 self.f.shutdown() 1014 return base.StatusReceiverMultiService.stopService(self)
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sun Mar 25 19:40:41 2012 | http://epydoc.sourceforge.net |