Source code for aioxmpp.im.service

########################################################################
# File name: service.py
# This file is part of: aioxmpp
#
# LICENSE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#
########################################################################
import functools

import aioxmpp.callbacks
import aioxmpp.service


[docs]class ConversationService(aioxmpp.service.Service): """ Central place where all :class:`.im.conversation.AbstractConversation` subclass instances are collected. It provides discoverability of all existing conversations (in no particular order) and signals on addition and removal of active conversations. This is useful for front ends to track conversations globally without needing to know about the specific conversation providers. .. signal:: on_conversation_added(conversation) A new conversation has been added. :param conversation: The conversation which was added. :type conversation: :class:`~.im.conversation.AbstractConversation` This signal is fired when a new conversation is added by a :term:`Conversation Implementation`. .. note:: If you are looking for a "on_conversation_removed" event or similar, there is none. You should use the :meth:`.AbstractConversation.on_exit` event of the `conversation`. .. signal:: on_message(conversation, *args, **kwargs) Emits whenever any active conversation emits its :meth:`~.im.Conversation.on_message` event. The arguments are forwarded 1:1, with the :class:`~.im.AbstractConversation` instance pre-pended to the argument list. .. autoattribute:: conversations .. automethod:: get_conversation For :term:`Conversation Implementations <Conversation Implementation>`, the following methods are intended; they should not be used by applications. .. automethod:: _add_conversation """ on_conversation_added = aioxmpp.callbacks.Signal() on_message = aioxmpp.callbacks.Signal() def __init__(self, client, **kwargs): super().__init__(client, **kwargs) self._conversation_meta = {} self._conversation_map = {} @property def conversations(self): """ Return an iterable of conversations in which the local client is participating. """ return self._conversation_meta.keys() def _remove_conversation(self, conv): del self._conversation_map[conv.jid] tokens, = self._conversation_meta.pop(conv) for signal, token in tokens: signal.disconnect(token) def _handle_conversation_exit(self, conv, *args, **kwargs): self._remove_conversation(conv) return False
[docs] def _add_conversation(self, conversation): """ Add the conversation and fire the :meth:`on_conversation_added` event. :param conversation: The conversation object to add. :type conversation: :class:`~.AbstractConversation` The conversation is added to the internal list of conversations which can be queried at :attr:`conversations`. The :meth:`on_conversation_added` event is fired. In addition, the :class:`ConversationService` subscribes to the :meth:`~.AbstractConversation.on_exit` event to remove the conversation from the list automatically. There is no need to remove a conversation from the list explicitly. """ handler = functools.partial( self._handle_conversation_exit, conversation ) tokens = [] def linked_token(signal, handler): return signal, signal.connect(handler) tokens.append(linked_token(conversation.on_exit, handler)) tokens.append(linked_token(conversation.on_failure, handler)) tokens.append(linked_token(conversation.on_message, functools.partial( self.on_message, conversation, ))) self._conversation_meta[conversation] = ( tokens, ) self._conversation_map[conversation.jid] = conversation self.on_conversation_added(conversation)
[docs] def get_conversation(self, conversation_address): """ Return the :class:`.im.AbstractConversation` for a given JID. :raises KeyError: if there is currently no matching conversation """ return self._conversation_map[conversation_address]