Merge branch 'develop' into stream_features
This commit is contained in:
		| @@ -119,7 +119,7 @@ class xep_0030(base_plugin): | ||||
|     def post_init(self): | ||||
|         """Handle cross-plugin dependencies.""" | ||||
|         base_plugin.post_init(self) | ||||
|         if self.xmpp['xep_0059']: | ||||
|         if 'xep_0059' in self.xmpp.plugin: | ||||
|             register_stanza_plugin(DiscoItems, | ||||
|                                    self.xmpp['xep_0059'].stanza.Set) | ||||
|  | ||||
|   | ||||
| @@ -1,104 +0,0 @@ | ||||
| """ | ||||
|     SleekXMPP: The Sleek XMPP Library | ||||
|     Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of SleekXMPP. | ||||
|  | ||||
|     See the file LICENSE for copying permissio | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| from . import base | ||||
| from .. xmlstream.handler.callback import Callback | ||||
| from .. xmlstream.matcher.xpath import MatchXPath | ||||
| from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID | ||||
| from .. stanza.message import Message | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class ChatState(ElementBase): | ||||
|     namespace = 'http://jabber.org/protocol/chatstates' | ||||
|     plugin_attrib = 'chat_state' | ||||
|     interface = set(('state',)) | ||||
|     states = set(('active', 'composing', 'gone', 'inactive', 'paused')) | ||||
|  | ||||
|     def active(self): | ||||
|         self.setState('active') | ||||
|  | ||||
|     def composing(self): | ||||
|         self.setState('composing') | ||||
|  | ||||
|     def gone(self): | ||||
|         self.setState('gone') | ||||
|  | ||||
|     def inactive(self): | ||||
|         self.setState('inactive') | ||||
|  | ||||
|     def paused(self): | ||||
|         self.setState('paused') | ||||
|  | ||||
|     def setState(self, state): | ||||
|         if state in self.states: | ||||
|             self.name = state | ||||
|             self.xml.tag = '{%s}%s' % (self.namespace, state) | ||||
|         else: | ||||
|             raise ValueError('Invalid chat state') | ||||
|  | ||||
|     def getState(self): | ||||
|         return self.name | ||||
|  | ||||
| # In order to match the various chat state elements, | ||||
| # we need one stanza object per state, even though | ||||
| # they are all the same except for the initial name | ||||
| # value. Do not depend on the type of the chat state | ||||
| # stanza object for the actual state. | ||||
|  | ||||
| class Active(ChatState): | ||||
|     name = 'active' | ||||
| class Composing(ChatState): | ||||
|     name = 'composing' | ||||
| class Gone(ChatState): | ||||
|     name = 'gone' | ||||
| class Inactive(ChatState): | ||||
|     name = 'inactive' | ||||
| class Paused(ChatState): | ||||
|     name = 'paused' | ||||
|  | ||||
|  | ||||
| class xep_0085(base.base_plugin): | ||||
|     """ | ||||
|     XEP-0085 Chat State Notifications | ||||
|     """ | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xep = '0085' | ||||
|         self.description = 'Chat State Notifications' | ||||
|  | ||||
|         handlers = [('Active Chat State', 'active'), | ||||
|                     ('Composing Chat State', 'composing'), | ||||
|                     ('Gone Chat State', 'gone'), | ||||
|                     ('Inactive Chat State', 'inactive'), | ||||
|                     ('Paused Chat State', 'paused')] | ||||
|         for handler in handlers: | ||||
|             self.xmpp.registerHandler( | ||||
|                 Callback(handler[0], | ||||
|                          MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns, | ||||
|                                                             ChatState.namespace, | ||||
|                                                             handler[1])), | ||||
|                          self._handleChatState)) | ||||
|  | ||||
|         registerStanzaPlugin(Message, Active) | ||||
|         registerStanzaPlugin(Message, Composing) | ||||
|         registerStanzaPlugin(Message, Gone) | ||||
|         registerStanzaPlugin(Message, Inactive) | ||||
|         registerStanzaPlugin(Message, Paused) | ||||
|  | ||||
|     def post_init(self): | ||||
|         base.base_plugin.post_init(self) | ||||
|         self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates') | ||||
|  | ||||
|     def _handleChatState(self, msg): | ||||
|         state = msg['chat_state'].name | ||||
|         log.debug("Chat State: %s, %s" % (state, msg['from'].jid)) | ||||
|         self.xmpp.event('chatstate_%s' % state, msg) | ||||
							
								
								
									
										10
									
								
								sleekxmpp/plugins/xep_0085/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								sleekxmpp/plugins/xep_0085/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| """ | ||||
|     SleekXMPP: The Sleek XMPP Library | ||||
|     Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of SleekXMPP. | ||||
|  | ||||
|     See the file LICENSE for copying permissio | ||||
| """ | ||||
|  | ||||
| from sleekxmpp.plugins.xep_0085.stanza import ChatState | ||||
| from sleekxmpp.plugins.xep_0085.chat_states import xep_0085 | ||||
							
								
								
									
										49
									
								
								sleekxmpp/plugins/xep_0085/chat_states.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								sleekxmpp/plugins/xep_0085/chat_states.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| """ | ||||
|     SleekXMPP: The Sleek XMPP Library | ||||
|     Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of SleekXMPP. | ||||
|  | ||||
|     See the file LICENSE for copying permissio | ||||
| """ | ||||
|  | ||||
| import logging | ||||
|  | ||||
| import sleekxmpp | ||||
| from sleekxmpp.stanza import Message | ||||
| from sleekxmpp.xmlstream.handler import Callback | ||||
| from sleekxmpp.xmlstream.matcher import StanzaPath | ||||
| from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET | ||||
| from sleekxmpp.plugins.base import base_plugin | ||||
| from sleekxmpp.plugins.xep_0085 import stanza, ChatState | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class xep_0085(base_plugin): | ||||
|  | ||||
|     """ | ||||
|     XEP-0085 Chat State Notifications | ||||
|     """ | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xep = '0085' | ||||
|         self.description = 'Chat State Notifications' | ||||
|         self.stanza = stanza | ||||
|  | ||||
|         for state in ChatState.states: | ||||
|             self.xmpp.register_handler( | ||||
|                 Callback('Chat State: %s' % state, | ||||
|                          StanzaPath('message@chat_state=%s' % state), | ||||
|                          self._handle_chat_state)) | ||||
|  | ||||
|         register_stanza_plugin(Message, ChatState) | ||||
|  | ||||
|     def post_init(self): | ||||
|         base_plugin.post_init(self) | ||||
|         self.xmpp.plugin['xep_0030'].add_feature(ChatState.namespace) | ||||
|  | ||||
|     def _handle_chat_state(self, msg): | ||||
|         state = msg['chat_state'] | ||||
|         log.debug("Chat State: %s, %s" % (state, msg['from'].jid)) | ||||
|         self.xmpp.event('chatstate_%s' % state, msg) | ||||
							
								
								
									
										73
									
								
								sleekxmpp/plugins/xep_0085/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								sleekxmpp/plugins/xep_0085/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| """ | ||||
|     SleekXMPP: The Sleek XMPP Library | ||||
|     Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of SleekXMPP. | ||||
|  | ||||
|     See the file LICENSE for copying permissio | ||||
| """ | ||||
|  | ||||
| import sleekxmpp | ||||
| from sleekxmpp.xmlstream import ElementBase, ET | ||||
|  | ||||
|  | ||||
| class ChatState(ElementBase): | ||||
|  | ||||
|     """ | ||||
|     Example chat state stanzas: | ||||
|         <message> | ||||
|           <active xmlns="http://jabber.org/protocol/chatstates" /> | ||||
|         </message> | ||||
|  | ||||
|         <message> | ||||
|           <paused xmlns="http://jabber.org/protocol/chatstates" /> | ||||
|         </message> | ||||
|  | ||||
|     Stanza Interfaces: | ||||
|         chat_state | ||||
|  | ||||
|     Attributes: | ||||
|         states | ||||
|  | ||||
|     Methods: | ||||
|         get_chat_state | ||||
|         set_chat_state | ||||
|         del_chat_state | ||||
|     """ | ||||
|  | ||||
|     name = '' | ||||
|     namespace = 'http://jabber.org/protocol/chatstates' | ||||
|     plugin_attrib = 'chat_state' | ||||
|     interfaces = set(('chat_state',)) | ||||
|     is_extension = True | ||||
|  | ||||
|     states = set(('active', 'composing', 'gone', 'inactive', 'paused')) | ||||
|  | ||||
|     def setup(self, xml=None): | ||||
|         self.xml = ET.Element('') | ||||
|         return True | ||||
|  | ||||
|     def get_chat_state(self): | ||||
|         parent = self.parent() | ||||
|         for state in self.states: | ||||
|             state_xml = parent.find('{%s}%s' % (self.namespace, state)) | ||||
|             if state_xml is not None: | ||||
|                 self.xml = state_xml | ||||
|                 return state | ||||
|         return '' | ||||
|  | ||||
|     def set_chat_state(self, state): | ||||
|         self.del_chat_state() | ||||
|         parent = self.parent() | ||||
|         if state in self.states: | ||||
|             self.xml = ET.Element('{%s}%s' % (self.namespace, state)) | ||||
|             parent.append(self.xml) | ||||
|         elif state not in [None, '']: | ||||
|             raise ValueError('Invalid chat state') | ||||
|  | ||||
|     def del_chat_state(self): | ||||
|         parent = self.parent() | ||||
|         for state in self.states: | ||||
|             state_xml = parent.find('{%s}%s' % (self.namespace, state)) | ||||
|             if state_xml is not None: | ||||
|                 self.xml = ET.Element('') | ||||
|                 parent.xml.remove(state_xml) | ||||
| @@ -42,7 +42,7 @@ class xep_0092(base_plugin): | ||||
|  | ||||
|         self.xmpp.register_handler( | ||||
|                 Callback('Software Version', | ||||
|                          StanzaPath('iq/software_version'), | ||||
|                          StanzaPath('iq@=get/software_version'), | ||||
|                          self._handle_version)) | ||||
|  | ||||
|         register_stanza_plugin(Iq, Version) | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class xep_0199(base_plugin): | ||||
|         self.xep = '0199' | ||||
|         self.stanza = stanza | ||||
|  | ||||
|         self.keepalive = self.config.get('keepalive', True) | ||||
|         self.keepalive = self.config.get('keepalive', False) | ||||
|         self.frequency = float(self.config.get('frequency', 300)) | ||||
|         self.timeout = self.config.get('timeout', 30) | ||||
|  | ||||
| @@ -90,7 +90,7 @@ class xep_0199(base_plugin): | ||||
|             """Send ping request to the server.""" | ||||
|             log.debug("Pinging...") | ||||
|             resp = self.send_ping(self.xmpp.boundjid.host, self.timeout) | ||||
|             if not resp: | ||||
|             if resp is None or resp is False: | ||||
|                 log.debug("Did not recieve ping back in time." + \ | ||||
|                           "Requesting Reconnect.") | ||||
|                 self.xmpp.reconnect() | ||||
| @@ -160,4 +160,4 @@ class xep_0199(base_plugin): | ||||
|  | ||||
|  | ||||
| # Backwards compatibility for names | ||||
| Ping.sendPing = Ping.send_ping | ||||
| xep_0199.sendPing = xep_0199.send_ping | ||||
|   | ||||
| @@ -10,6 +10,7 @@ from __future__ import with_statement, unicode_literals | ||||
|  | ||||
| import copy | ||||
| import logging | ||||
| import signal | ||||
| import socket as Socket | ||||
| import ssl | ||||
| import sys | ||||
| @@ -195,6 +196,53 @@ class XMLStream(object): | ||||
|         self.auto_reconnect = True | ||||
|         self.is_client = False | ||||
|  | ||||
|     def use_signals(self, signals=None): | ||||
|         """ | ||||
|         Register signal handlers for SIGHUP and SIGTERM, if possible, | ||||
|         which will raise a "killed" event when the application is | ||||
|         terminated. | ||||
|  | ||||
|         If a signal handler already existed, it will be executed first, | ||||
|         before the "killed" event is raised. | ||||
|  | ||||
|         Arguments: | ||||
|             signals -- A list of signal names to be monitored. | ||||
|                        Defaults to ['SIGHUP', 'SIGTERM']. | ||||
|         """ | ||||
|         if signals is None: | ||||
|             signals = ['SIGHUP', 'SIGTERM'] | ||||
|  | ||||
|         existing_handlers = {} | ||||
|         for sig_name in signals: | ||||
|             if hasattr(signal, sig_name): | ||||
|                 sig = getattr(signal, sig_name) | ||||
|                 handler = signal.getsignal(sig) | ||||
|                 if handler: | ||||
|                     existing_handlers[sig] = handler | ||||
|  | ||||
|         def handle_kill(signum, frame): | ||||
|             """ | ||||
|             Capture kill event and disconnect cleanly after first | ||||
|             spawning the "killed" event. | ||||
|             """ | ||||
|  | ||||
|             if signum in existing_handlers and \ | ||||
|                    existing_handlers[signum] != handle_kill: | ||||
|                 existing_handlers[signum](signum, frame) | ||||
|  | ||||
|             self.event("killed", direct=True) | ||||
|             self.disconnect() | ||||
|  | ||||
|         try: | ||||
|             for sig_name in signals: | ||||
|                 if hasattr(signal, sig_name): | ||||
|                     sig = getattr(signal, sig_name) | ||||
|                     signal.signal(sig, handle_kill) | ||||
|             self.__signals_installed = True | ||||
|         except: | ||||
|             log.debug("Can not set interrupt signal handlers. " + \ | ||||
|                       "SleekXMPP is not running from a main thread.") | ||||
|  | ||||
|     def new_id(self): | ||||
|         """ | ||||
|         Generate and return a new stream ID in hexadecimal form. | ||||
| @@ -305,8 +353,7 @@ class XMLStream(object): | ||||
|         self.send_raw(self.stream_footer) | ||||
|         # Wait for confirmation that the stream was | ||||
|         # closed in the other direction. | ||||
|         if not reconnect: | ||||
|             self.auto_reconnect = False | ||||
|         self.auto_reconnect = reconnect | ||||
|         self.stream_end_event.wait(4) | ||||
|         if not self.auto_reconnect: | ||||
|             self.stop.set() | ||||
| @@ -731,6 +778,7 @@ class XMLStream(object): | ||||
|             if not self.stop.isSet() and self.auto_reconnect: | ||||
|                 self.reconnect() | ||||
|             else: | ||||
|                 self.event('killed', direct=True) | ||||
|                 self.disconnect() | ||||
|                 self.event_queue.put(('quit', None, None)) | ||||
|         self.scheduler.run = False | ||||
| @@ -909,6 +957,7 @@ class XMLStream(object): | ||||
|                     return False | ||||
|         except KeyboardInterrupt: | ||||
|             log.debug("Keyboard Escape Detected in _event_runner") | ||||
|             self.event('killed', direct=True) | ||||
|             self.disconnect() | ||||
|             return | ||||
|         except SystemExit: | ||||
| @@ -934,6 +983,7 @@ class XMLStream(object): | ||||
|                     self.disconnect(self.auto_reconnect) | ||||
|         except KeyboardInterrupt: | ||||
|             log.debug("Keyboard Escape Detected in _send_thread") | ||||
|             self.event('killed', direct=True) | ||||
|             self.disconnect() | ||||
|             return | ||||
|         except SystemExit: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Lance Stout
					Lance Stout