forked from platypush/platypush
732 lines
25 KiB
Python
732 lines
25 KiB
Python
import os
|
|
from typing import Iterable, Optional, Type, Union
|
|
from typing_extensions import override
|
|
|
|
import aioxmpp
|
|
import aioxmpp.im
|
|
|
|
from platypush.config import Config
|
|
from platypush.message.event.xmpp import XmppConnectedEvent
|
|
from platypush.plugins import AsyncRunnablePlugin, action
|
|
|
|
from ._base import XmppBasePlugin
|
|
from ._config import XmppConfig
|
|
from ._handlers import (
|
|
XmppBaseHandler,
|
|
XmppConnectionHandler,
|
|
XmppConversationHandler,
|
|
XmppHandlersRegistry,
|
|
XmppPresenceHandler,
|
|
XmppRoomHandler,
|
|
XmppRosterHandler,
|
|
discover_handlers,
|
|
)
|
|
from ._mixins import XmppBaseMixin
|
|
from ._state import SerializedState, StateSerializer
|
|
from ._types import Errors, XmppPresence
|
|
|
|
|
|
# pylint: disable=too-many-ancestors
|
|
class XmppPlugin(AsyncRunnablePlugin, XmppBasePlugin):
|
|
"""
|
|
XMPP integration.
|
|
|
|
Requires:
|
|
|
|
* **aioxmpp** (``pip install aioxmpp``)
|
|
* **pytz** (``pip install pytz``)
|
|
|
|
Triggers:
|
|
|
|
* :class:`platypush.message.event.xmpp.XmppConnectedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppContactAddRequestAcceptedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppContactAddRequestEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppContactAddRequestRejectedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationAddedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationEnterEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationExitEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationJoinEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationLeaveEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppConversationNickChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppDisconnectedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppMessageReceivedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppPresenceChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomAffiliationChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomEnterEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomExitEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomInviteAcceptedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomInviteEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomInviteRejectedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomJoinEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomLeaveEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomMessageReceivedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomNickChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomPresenceChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomRoleChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomTopicChangedEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomUserAvailableEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppRoomUserUnavailableEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppUserAvailableEvent`
|
|
* :class:`platypush.message.event.xmpp.XmppUserUnavailableEvent`
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
user_id: str,
|
|
password: Optional[str] = None,
|
|
language: Optional[str] = None,
|
|
anonymous: bool = False,
|
|
auto_accept_invites: bool = True,
|
|
restore_state: bool = True,
|
|
state_file: Optional[str] = None,
|
|
**kwargs,
|
|
):
|
|
"""
|
|
:param user_id: Jabber/user ID, in the format ``user@example.org``.
|
|
:param password: User password.
|
|
:param language: ISO string for the language code that will be used by
|
|
the bot (default: ``None``).
|
|
:param anonymous: Whether to use anonymous authentication (default:
|
|
``False``).
|
|
:param auto_accept_invites: Whether to automatically accept invites to
|
|
conversations (default: True). If set to False, and you still want
|
|
some control on which invites should be accepted, you can create a
|
|
``hook`` on
|
|
:class:`platypush.message.event.xmpp.XmppRoomInviteEvent` that
|
|
calls either :meth:`.accept_invite` or :meth:`.reject_invite` with
|
|
the ``room_id`` specified on the event, if it is a room event, or
|
|
subscribe to
|
|
:class:`platypush.message.event.xmpp.XmppContactAddRequestEvent`
|
|
and call either :meth:`.accept_invite` or :meth:`.reject_invite`
|
|
with the ``user_id`` specified on the event, if it is a contact add
|
|
request.
|
|
:param restore_state: If ``True`` (default) then any previously joined
|
|
conversation or subscribed contact will be joined/subscribed again
|
|
when the plugin restarts. Otherwise, upon restart the plugin will
|
|
start from a state with no subscriptions nor joined rooms.
|
|
:param state_file: Path where the previous state will be stored, if
|
|
``restore_state`` is ``True``. Default:
|
|
``<WORKDIR>/xmpp/state.json``.
|
|
"""
|
|
super(XmppBasePlugin, self).__init__(user_id=user_id, language=language)
|
|
super(AsyncRunnablePlugin, self).__init__(**kwargs)
|
|
|
|
self._security = aioxmpp.make_security_layer(password, anonymous=anonymous)
|
|
self._config = XmppConfig(
|
|
auto_accept_invites=auto_accept_invites,
|
|
restore_state=restore_state,
|
|
state_file=os.path.expanduser(
|
|
state_file or os.path.join(Config.get_workdir(), 'xmpp', 'state.json')
|
|
),
|
|
)
|
|
self._loaded_state = SerializedState()
|
|
self._state_serializer = StateSerializer(user_id=self._jid, config=self._config)
|
|
self._handlers = XmppHandlersRegistry(self)
|
|
self.restore_state()
|
|
|
|
def restore_state(self):
|
|
"""
|
|
Reload the previous state from the configured state file.
|
|
"""
|
|
if not (self._config.state_file and self._config.restore_state):
|
|
return
|
|
|
|
self._loaded_state = self._state_serializer.load()
|
|
|
|
@property
|
|
def _conn_handler(self) -> XmppConnectionHandler:
|
|
return self._handlers[XmppConnectionHandler]
|
|
|
|
@property
|
|
def _conv_handler(self) -> XmppConversationHandler:
|
|
return self._handlers[XmppConversationHandler]
|
|
|
|
@property
|
|
def _presence_handler(self) -> XmppPresenceHandler:
|
|
return self._handlers[XmppPresenceHandler]
|
|
|
|
@property
|
|
def _room_handler(self) -> XmppRoomHandler:
|
|
return self._handlers[XmppRoomHandler]
|
|
|
|
@property
|
|
def _roster_handler(self) -> XmppRosterHandler:
|
|
return self._handlers[XmppRosterHandler]
|
|
|
|
def _on_disconnect(self, reason: Optional[Union[str, Exception]] = None):
|
|
self._conn_handler.disconnect(reason)
|
|
|
|
def _register_handlers(self):
|
|
for hndl_type in discover_handlers():
|
|
hndl = self.register_xmpp_handler(hndl_type)
|
|
hndl.restore_state()
|
|
|
|
def register_xmpp_handler(self, hndl_type: Type[XmppBaseMixin]) -> XmppBaseHandler:
|
|
self.logger.debug('Registering handler: %s', hndl_type)
|
|
self._handlers[hndl_type] = hndl_type(
|
|
user_id=self._jid,
|
|
language=self._lang,
|
|
config=self._config,
|
|
state=self._state,
|
|
client=self._client,
|
|
loop=self._loop,
|
|
state_serializer=self._state_serializer,
|
|
loaded_state=self._loaded_state,
|
|
)
|
|
|
|
return self._handlers[hndl_type]
|
|
|
|
@override
|
|
def should_stop(self) -> bool:
|
|
return super().should_stop() or self._state.should_stop.is_set()
|
|
|
|
@override
|
|
def stop(self):
|
|
self._state.should_stop.set()
|
|
self._stop_state_serializer()
|
|
self._stop_client()
|
|
self._on_disconnect(reason='Plugin terminated')
|
|
super().stop()
|
|
|
|
def _stop_state_serializer(self):
|
|
if self._state_serializer:
|
|
self._state_serializer.flush()
|
|
self._state_serializer.wait(self._state_serializer.flush_timeout)
|
|
|
|
def _stop_client(self):
|
|
if self._client:
|
|
self._client.stop()
|
|
self._client = None
|
|
|
|
@override
|
|
async def listen(self):
|
|
self._client = aioxmpp.PresenceManagedClient(self._jid, self._security)
|
|
|
|
try:
|
|
async with self._client.connected():
|
|
self._register_handlers()
|
|
self._post_event(XmppConnectedEvent)
|
|
await self._state.should_stop.wait()
|
|
except Exception as e:
|
|
self.logger.warning('XMPP connection error: %s', e)
|
|
self.logger.exception(e)
|
|
self._on_disconnect(e)
|
|
raise e
|
|
|
|
@action
|
|
def send_message(
|
|
self,
|
|
body: str,
|
|
user_id: Optional[str] = None,
|
|
room_id: Optional[str] = None,
|
|
language: Optional[str] = None,
|
|
):
|
|
"""
|
|
Send a message to a target (the Jabber ID of another user or room).
|
|
|
|
:param body: Message body.
|
|
:param user_id: Jabber ID of the target user. Either user_id or room_id
|
|
should be specified.
|
|
:param room_id: Jabber ID of the target room. Either user_id or room_id
|
|
should be specified.
|
|
:param language: Override the default language code.
|
|
"""
|
|
if room_id:
|
|
self._room_handler.send_message(
|
|
room_id=room_id, body=body, language=language
|
|
)
|
|
elif user_id:
|
|
self._conv_handler.send_message(
|
|
user_id=user_id, body=body, language=language
|
|
)
|
|
else:
|
|
raise AssertionError(Errors.USER_ID_OR_ROOM_ID)
|
|
|
|
@action
|
|
def join(
|
|
self,
|
|
room_id: str,
|
|
nick: Optional[str] = None,
|
|
password: Optional[str] = None,
|
|
auto_rejoin: bool = True,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Join a room/conversation.
|
|
|
|
:param room_id: The Jabber ID of the conversation to join.
|
|
:param nick: The nickname that the bot should use in the room (default:
|
|
the nickname specified in the configuration's ``user_id``
|
|
parameter).
|
|
:param password: The password of the room (default: None).
|
|
:param auto_rejoin: Whether to automatically rejoin the room after
|
|
disconnection/kick (default: True).
|
|
:param timeout: Room join timeout (default: 20 seconds). Set to null
|
|
for no timeout.
|
|
"""
|
|
nick = nick or self._jid.localpart
|
|
self._async_run(
|
|
self._room_handler.join,
|
|
room_id,
|
|
timeout=timeout,
|
|
nick=nick,
|
|
password=password,
|
|
auto_rejoin=auto_rejoin,
|
|
)
|
|
|
|
@action
|
|
def leave(
|
|
self, room_id: str, timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT
|
|
):
|
|
"""
|
|
Leave a room/conversation.
|
|
|
|
:param room_id: The Jabber ID of the conversation to leave.
|
|
:param timeout: Room leave timeout (default: 20 seconds). Set to null
|
|
for no timeout.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.leave,
|
|
room_id,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def accept_invite(
|
|
self, room_id: Optional[str] = None, user_id: Optional[str] = None
|
|
):
|
|
"""
|
|
Accept a pending invite to a multi-user conversation or a contact add
|
|
request.
|
|
|
|
:param user_id: The target ``user_id`` if this is a contact add request.
|
|
:param room_id: The target ``room_id`` if this is a room invite request.
|
|
"""
|
|
if room_id:
|
|
self._room_handler.accept_invite(room_id)
|
|
elif user_id:
|
|
self._roster_handler.accept_invite(user_id)
|
|
else:
|
|
raise AssertionError(Errors.USER_ID_OR_ROOM_ID)
|
|
|
|
@action
|
|
def reject_invite(
|
|
self, room_id: Optional[str] = None, user_id: Optional[str] = None
|
|
):
|
|
"""
|
|
Reject a pending invite to a multi-user conversation or a contact add
|
|
request.
|
|
|
|
:param user_id: The target ``user_id`` if this is a contact add request.
|
|
:param room_id: The target ``room_id`` if this is a room invite request.
|
|
"""
|
|
if room_id:
|
|
self._room_handler.reject_invite(room_id)
|
|
elif user_id:
|
|
self._roster_handler.reject_invite(user_id)
|
|
else:
|
|
raise AssertionError(Errors.USER_ID_OR_ROOM_ID)
|
|
|
|
@action
|
|
def invite(
|
|
self,
|
|
room_id: str,
|
|
user_id: str,
|
|
mode: str = 'direct',
|
|
text: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Invite a user to a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param user_id: The JID of the user to invite.
|
|
:param timeout: Invite request send timeout (default: 20 seconds). Set
|
|
to null for no timeout.
|
|
:param mode: Invite mode - can be ``direct`` (default) or ``mediated``.
|
|
|
|
- ``direct``: The invitation is sent directly to the invitee,
|
|
without going through a service specific to the conversation.
|
|
|
|
- ``mediated``: The invitation is sent indirectly through a service
|
|
which is providing the conversation. Advantages of using this mode
|
|
include most notably that the service can automatically add the
|
|
invitee to the list of allowed participants in configurations
|
|
where such restrictions exist (or deny the request if the inviter
|
|
does not have the permissions to do so).
|
|
:param text: Optional text to send with the invitation.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.invite,
|
|
room_id=room_id,
|
|
user_id=aioxmpp.JID.fromstr(user_id),
|
|
mode=getattr(
|
|
aioxmpp.im.InviteMode, mode.upper(), aioxmpp.im.InviteMode.DIRECT
|
|
),
|
|
text=text,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def set_presence(self, presence: Union[str, XmppPresence]):
|
|
"""
|
|
Set/broadcast a new presence state for the user.
|
|
|
|
:param presence: The new presence state. Possible values are:
|
|
|
|
- ``available``
|
|
- ``offline``
|
|
- ``away``
|
|
- ``xa``
|
|
- ``chat``
|
|
- ``dnd``
|
|
|
|
"""
|
|
pres = XmppPresence(presence.lower()) if isinstance(presence, str) else presence
|
|
self._presence_handler.set_presence(pres)
|
|
|
|
@action
|
|
def set_affiliation(
|
|
self,
|
|
room_id: str,
|
|
user_id: str,
|
|
affiliation: str,
|
|
reason: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Change the affiliation of a user to a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param user_id: The user JID.
|
|
:param affiliation: The affiliation to set. Possible values are:
|
|
|
|
- ``owner``
|
|
- ``member``
|
|
- ``none``
|
|
- ``outcast``
|
|
- ``publisher``
|
|
- ``publish-only``
|
|
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
:param reason: Optional reason for the change.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.set_affiliation,
|
|
room_id=room_id,
|
|
user_id=aioxmpp.JID.fromstr(user_id),
|
|
affiliation=affiliation,
|
|
reason=reason,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def set_role(
|
|
self,
|
|
room_id: str,
|
|
user_id: str,
|
|
role: str,
|
|
reason: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Change the role of a user in a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param user_id: The user JID.
|
|
:param role: The role to set. Possible values are:
|
|
|
|
- ``none``
|
|
- ``participant``
|
|
- ``visitor``
|
|
- ``moderator``
|
|
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
:param reason: Optional reason for the change.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.set_role,
|
|
room_id=room_id,
|
|
user_id=aioxmpp.JID.fromstr(user_id),
|
|
role=role,
|
|
reason=reason,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def kick(
|
|
self,
|
|
room_id: str,
|
|
user_id: str,
|
|
reason: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Kick a user from a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param user_id: The JID of the user to kick.
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
:param reason: Kick reason.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.kick,
|
|
room_id=room_id,
|
|
user_id=aioxmpp.JID.fromstr(user_id),
|
|
reason=reason,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def ban(
|
|
self,
|
|
room_id: str,
|
|
user_id: str,
|
|
reason: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Ban a user from a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param user_id: The JID of the user to ban.
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
:param reason: Ban reason.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.ban,
|
|
room_id=room_id,
|
|
user_id=aioxmpp.JID.fromstr(user_id),
|
|
reason=reason,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def set_topic(
|
|
self,
|
|
room_id: str,
|
|
topic: str,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Set the topic of a room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param topic: New topic.
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.set_topic,
|
|
room_id=room_id,
|
|
topic=topic,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def set_room_configuration(
|
|
self,
|
|
room_id: str,
|
|
name: Optional[bool] = None,
|
|
description: Optional[bool] = None,
|
|
members_only: Optional[bool] = None,
|
|
persistent: Optional[bool] = None,
|
|
moderated: Optional[bool] = None,
|
|
allow_invites: Optional[bool] = None,
|
|
allow_private_messages: Optional[bool] = None,
|
|
allow_change_subject: Optional[bool] = None,
|
|
enable_logging: Optional[bool] = None,
|
|
max_history_fetch: Optional[int] = None,
|
|
max_users: Optional[int] = None,
|
|
password_protected: Optional[bool] = None,
|
|
public: Optional[bool] = None,
|
|
room_admins: Optional[Iterable[str]] = None,
|
|
room_owners: Optional[Iterable[str]] = None,
|
|
password: Optional[str] = None,
|
|
language: Optional[str] = None,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Changes the configuration of a room.
|
|
|
|
All the parameters are optional, and only those that have a non-null
|
|
value will be set.
|
|
|
|
:param room_id: The target room JID.
|
|
:param name: New room name.
|
|
:param description: New room description.
|
|
:param members_only: Whether or not this room is only for members.
|
|
:param persistent: Whether or not this room is persistent.
|
|
:param moderated: Whether or not this room is moderated.
|
|
:param allow_invites: Whether or not this room allows invites.
|
|
:param allow_private_messages: Whether or not this room allows private
|
|
messages.
|
|
:param allow_change_subject: Whether or not this room allows changing
|
|
its subject.
|
|
:param enable_logging: Whether or not this room has logging enabled.
|
|
:param max_history_fetch: Maximum number of past messages to fetch when
|
|
joining the room.
|
|
:param max_users: Maximum number of users allowed in the room.
|
|
:param password_protected: Whether or not this room is password protected.
|
|
:param public: Whether or not this room is publicly visible.
|
|
:param room_admins: List of room admins, by Jabber ID.
|
|
:param room_owners: List of room owners, by Jabber ID.
|
|
:param password: If the room is password protected, configure its
|
|
password here.
|
|
:param language: Language of the room (ISO 2-letter code).
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.set_room_config,
|
|
room_id=room_id,
|
|
name=name,
|
|
description=description,
|
|
members_only=members_only,
|
|
persistent=persistent,
|
|
moderated=moderated,
|
|
allow_invites=allow_invites,
|
|
allow_private_messages=allow_private_messages,
|
|
allow_change_subject=allow_change_subject,
|
|
enable_logging=enable_logging,
|
|
max_history_fetch=max_history_fetch,
|
|
max_users=max_users,
|
|
password_protected=password_protected,
|
|
public=public,
|
|
room_admins=room_admins,
|
|
room_owners=room_owners,
|
|
password=password,
|
|
language=language,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def set_nick(
|
|
self,
|
|
room_id: str,
|
|
nick: str,
|
|
timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT,
|
|
):
|
|
"""
|
|
Set the nick of the user on a specific room.
|
|
|
|
:param room_id: The target room JID.
|
|
:param nick: New nick.
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.set_nick,
|
|
room_id=room_id,
|
|
nick=nick,
|
|
timeout=timeout,
|
|
)
|
|
|
|
@action
|
|
def add_user(self, user_id: str):
|
|
"""
|
|
Add the specified user ID to the roster.
|
|
|
|
:param user_id: The Jabber ID of the user to add.
|
|
"""
|
|
self._roster_handler.add_user(user_id)
|
|
|
|
@action
|
|
def remove_user(self, user_id: str):
|
|
"""
|
|
Remove the specified user ID from the roster.
|
|
|
|
:param user_id: The Jabber ID of the user to remove.
|
|
"""
|
|
self._roster_handler.remove_user(user_id)
|
|
|
|
@action
|
|
def request_voice(
|
|
self, room_id: str, timeout: Optional[float] = XmppBaseMixin.DEFAULT_TIMEOUT
|
|
):
|
|
"""
|
|
Request voice (i.e. participant role) in a room.
|
|
|
|
:param room_id: The Jabber ID of the room.
|
|
:param timeout: Request timeout (default: 20 seconds). Set to null for
|
|
no timeout.
|
|
"""
|
|
self._async_run(
|
|
self._room_handler.request_voice, room_id=room_id, timeout=timeout
|
|
)
|
|
|
|
@action
|
|
def status(self):
|
|
"""
|
|
Get the current status of the client.
|
|
|
|
:return:
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
# List of pending room invites, as Jabber IDs
|
|
"room_invites": ["bar@conference.xmpp.example.org"],
|
|
|
|
# List of pending user invites, as Jabber IDs
|
|
"user_invites": ["ignore-me@example.org"],
|
|
|
|
# List of users the client is subscribed to
|
|
"users": [
|
|
"buddy@example.org"
|
|
],
|
|
|
|
# Map of rooms the client has joined, indexed by room ID
|
|
"rooms": {
|
|
"tests@conference.xmpp.manganiello.tech": {
|
|
"room_id": "foo@conference.xmpp.example.org",
|
|
"joined": true,
|
|
# Possible values:
|
|
# ACTIVE, DISCONNECTED, HISTORY, JOIN_PRESENCE
|
|
"state": "ACTIVE",
|
|
"nick": "me",
|
|
|
|
# Map of room members, indexed by user ID
|
|
"members": {
|
|
"me@example.org": {
|
|
"user_id": "me@example.org",
|
|
"nick": "me",
|
|
# Possible affiliation values:
|
|
# none, member, outcast, owner, publisher, publish-only
|
|
"affiliation": "none",
|
|
|
|
# Possible role values:
|
|
# none, participant, visitor, moderator
|
|
"role": "participant",
|
|
"is_self": true,
|
|
"available": true,
|
|
|
|
# Possible state values:
|
|
# available, offline, away, xa, chat, dnd
|
|
"state": "available"
|
|
},
|
|
|
|
"buddy@example.org": {
|
|
"user_id": "buddy@example.org",
|
|
"nick": "SomeBuddy",
|
|
"affiliation": "owner",
|
|
"role": "moderator",
|
|
"is_self": false,
|
|
"available": true,
|
|
"state": "away"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
"""
|
|
return self._state.asdict(return_passwords=False)
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|