PageRenderTime 65ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/ZenPacks.chudler.xmppBot/ZenPacks/chudler/xmppBot/xmppBot.py

https://github.com/searthrowl/Community-Zenpacks
Python | 211 lines | 162 code | 32 blank | 17 comment | 34 complexity | 3879b9e82d9ebe67b8a570965cb586e1 MD5 | raw file
  1. #!/usr/bin/env python
  2. """Start the jabber bot and load our plugins"""
  3. import Globals
  4. import sys
  5. import os, glob
  6. import transaction
  7. from Jabber.Adapter import TwistedJabberClient
  8. from Jabber.Plugins import initPluginSystem, findPlugins
  9. HAVE_SSL = False
  10. try:
  11. from twisted.internet import ssl
  12. HAVE_SSL = True
  13. except ImportError:
  14. ssl = None
  15. from Products.ZenUtils.ZCmdBase import ZCmdBase
  16. from Products.ZenHub.PBDaemon import PBDaemon
  17. from Products.ZenEvents.zenactions import *
  18. class XmppBot(ZenActions, ZCmdBase, PBDaemon):
  19. def buildOptions(self):
  20. ZCmdBase.buildOptions(self)
  21. self.parser.add_option( '--jabber_pass', dest='jabber_pass', type='string', help='Password used to connect to the XMPP server')
  22. self.parser.add_option( '--jabber_user', dest='jabber_user', type='string', help='Username used to connect to the XMPP server')
  23. self.parser.add_option( '--jabber_host', dest='jabber_host', type='string', default='localhost', help='(OPTIONAL) XMPP server to connect')
  24. self.parser.add_option( '--jabber_port', dest='jabber_port', type='int', help='(OPTIONAL) Port used to connect to the XMPP server')
  25. self.parser.add_option( '--first_user', dest='first_user', type='string', help='User mapping to bootstrap the bot with at least on authorized user. Example specification myzenossname,myjabberid@server.example.com')
  26. self.parser.add_option( '--resource', dest='resource', type='string', default='xmppbot', help='(OPTIONAL) jabber resource name.')
  27. self.parser.add_option( '--im_host', dest='im_host', type='string', help='(OPTIONAL) option for addressing IM users when the server is not known. If this is ommitted, jabber_host will be used.')
  28. self.parser.add_option( '--ssl', dest='ssl', action='store_true', help='(OPTIONAL) ssl.')
  29. self.parser.add_option( '--group_server', type='string', dest='group_server', help='(OPTIONAL) Conference/groupchat server. If the name has no dots, it will use group_server.jabber_host')
  30. self.parser.add_option( '--chatrooms', dest='chatrooms', action='append', type='string', help='(OPTIONAL) First chatroom joined to when connecting. May be specified multiple times to join multiple rooms.')
  31. self.parser.add_option('--cycletime', dest='cycletime', default=60, type='int', help='(OPTIONAL) check events every cycletime seconds')
  32. self.parser.add_option( '--zopeurl', type='string', dest='zopeurl', default='http://%s:%d' % (socket.getfqdn(), 8080), help='(OPTIONAL) http path to the root of the zope server')
  33. self.parser.add_option('--monitor', type='string', dest='monitor', default=DEFAULT_MONITOR, help='(OPTIONAL) Name of monitor instance to use for heartbeat events. Default is %s.' % DEFAULT_MONITOR)
  34. def __init__(self):
  35. PBDaemon.__init__(self, keeproot=True)
  36. ZCmdBase.__init__(self)
  37. wants_ssl = self.options.ssl
  38. if wants_ssl and not HAVE_SSL:
  39. self.log.error('SSL was requested for Jabber connection, but pyopenssl is not installed. Please install it and start the xmppBot again.')
  40. sys.exit(2)
  41. if not self.options.jabber_pass:
  42. self.log.error('--jabber_pass is required')
  43. sys.exit(2)
  44. if not self.options.jabber_user:
  45. self.log.error('--jabber_user is required')
  46. sys.exit(2)
  47. if self.options.first_user:
  48. try:
  49. zenUser, jabberId = self.options.first_user.split(',')
  50. except ValueError:
  51. self.log.error('--first_user option must contain both zenuser and jabberid separated by a comma. Example: chudler,chudler@im.example.com')
  52. sys.exit(2)
  53. if zenUser and jabberId:
  54. self.setFirstUser(zenUser, jabberId)
  55. else:
  56. self.log.error('--first_user option must contain both zenuser and jabberid separated by a comma. Example: chudler,chudler@im.example.com')
  57. sys.exit(2)
  58. # taken from zenactions.py
  59. self.schedule = Schedule(self.options, self.dmd)
  60. self.actions = []
  61. self.loadActionRules()
  62. self.updateCheck = UpdateCheck()
  63. self.sendEvent(Event.Event(device=self.options.monitor,
  64. eventClass=App_Start, summary='Jabber Bot started', severity=0, component='xmppbot'))
  65. password = self.options.jabber_pass
  66. chatrooms = self.options.chatrooms
  67. username = self.options.jabber_user
  68. server = self.options.jabber_host
  69. port = self.options.jabber_port
  70. groupServer = self.options.group_server
  71. realHost = self.options.im_host
  72. resource = self.options.resource
  73. self.client = TwistedJabberClient(server=server, username=username, password=password,
  74. port=port,
  75. groupServer=groupServer,
  76. chatrooms=chatrooms, ssl=wants_ssl,
  77. realHost=realHost, resource=resource)
  78. path = os.path.realpath(sys.argv[0])
  79. pluginPath = os.path.dirname(path) + '/Jabber/plugins'
  80. self.log.info("xmppBot plugins will be loaded from %s" % pluginPath)
  81. plugins = [pluginFile.split('/')[-1].split('.py')[0] for pluginFile in glob.glob( os.path.join(pluginPath, '*.py') )]
  82. self.log.debug("xmppBot loading pugins %s" % ', '.join(plugins))
  83. initPluginSystem(pluginPath=pluginPath, plugins=plugins, jabberClient=self.client)
  84. # connect to the jabber server
  85. self.log.info('Connecting to server')
  86. reactor = self.client.connect()
  87. # begin looking for zenevents
  88. self.schedule.start()
  89. self.runCycle()
  90. reactor.suggestThreadPoolSize(10)
  91. reactor.run()
  92. def setFirstUser(self, zenUser, jabberId):
  93. zenUser = zenUser.lower()
  94. haveUser = False
  95. for user in self.dmd.ZenUsers.getAllUserSettings():
  96. if user.id.lower() == zenUser:
  97. haveUser = True
  98. user._updateProperty('JabberId', jabberId)
  99. transaction.commit()
  100. break
  101. return haveUser
  102. # ripped from zenactions.py
  103. def mainbody(self):
  104. """main loop to run actions.
  105. """
  106. from twisted.internet.process import reapAllProcesses
  107. reapAllProcesses()
  108. zem = self.dmd.ZenEventManager
  109. self.loadActionRules()
  110. self.processRules(zem)
  111. # ripped from zenactions.py
  112. def runCycle(self):
  113. try:
  114. start = time.time()
  115. self.syncdb()
  116. self.mainbody()
  117. self.log.info('processed %s rules in %.2f secs', len(self.actions), time.time()-start)
  118. except:
  119. self.log.exception('unexpected exception')
  120. reactor.callLater(60, self.runCycle)
  121. # ripped from zenactions.py
  122. def loadActionRules(self):
  123. self.actions = []
  124. for ar in self.dmd.ZenUsers.getAllActionRules():
  125. if not ar.enabled: continue
  126. if not ar.action.title() == 'Xmpp': continue
  127. userid = ar.getUser().id
  128. self.actions.append(ar)
  129. self.log.debug('action:%s for:%s loaded', ar.getId(), userid)
  130. # ripped from zenactions.py
  131. def sendHeartbeat(self):
  132. """Send a heartbeat event for this monitor.
  133. """
  134. timeout = self.options.cycletime*3
  135. evt = Event.EventHeartbeat(self.options.monitor, 'xmppbot', timeout)
  136. self.sendEvent(evt)
  137. self.niceDoggie(self.options.cycletime)
  138. # zenactions will call sendXmpp when an event comes in that the user wants
  139. # to see.
  140. def sendXmpp(self, action, data, clear = None):
  141. message, body = self.format(action, data, clear)
  142. recipients = self.getAddress(action)
  143. if not recipients:
  144. self.log.warning('failed to send message %s on rule %s: %s', action.getUser().id, action.id, 'Unspecified recipient.')
  145. return True
  146. for recipient in recipients:
  147. self.log.debug('Sending message to %s: %s', recipient, message)
  148. if recipient.lower().endswith('/groupchat'):
  149. messageType = 'groupchat'
  150. else:
  151. messageType = 'chat'
  152. self.client.sendMessage(message, recipient, messageType)
  153. return True
  154. def getAddress(self, action):
  155. if action.targetAddr:
  156. return [action.targetAddr]
  157. else:
  158. results = []
  159. zenUser = action.getUser()
  160. # attempt to detect a group and resolve its users, otherwise see if it is a user
  161. if 'getMemberUserIds' in dir(zenUser):
  162. for username in zenUser.getMemberUserIds():
  163. try:
  164. results.append(zenUser.getUserSettings(username).getProperty('JabberId').strip())
  165. except None:
  166. self.log.error('Unable to send xmpp alert message to %s. This might happen if they are missing the jabberId property. Try the bot command "setjid"' % username)
  167. # getEmailAddresses should at least identify an entity that would have a jabberId on it, unless it was a group (above)
  168. elif 'getEmailAddresses' in dir(zenUser):
  169. try:
  170. results.append(zenUser.getProperty('JabberId').lower())
  171. except None:
  172. self.log.error('Unable to send xmpp alert message to %s. This might happen if they are missing the jabberId property. Try the bot command "setjid"' % zenUser)
  173. else:
  174. self.log.error('Unable to send xmpp alert message to %s. Please report this error to the author of this plugin' % zenUser)
  175. return results
  176. if __name__ == '__main__':
  177. import logging
  178. logging.getLogger('zen.Events').setLevel(80)
  179. bot = XmppBot()