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 usage, log 23 from twisted.application import internet 24 from twisted.internet import defer, 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.process.properties import Properties 33 34 # twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing 35 try: 36 from twisted.internet import ssl 37 have_ssl = True 38 except ImportError: 39 have_ssl = False42 irc_colors = [ 43 'WHITE', 44 'BLACK', 45 'NAVY_BLUE', 46 'GREEN', 47 'RED', 48 'BROWN', 49 'PURPLE', 50 'OLIVE', 51 'YELLOW', 52 'LIME_GREEN', 53 'TEAL', 54 'AQUA_LIGHT', 55 'ROYAL_BLUE', 56 'HOT_PINK', 57 'DARK_GRAY', 58 'LIGHT_GRAY' 59 ] 60 61 if useColors: 62 return "%c%d%s%c" % (3, irc_colors.index(color), text, 3) 63 else: 64 return text656968 ValueError.__init__(self, string, *more)71 optParameters = [ 72 ["builder", None, None, "which Builder to start"], 73 ["branch", None, None, "which branch to build"], 74 ["revision", None, None, "which revision to build"], 75 ["reason", None, None, "the reason for starting the build"], 76 ["props", None, None, 77 "A set of properties made available in the build environment, " 78 "format is --properties=prop1=value1,prop2=value2,.. " 79 "option can be specified multiple times."], 80 ] 819283 args = list(args) 84 if len(args) > 0: 85 if self['builder'] is not None: 86 raise UsageError("--builder provided in two ways") 87 self['builder'] = args.pop(0) 88 if len(args) > 0: 89 if self['reason'] is not None: 90 raise UsageError("--reason provided in two ways") 91 self['reason'] = " ".join(args)95 hasStarted = False 96 timer = None 9712699 self.parent = parent 100 self.useRevisions = useRevisions 101 self.useColors = useColors 102 self.timer = reactor.callLater(5, self.soon)103105 del self.timer 106 if not self.hasStarted: 107 self.parent.send("The build has been queued, I'll give a shout" 108 " when it starts")109111 self.hasStarted = True 112 if self.timer: 113 self.timer.cancel() 114 del self.timer 115 eta = s.getETA() 116 if self.useRevisions: 117 response = "build containing revision(s) [%s] forced" % s.getRevisions() 118 else: 119 response = "build #%d forced" % s.getNumber() 120 if eta is not None: 121 response = "build forced [ETA %s]" % self.parent.convertTime(eta) 122 self.parent.send(response) 123 self.parent.send("I'll give a shout when the build finishes") 124 d = s.waitUntilFinished() 125 d.addCallback(self.parent.watchedBuildFinished)128 implements(IStatusReceiver) 129 """I hold the state for a single user's interaction with the buildbot. 130 131 There will be one instance of me for each user who interacts personally 132 with the buildbot. There will be an additional instance for each 133 'broadcast contact' (chat rooms, IRC channels as a whole). 134 """ 135560 561 562 command_FORCE.usage = "force build [--branch=branch] [--revision=revision] [--props=prop1=val1,prop2=val2...] <which> <reason> - Force a build" 563137 self.bot = bot 138 self.master = bot.master 139 self.notify_events = {} 140 self.subscribed = 0 141 self.muted = False 142 self.useRevisions = bot.useRevisions 143 self.useColors = bot.useColors 144 self.reported_builds = [] # tuples (when, buildername, buildnum) 145 self.add_notification_events(bot.notify_events) 146 147 # when people send us public messages ("buildbot: command"), 148 # self.dest is the name of the channel ("#twisted"). When they send 149 # us private messages (/msg buildbot command), self.dest is their 150 # username. 151 self.dest = dest152 153 # silliness 154 155 silly = { 156 "What happen ?": [ "Somebody set up us the bomb." ], 157 "It's You !!": ["How are you gentlemen !!", 158 "All your base are belong to us.", 159 "You are on the way to destruction."], 160 "What you say !!": ["You have no chance to survive make your time.", 161 "HA HA HA HA ...."], 162 } 163165 response = self.silly[message] 166 when = 0.5 167 for r in response: 168 reactor.callLater(when, self.send, r) 169 when += 2.5170172 try: 173 b = self.bot.status.getBuilder(which) 174 except KeyError: 175 raise UsageError, "no such builder '%s'" % which 176 return b177179 if not self.bot.control: 180 raise UsageError("builder control is not enabled") 181 try: 182 bc = self.bot.control.getBuilder(which) 183 except KeyError: 184 raise UsageError("no such builder '%s'" % which) 185 return bc186188 """ 189 @rtype: list of L{buildbot.process.builder.Builder} 190 """ 191 names = self.bot.status.getBuilderNames(categories=self.bot.categories) 192 names.sort() 193 builders = [self.bot.status.getBuilder(n) for n in names] 194 return builders195197 if seconds < 60: 198 return "%d seconds" % seconds 199 minutes = int(seconds / 60) 200 seconds = seconds - 60*minutes 201 if minutes < 60: 202 return "%dm%02ds" % (minutes, seconds) 203 hours = int(minutes / 60) 204 minutes = minutes - 60*hours 205 return "%dh%02dm%02ds" % (hours, minutes, seconds)206208 """Returns True if this build should be reported for this contact 209 (eliminating duplicates), and also records the report for later""" 210 for w, b, n in self.reported_builds: 211 if b == builder and n == buildnum: 212 return False 213 self.reported_builds.append([util.now(), builder, buildnum]) 214 215 # clean the reported builds 216 horizon = util.now() - 60 217 while self.reported_builds and self.reported_builds[0][0] < horizon: 218 self.reported_builds.pop(0) 219 220 # and return True, since this is a new one 221 return True222224 self.send("yes?")225 228230 args = shlex.split(args) 231 if len(args) == 0: 232 raise UsageError, "try 'list builders'" 233 if args[0] == 'builders': 234 builders = self.getAllBuilders() 235 str = "Configured builders: " 236 for b in builders: 237 str += b.name 238 state = b.getState()[0] 239 if state == 'offline': 240 str += "[offline]" 241 str += " " 242 str.rstrip() 243 self.send(str) 244 return245 command_LIST.usage = "list builders - List configured builders" 246248 args = shlex.split(args) 249 if len(args) == 0: 250 which = "all" 251 elif len(args) == 1: 252 which = args[0] 253 else: 254 raise UsageError, "try 'status <builder>'" 255 if which == "all": 256 builders = self.getAllBuilders() 257 for b in builders: 258 self.emit_status(b.name) 259 return 260 self.emit_status(which)261 command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" 262264 if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): 265 raise UsageError("try 'notify on|off <EVENT>'")266 269 275 279 283285 for event in events: 286 self.validate_notification_event(event) 287 self.notify_events[event] = 1 288 289 if not self.subscribed: 290 self.subscribe_to_build_events()291293 for event in events: 294 self.validate_notification_event(event) 295 del self.notify_events[event] 296 297 if len(self.notify_events) == 0 and self.subscribed: 298 self.unsubscribe_from_build_events()299 305307 args = shlex.split(args) 308 309 if not args: 310 raise UsageError("try 'notify on|off|list <EVENT>'") 311 action = args.pop(0) 312 events = args 313 314 if action == "on": 315 if not events: events = ('started','finished') 316 self.add_notification_events(events) 317 318 self.list_notified_events() 319 320 elif action == "off": 321 if events: 322 self.remove_notification_events(events) 323 else: 324 self.remove_all_notification_events() 325 326 self.list_notified_events() 327 328 elif action == "list": 329 self.list_notified_events() 330 return 331 332 else: 333 raise UsageError("try 'notify on|off <EVENT>'")334 335 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)" 336338 args = shlex.split(args) 339 if len(args) != 1: 340 raise UsageError("try 'watch <builder>'") 341 which = args[0] 342 b = self.getBuilder(which) 343 builds = b.getCurrentBuilds() 344 if not builds: 345 self.send("there are no builds currently running") 346 return 347 for build in builds: 348 assert not build.isFinished() 349 d = build.waitUntilFinished() 350 d.addCallback(self.watchedBuildFinished) 351 if self.useRevisions: 352 r = "watching build %s containing revision(s) [%s] until it finishes" \ 353 % (which, build.getRevisions()) 354 else: 355 r = "watching build %s #%d until it finishes" \ 356 % (which, build.getNumber()) 357 eta = build.getETA() 358 if eta is not None: 359 r += " [%s]" % self.convertTime(eta) 360 r += ".." 361 self.send(r)362 command_WATCH.usage = "watch <which> - announce the completion of an active build" 363365 if (self.bot.categories != None and 366 builder.category not in self.bot.categories): 367 return 368 369 log.msg('[Contact] Builder %s added' % (builder)) 370 builder.subscribe(self)371373 log.msg('[Contact] Builder %s removed' % (builderName))374376 builder = build.getBuilder() 377 log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) 378 379 # only notify about builders we are interested in 380 381 if (self.bot.categories != None and 382 builder.category not in self.bot.categories): 383 log.msg('Not notifying for a build in the wrong category') 384 return 385 386 if not self.notify_for('started'): 387 return 388 389 if self.useRevisions: 390 r = "build containing revision(s) [%s] on %s started" % \ 391 (build.getRevisions(), builder.getName()) 392 else: 393 r = "build #%d of %s started, including [%s]" % \ 394 (build.getNumber(), 395 builder.getName(), 396 ", ".join([str(c.revision) for c in build.getChanges()]) 397 ) 398 399 self.send(r)400 401 results_descriptions = { 402 SUCCESS: ("Success", 'GREEN'), 403 WARNINGS: ("Warnings", 'YELLOW'), 404 FAILURE: ("Failure", 'RED'), 405 EXCEPTION: ("Exception", 'PURPLE'), 406 RETRY: ("Retry", 'AQUA_LIGHT'), 407 } 408 411413 builder = build.getBuilder() 414 415 if (self.bot.categories != None and 416 builder.category not in self.bot.categories): 417 return 418 419 if not self.notify_for_finished(build): 420 return 421 422 builder_name = builder.getName() 423 buildnum = build.getNumber() 424 buildrevs = build.getRevisions() 425 426 results = self.getResultsDescriptionAndColor(build.getResults()) 427 if self.reportBuild(builder_name, buildnum): 428 if self.useRevisions: 429 r = "build containing revision(s) [%s] on %s is complete: %s" % \ 430 (buildrevs, builder_name, results[0]) 431 else: 432 r = "build #%d of %s is complete: %s" % \ 433 (buildnum, builder_name, results[0]) 434 435 r += ' [%s]' % maybeColorize(" ".join(build.getText()), results[1], self.useColors) 436 buildurl = self.bot.status.getURLForThing(build) 437 if buildurl: 438 r += " Build details are at %s" % buildurl 439 440 if self.bot.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: 441 r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) 442 443 self.send(r)444446 results = build.getResults() 447 448 if self.notify_for('finished'): 449 return True 450 451 if self.notify_for(lower(self.results_descriptions.get(results)[0])): 452 return True 453 454 prevBuild = build.getPreviousBuild() 455 if prevBuild: 456 prevResult = prevBuild.getResults() 457 458 required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)[0]), \ 459 'To', \ 460 capitalize(self.results_descriptions.get(results)[0])), \ 461 '') 462 463 if (self.notify_for(required_notification_control_string)): 464 return True 465 466 return False467469 470 # only notify about builders we are interested in 471 builder = b.getBuilder() 472 if (self.bot.categories != None and 473 builder.category not in self.bot.categories): 474 return 475 476 builder_name = builder.getName() 477 buildnum = b.getNumber() 478 buildrevs = b.getRevisions() 479 480 results = self.getResultsDescriptionAndColor(b.getResults()) 481 if self.reportBuild(builder_name, buildnum): 482 if self.useRevisions: 483 r = "Hey! build %s containing revision(s) [%s] is complete: %s" % \ 484 (builder_name, buildrevs, results[0]) 485 else: 486 r = "Hey! build %s #%d is complete: %s" % \ 487 (builder_name, buildnum, results[0]) 488 489 r += ' [%s]' % maybeColorize(" ".join(b.getText()), results[1], self.useColors) 490 self.send(r) 491 buildurl = self.bot.status.getURLForThing(b) 492 if buildurl: 493 self.send("Build details are at %s" % buildurl)494496 errReply = "try 'force build [--branch=BRANCH] [--revision=REVISION] [--props=PROP1=VAL1,PROP2=VAL2...] <WHICH> <REASON>'" 497 args = shlex.split(args) 498 if not args: 499 raise UsageError(errReply) 500 what = args.pop(0) 501 if what != "build": 502 raise UsageError(errReply) 503 opts = ForceOptions() 504 opts.parseOptions(args) 505 506 which = opts['builder'] 507 branch = opts['branch'] 508 revision = opts['revision'] 509 reason = opts['reason'] 510 props = opts['props'] 511 512 if which is None: 513 raise UsageError("you must provide a Builder, " + errReply) 514 515 # keep weird stuff out of the branch, revision, and properties args. 516 branch_validate = self.master.config.validation['branch'] 517 revision_validate = self.master.config.validation['revision'] 518 pname_validate = self.master.config.validation['property_name'] 519 pval_validate = self.master.config.validation['property_value'] 520 if branch and not branch_validate.match(branch): 521 log.msg("bad branch '%s'" % branch) 522 self.send("sorry, bad branch '%s'" % branch) 523 return 524 if revision and not revision_validate.match(revision): 525 log.msg("bad revision '%s'" % revision) 526 self.send("sorry, bad revision '%s'" % revision) 527 return 528 529 properties = Properties() 530 if props: 531 # split props into name:value dict 532 pdict = {} 533 propertylist = props.split(",") 534 for i in range(0,len(propertylist)): 535 splitproperty = propertylist[i].split("=", 1) 536 pdict[splitproperty[0]] = splitproperty[1] 537 538 # set properties 539 for prop in pdict: 540 pname = prop 541 pvalue = pdict[prop] 542 if not pname_validate.match(pname) \ 543 or not pval_validate.match(pvalue): 544 log.msg("bad property name='%s', value='%s'" % (pname, pvalue)) 545 self.send("sorry, bad property name='%s', value='%s'" % 546 (pname, pvalue)) 547 return 548 properties.setProperty(pname, pvalue, "Force Build IRC") 549 550 bc = self.getControl(which) 551 552 reason = "forced: by %s: %s" % (self.describeUser(who), reason) 553 ss = SourceStamp(branch=branch, revision=revision) 554 d = bc.submitBuildRequest(ss, reason, props=properties.asDict()) 555 def subscribe(buildreq): 556 ireq = IrcBuildRequest(self, self.useRevisions) 557 buildreq.subscribe(ireq.started)558 d.addCallback(subscribe) 559 d.addErrback(log.err, "while forcing a build")565 args = shlex.split(args) 566 if len(args) < 3 or args[0] != 'build': 567 raise UsageError, "try 'stop build WHICH <REASON>'" 568 which = args[1] 569 reason = args[2] 570 571 buildercontrol = self.getControl(which) 572 573 r = "stopped: by %s: %s" % (self.describeUser(who), reason) 574 575 # find an in-progress build 576 builderstatus = self.getBuilder(which) 577 builds = builderstatus.getCurrentBuilds() 578 if not builds: 579 self.send("sorry, no build is currently running") 580 return 581 for build in builds: 582 num = build.getNumber() 583 revs = build.getRevisions() 584 585 # obtain the BuildControl object 586 buildcontrol = buildercontrol.getBuild(num) 587 588 # make it stop 589 buildcontrol.stopBuild(r) 590 591 if self.useRevisions: 592 response = "build containing revision(s) [%s] interrupted" % revs 593 else: 594 response = "build %d interrupted" % num 595 self.send(response)596 597 command_STOP.usage = "stop build <which> <reason> - Stop a running build" 598600 b = self.getBuilder(which) 601 str = "%s: " % which 602 state, builds = b.getState() 603 str += state 604 if state == "idle": 605 last = b.getLastFinishedBuild() 606 if last: 607 start,finished = last.getTimes() 608 str += ", last build %s ago: %s" % \ 609 (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) 610 if state == "building": 611 t = [] 612 for build in builds: 613 step = build.getCurrentStep() 614 if step: 615 s = "(%s)" % " ".join(step.getText()) 616 else: 617 s = "(no current step)" 618 ETA = build.getETA() 619 if ETA is not None: 620 s += " [ETA %s]" % self.convertTime(ETA) 621 t.append(s) 622 str += ", ".join(t) 623 self.send(str)624626 args = shlex.split(args) 627 628 if len(args) == 0: 629 which = "all" 630 elif len(args) == 1: 631 which = args[0] 632 else: 633 raise UsageError, "try 'last <builder>'" 634 635 def emit_last(which): 636 last = self.getBuilder(which).getLastFinishedBuild() 637 if not last: 638 str = "(no builds run since last restart)" 639 else: 640 start,finish = last.getTimes() 641 str = "%s ago: " % (self.convertTime(int(util.now() - finish))) 642 str += " ".join(last.getText()) 643 self.send("last build [%s]: %s" % (which, str))644 645 if which == "all": 646 builders = self.getAllBuilders() 647 for b in builders: 648 emit_last(b.name) 649 return 650 emit_last(which) 651 command_LAST.usage = "last <which> - list last build status for builder <which>" 652654 commands = [] 655 for k in dir(self): 656 if k.startswith('command_'): 657 commands.append(k[8:].lower()) 658 commands.sort() 659 return commands660662 if self.dest[0] == '#': 663 return "IRC user <%s> on channel %s" % (user, self.dest) 664 return "IRC user <%s> (privmsg)" % user665 666 # commands 667669 # The order of these is important! ;) 670 self.send("Shutting up for now.") 671 self.muted = True672 command_MUTE.usage = "mute - suppress all messages until a corresponding 'unmute' is issued" 673675 if self.muted: 676 # The order of these is important! ;) 677 self.muted = False 678 self.send("I'm baaaaaaaaaaack!") 679 else: 680 self.send("You hadn't told me to be quiet, but it's the thought that counts, right?")681 command_UNMUTE.usage = "unmute - disable a previous 'mute'" 682684 args = shlex.split(args) 685 if len(args) == 0: 686 self.send("Get help on what? (try 'help <foo>', " 687 "or 'commands' for a command list)") 688 return 689 command = args[0] 690 meth = self.getCommandMethod(command) 691 if not meth: 692 raise UsageError, "no such command '%s'" % command 693 usage = getattr(meth, 'usage', None) 694 if usage: 695 self.send("Usage: %s" % usage) 696 else: 697 self.send("No usage info for '%s'" % command)698 command_HELP.usage = "help <command> - Give help for <command>" 699 703 command_SOURCE.usage = "source - the source code for Buildbot" 704706 commands = self.build_commands() 707 str = "buildbot commands: " + ", ".join(commands) 708 self.send(str)709 command_COMMANDS.usage = "commands - List available commands" 710712 self.act("readies phasers")713715 reactor.callLater(1.0, self.send, "<(^.^<)") 716 reactor.callLater(2.0, self.send, "<(^.^)>") 717 reactor.callLater(3.0, self.send, "(>^.^)>") 718 reactor.callLater(3.5, self.send, "(7^.^)7") 719 reactor.callLater(5.0, self.send, "(>^.^<)")720 721 # communication with the user 722 726 730 731 # main dispatchers for incoming messages 732734 return getattr(self, 'command_' + command.upper(), None)735737 # a message has arrived from 'who'. For broadcast contacts (i.e. when 738 # people do an irc 'buildbot: command'), this will be a string 739 # describing the sender of the message in some useful-to-log way, and 740 # a single Contact may see messages from a variety of users. For 741 # unicast contacts (i.e. when people do an irc '/msg buildbot 742 # command'), a single Contact will only ever see messages from a 743 # single user. 744 message = message.lstrip() 745 if self.silly.has_key(message): 746 self.doSilly(message) 747 return defer.succeed(None) 748 749 parts = message.split(' ', 1) 750 if len(parts) == 1: 751 parts = parts + [''] 752 cmd, args = parts 753 log.msg("irc command", cmd) 754 755 meth = self.getCommandMethod(cmd) 756 if not meth and message[-1] == '!': 757 self.send("What you say!") 758 return defer.succeed(None) 759 760 if meth: 761 d = defer.maybeDeferred(meth, args.strip(), who) 762 @d.addErrback 763 def usageError(f): 764 f.trap(UsageError) 765 self.send(str(f.value))766 @d.addErrback 767 def logErr(f): 768 log.err(f) 769 self.send("Something bad happened (see logs)") 770 d.addErrback(log.err) 771 return d 772 return defer.succeed(None) 773775 # this is sent when somebody performs an action that mentions the 776 # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of 777 # the person who performed the action, so if their action provokes a 778 # response, they can be named. This is 100% silly. 779 if not data.endswith("s "+ self.bot.nickname): 780 return 781 words = data.split() 782 verb = words[-2] 783 if verb == "kicks": 784 response = "%s back" % verb 785 else: 786 response = "%s %s too" % (verb, user) 787 self.act(response)788791 """I represent the buildbot to an IRC server. 792 """ 793 contactClass = IRCContact 794891795 - def __init__(self, nickname, password, channels, pm_to_nicks, status, 796 categories, notify_events, noticeOnChannel=False, 797 useRevisions=False, showBlameList=False, useColors=True):798 self.nickname = nickname 799 self.channels = channels 800 self.pm_to_nicks = pm_to_nicks 801 self.password = password 802 self.status = status 803 self.master = status.master 804 self.categories = categories 805 self.notify_events = notify_events 806 self.hasQuit = 0 807 self.contacts = {} 808 self.noticeOnChannel = noticeOnChannel 809 self.useColors = useColors 810 self.useRevisions = useRevisions 811 self.showBlameList = showBlameList 812 self._keepAliveCall = task.LoopingCall(lambda: self.ping(self.nickname))813 817819 if self._keepAliveCall.running: 820 self._keepAliveCall.stop() 821 irc.IRCClient.connectionLost(self, reason)822824 if self.noticeOnChannel and dest[0] == '#': 825 self.notice(dest, message) 826 else: 827 self.msg(dest, message)828830 name = name.lower() # nicknames and channel names are case insensitive 831 if name in self.contacts: 832 return self.contacts[name] 833 new_contact = self.contactClass(self, name) 834 self.contacts[name] = new_contact 835 return new_contact836838 log.msg("%s: %s" % (self, msg))839 840 841 # the following irc.IRCClient methods are called when we have input 842844 user = user.split('!', 1)[0] # rest is ~user@hostname 845 # channel is '#twisted' or 'buildbot' (for private messages) 846 if channel == self.nickname: 847 # private message 848 contact = self.getContact(user) 849 contact.handleMessage(message, user) 850 return 851 # else it's a broadcast message, maybe for us, maybe not. 'channel' 852 # is '#twisted' or the like. 853 contact = self.getContact(channel) 854 if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): 855 message = message[len("%s:" % self.nickname):] 856 contact.handleMessage(message, user)857859 user = user.split('!', 1)[0] # rest is ~user@hostname 860 # somebody did an action (/me actions) in the broadcast channel 861 contact = self.getContact(channel) 862 if self.nickname in data: 863 contact.handleAction(data, user)864866 if self.password: 867 self.msg("Nickserv", "IDENTIFY " + self.password) 868 for c in self.channels: 869 if isinstance(c, dict): 870 channel = c.get('channel', None) 871 password = c.get('password', None) 872 else: 873 channel = c 874 password = None 875 self.join(channel=channel, key=password) 876 for c in self.pm_to_nicks: 877 self.getContact(c)878880 self.log("I have joined %s" % (channel,)) 881 # trigger contact contructor, which in turn subscribes to notify events 882 self.getContact(channel)883885 self.log("I have left %s" % (channel,))886894 lostDelay = random.randint(1, 5) 895 failedDelay = random.randint(45, 60) 896908898 if lostDelay is not None: 899 self.lostDelay = lostDelay 900 if failedDelay is not None: 901 self.failedDelay = failedDelay902 905911 protocol = IrcStatusBot 912 913 status = None 914 control = None 915 shuttingDown = False 916 p = None 917973918 - def __init__(self, nickname, password, channels, pm_to_nicks, categories, notify_events, 919 noticeOnChannel=False, useRevisions=False, showBlameList=False, 920 lostDelay=None, failedDelay=None, useColors=True):921 ThrottledClientFactory.__init__(self, lostDelay=lostDelay, 922 failedDelay=failedDelay) 923 self.status = None 924 self.nickname = nickname 925 self.password = password 926 self.channels = channels 927 self.pm_to_nicks = pm_to_nicks 928 self.categories = categories 929 self.notify_events = notify_events 930 self.noticeOnChannel = noticeOnChannel 931 self.useRevisions = useRevisions 932 self.showBlameList = showBlameList 933 self.useColors = useColors934 939941 self.shuttingDown = True 942 if self.p: 943 self.p.quit("buildmaster reconfigured: bot disconnecting")944946 p = self.protocol(self.nickname, self.password, 947 self.channels, self.pm_to_nicks, self.status, 948 self.categories, self.notify_events, 949 noticeOnChannel = self.noticeOnChannel, 950 useColors = self.useColors, 951 useRevisions = self.useRevisions, 952 showBlameList = self.showBlameList) 953 p.factory = self 954 p.status = self.status 955 p.control = self.control 956 self.p = p 957 return p958 959 # TODO: I think a shutdown that occurs while the connection is being 960 # established will make this explode 961963 if self.shuttingDown: 964 log.msg("not scheduling reconnection attempt") 965 return 966 ThrottledClientFactory.clientConnectionLost(self, connector, reason)967969 if self.shuttingDown: 970 log.msg("not scheduling reconnection attempt") 971 return 972 ThrottledClientFactory.clientConnectionFailed(self, connector, reason)976 implements(IStatusReceiver) 977 978 in_test_harness = False 979 980 compare_attrs = ["host", "port", "nick", "password", 981 "channels", "pm_to_nicks", "allowForce", "useSSL", 982 "useRevisions", "categories", "useColors", 983 "lostDelay", "failedDelay"] 9841036985 - def __init__(self, host, nick, channels, pm_to_nicks=[], port=6667, 986 allowForce=False, categories=None, password=None, notify_events={}, 987 noticeOnChannel = False, showBlameList = True, useRevisions=False, 988 useSSL=False, lostDelay=None, failedDelay=None, useColors=True):989 base.StatusReceiverMultiService.__init__(self) 990 991 assert allowForce in (True, False) # TODO: implement others 992 993 # need to stash these so we can detect changes later 994 self.host = host 995 self.port = port 996 self.nick = nick 997 self.channels = channels 998 self.pm_to_nicks = pm_to_nicks 999 self.password = password 1000 self.allowForce = allowForce 1001 self.useRevisions = useRevisions 1002 self.categories = categories 1003 self.notify_events = notify_events 1004 1005 self.f = IrcStatusFactory(self.nick, self.password, 1006 self.channels, self.pm_to_nicks, 1007 self.categories, self.notify_events, 1008 noticeOnChannel = noticeOnChannel, 1009 useRevisions = useRevisions, 1010 showBlameList = showBlameList, 1011 lostDelay = lostDelay, 1012 failedDelay = failedDelay, 1013 useColors = useColors) 1014 1015 if useSSL: 1016 # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo 1017 if not have_ssl: 1018 raise RuntimeError("useSSL requires PyOpenSSL") 1019 cf = ssl.ClientContextFactory() 1020 c = internet.SSLClient(self.host, self.port, self.f, cf) 1021 else: 1022 c = internet.TCPClient(self.host, self.port, self.f) 1023 1024 c.setServiceParent(self)10251027 base.StatusReceiverMultiService.setServiceParent(self, parent) 1028 self.f.status = parent 1029 if self.allowForce: 1030 self.f.control = interfaces.IControl(self.master)10311033 # make sure the factory will stop reconnecting 1034 self.f.shutdown() 1035 return base.StatusReceiverMultiService.stopService(self)
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Nov 21 16:22:58 2012 | http://epydoc.sourceforge.net |