v1.3.5
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
########################################################################
|
||||
# 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
|
||||
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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]
|
||||
Reference in New Issue
Block a user