class ChatRoom(Entity): """models the actions needed to interact in a chatroom""" implements(IDroneModelChatRoom) jabber = property(lambda s: services.getService('jabber')) jabber_config = property(lambda s: s.jabber.SERVICECONFIG) room = property(lambda s: s._room) jid = property(lambda s: '%s@%s' % \ (s.room, s.jabber_config.JABBER_CHAT_SERVICE)) nick = property(lambda s: s.jabber_config.JABBER_CHAT_NICK) def __init__(self, room): self._room = room self.conversation = Conversation(self.jid) self.conversation.groupChat = True def join(self): """join the chat room""" self.jabber.joinChatRoom(self.room) def leave(self): """leave the chat room""" self.jabber.leaveChatRoom(self.jid) def hear(self, message): """Only pay attention to messages that start with my chat nick""" if message.lower().split()[0] == self.nick: message = message[len(self.nick):].strip() self.conversation.hear(message)
# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### import os, re from types import FunctionType from droned.logging import log, err from droned.errors import ServiceNotAvailable import config import services try: #handle abstaction to service config jabber_service = services.getService('jabber') jabber_config = jabber_service.SERVICECONFIG except ServiceNotAvailable: jabber_service = None jabber_config = None #holds the responders responders = {} def loadAll(): """loads all of the responders""" responders.clear() my_dir = os.path.dirname(__file__) for filename in os.listdir(my_dir): if not filename.endswith('.py'): continue if filename == '__init__.py': continue
def running(): return bool(service) and getService(dependant_service).running
def get_resource(): """get the webserver resource from the droned service""" #thanks to twisted interface definitions we know whic argument is #the site object in which the original resource is attached. return getService(dependant_service).service.args[1].resource
# See the License for the specific language governing permissions and # limitations under the License. ############################################################################### from twisted.internet import task from droned.models.conversation import Conversation, ChatRoom from droned.models.environment import env from droned.models.event import Event from droned.models.team import Team from droned.models.server import Server from droned.errors import ServiceNotAvailable import services import config try: jabber = services.getService('jabber') jconfig = jabber.SERVICECONFIG except ServiceNotAvailable: jabber = None jconfig = None def notify_online(event): ready = env.ready for agent in Team('support').agents: agent.tell("%s droned reporting for duty." % config.ROMEO_ENV_NAME) if not ready: agent.tell("The environment is still being scanned, please wait...") def check(): if env.ready: for agent in Team('support').agents:
class Team(Entity): jabber_service = property(lambda s: services.getService('jabber')) jabber_config = property(lambda s: s.jabber_service.SERVICECONFIG) availableAgents = property( lambda s: (agent for agent in s.agents if agent.available)) available = property(lambda self: any(self.availableAgents)) busy = property(lambda s: not s.available) rosterPath = property(lambda s: '%s/%s' % (s.jabber_config.JABBER_TEAM_ROSTER, s.name)) pendingIssues = property(lambda s: \ (i for i in Issue.objects if not i.resolved and i.assignedTo == s)) def __init__(self, name): self.name = name self.agents = set() self.worker = LoopingCall(self.workNextIssue) self.worker.start(5) self.loadRoster() def __getstate__(self): return { 'name': self.name, 'members': set(agent.jid for agent in self.agents), } @staticmethod def construct(state): team = Team(state['name']) team.agents = set(SupportAgent(jid) for jid in state['members']) return team def loadRoster(self): if not os.path.exists(self.rosterPath): log('no roster exists for the %s team, so it has no members!' % self.name, warning=True) return for line in open(self.rosterPath): line = line.strip() if not line or line.startswith('#'): continue jid = line agent = SupportAgent(jid) self.agents.add(agent) def saveRoster(self): jids = [agent.jid for agent in self.agents] content = '\n'.join(jids) + '\n' def blockingWrite(): roster = open(self.rosterPath, 'w') roster.write(content) roster.close() config.reactor.callInThread(blockingWrite) def addMember(self, jid): self.agents.add(SupportAgent(jid)) self.saveRoster() def removeMember(self, jid): self.agents.discard(SupportAgent(jid)) self.saveRoster() def workNextIssue(self): while any(self.availableAgents) and any(self.pendingIssues): issue = self.pendingIssues.next() agent = self.availableAgents.next() agent.engage(issue) issue.whenResolved(lambda result: self.workNextIssue() or result) def notify(self, message): for agent in self.agents: agent.tell("<b>%s team notification:</b> %s" % (self.name, message))
# See the License for the specific language governing permissions and # limitations under the License. ############################################################################### from twisted.internet import task from droned.models.conversation import Conversation, ChatRoom from droned.models.environment import env from droned.models.event import Event from droned.models.team import Team from droned.models.server import Server from droned.errors import ServiceNotAvailable import services import config try: jabber = services.getService('jabber') jconfig = jabber.SERVICECONFIG except ServiceNotAvailable: jabber = None jconfig = None def notify_online(event): ready = env.ready for agent in Team('support').agents: agent.tell("%s droned reporting for duty." % config.ROMEO_ENV_NAME) if not ready: agent.tell( "The environment is still being scanned, please wait...") def check():
# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### import os, re from types import FunctionType from droned.logging import log, err from droned.errors import ServiceNotAvailable import config import services try: #handle abstaction to service config jabber_service = services.getService('jabber') jabber_config = jabber_service.SERVICECONFIG except ServiceNotAvailable: jabber_service = None jabber_config = None #holds the responders responders = {} def loadAll(): """loads all of the responders""" responders.clear() my_dir = os.path.dirname(__file__) for filename in os.listdir(my_dir): if not filename.endswith('.py'): continue
class Conversation(Entity): """model of a conversation""" implements(IDroneModelConversation) jabber = property(lambda s: services.getService('jabber')) jabber_config = property(lambda s: s.jabber.SERVICECONFIG) deferredAnswer = None expectedAnswers = () askingQuestion = property(lambda s: s.deferredAnswer is not None) groupChat = False authorized = property(lambda s: \ s._authorized or s.buddy == s.jabber_config.DEPUTY) _authorized = False idleTime = property(lambda s: time.time() - s.lastMessageReceived) responsive = property(lambda s: \ s.idleTime < s.jabber_config.CONVERSATION_RESPONSE_PERIOD) online = True #TODO: need to safely track this state in Jabber service buddy = property(lambda s: s._buddy) buddyName = property(lambda s: s._buddyName) serializable = True def __init__(self, buddy): self._buddy = buddy self._buddyName = buddy.split('@')[0] self.notificationQueue = [] self.lastMessageReceived = 0.0 self.context = ConversationContext(self) def __getstate__(self): return { 'buddy': self.buddy, 'authorized': self.authorized, 'subscriptions': list(self.context.get('subscriptions', [])) } @staticmethod def construct(state): conversation = Conversation(state['buddy']) conversation.context['subscriptions'] = set(state['subscriptions']) if state.get('authorized'): conversation.grantAuthorization(notify=False) for name in state['subscriptions']: Event(name).subscribe(conversation.notify) return conversation @staticmethod def byName(name): """Convenience method for looking up Conversation objects by short or long names """ for conversation in Conversation.objects: if conversation.buddy == name: #full jid return conversation elif conversation.buddyName == name: #short username return conversation def say(self, message, **options): "Send a message to the remote party" options['groupChat'] = self.groupChat self.jabber.sendMessage(self.buddy, message, **options) def hear(self, message): "Receive a message from the remote party" self.lastMessageReceived = time.time() for answer in self.expectedAnswers: if message == answer: d = self.deferredAnswer del self.expectedAnswers #Clear this before the callback in case it asks another question del self.deferredAnswer d.callback(answer) return try: dispatch(self, message) except NoMatch: self.say( "Sorry I don't know what you mean by that. Say <b>?</b> for help." ) except: self.say(Failure().getTraceback(), useHTML=False) def ask(self, question, answers): """Sends a message and returns a Deferred that fires whenever one of the given answers is received """ if self.askingQuestion: raise AlreadyAskingQuestion(self.context['question'], self.expectedAnswers) self.say(question) self.context['question'] = question self.context['expectedAnswers'] = answers self.deferredAnswer = Deferred() self.expectedAnswers = answers return self.deferredAnswer def nevermind(self): """forget the context of the conversation""" if self.askingQuestion: if 'question' in self.context: del self.context['question'] if 'expectedAnswers' in self.context: del self.context['expectedAnswers'] if hasattr(self, 'deferredAnswer'): # Probably best to just leave the deferred hanging #if not self.deferredAnswer.called: # self.deferredAnswer.callback(None) del self.deferredAnswer if hasattr(self, 'expectedAnswers'): del self.expectedAnswers def grantAuthorization(self, notify=True): """method to allow this conversation extra priviledges""" self._authorized = True self.context['authorized'] = True if not notify: return self.say( "You have been granted authorization by the environment administrator." ) def revokeAuthorization(self, notify=True): """method to deny this conversation extra priviledges""" self._authorized = False self.context['authorized'] = False self.nevermind() if not notify: return self.say( "Your authorization has been revoked by the environment administrator." ) def notify(self, event): """Tells the remote party that an event occurred""" self.notificationQueue.append(event) if len(self.notificationQueue) == 1: config.reactor.callLater(1, self.sendNotifications) def sendNotifications(self): """Formats and sends notification to the remote party""" message = "" for event in self.notificationQueue: params = ', '.join("%s=%s" % item for item in event.params.items()) params = escapeToXml(params) message += "[<b>%s</b> occurred (%s)]<br/>" % (event.name, params) self.notificationQueue = [] preStyle = '<font color="gray"><i>' postStyle = '</i></font>' self.say(preStyle + message + postStyle)