Commit 8a23706b authored by Maxime Buquet's avatar Maxime Buquet

Merge branch 'invite_impromptu' into 'master'

Invite impromptu

See merge request !2
parents c3e9130d 9b25abf9
...@@ -218,6 +218,13 @@ These commands work in *any* tab. ...@@ -218,6 +218,13 @@ These commands work in *any* tab.
/invitations /invitations
Show the pending invitations. Show the pending invitations.
/impromptu
**Usage:** ``/impromptu <jid> [jid ..]``
Invite specified JIDs into a newly created room.
.. versionadded:: 0.13
/activity /activity
**Usage:** ``/activity [<general> [specific] [comment]]`` **Usage:** ``/activity [<general> [specific] [comment]]``
...@@ -472,6 +479,14 @@ Normal Conversation tab commands ...@@ -472,6 +479,14 @@ Normal Conversation tab commands
Get the software version of the current interlocutor (usually its Get the software version of the current interlocutor (usually its
XMPP client and Operating System). XMPP client and Operating System).
/invite
**Usage:** ``/invite <jid> [jid ..]``
Invite specified JIDs, with this contact, into a newly
created room.
.. versionadded:: 0.13
.. _rostertab-commands: .. _rostertab-commands:
Contact list tab commands Contact list tab commands
......
...@@ -81,6 +81,15 @@ and certificate validation. ...@@ -81,6 +81,15 @@ and certificate validation.
you know what you are doing, see the :ref:`ciphers` dedicated section you know what you are doing, see the :ref:`ciphers` dedicated section
for more details. for more details.
default_muc_service
**Default value:** ``[empty]``
If specified, will be used instead of the MUC service provided by
the user domain.
.. versionadded:: 0.13
force_encryption force_encryption
**Default value:** ``true`` **Default value:** ``true``
......
...@@ -49,6 +49,7 @@ DEFAULT_CONFIG = { ...@@ -49,6 +49,7 @@ DEFAULT_CONFIG = {
'custom_host': '', 'custom_host': '',
'custom_port': '', 'custom_port': '',
'default_nick': '', 'default_nick': '',
'default_muc_service': '',
'deterministic_nick_colors': True, 'deterministic_nick_colors': True,
'device_id': '', 'device_id': '',
'nick_color_aliases': True, 'nick_color_aliases': True,
......
...@@ -6,6 +6,7 @@ import logging ...@@ -6,6 +6,7 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
import asyncio
from xml.etree import cElementTree as ET from xml.etree import cElementTree as ET
from slixmpp.exceptions import XMPPError from slixmpp.exceptions import XMPPError
...@@ -763,6 +764,24 @@ class CommandCore: ...@@ -763,6 +764,24 @@ class CommandCore:
self.core.invite(to.full, room, reason=reason) self.core.invite(to.full, room, reason=reason)
self.core.information('Invited %s to %s' % (to.bare, room), 'Info') self.core.information('Invited %s to %s' % (to.bare, room), 'Info')
@command_args_parser.quoted(1, 0)
def impromptu(self, args: str) -> None:
"""/impromptu <jid> [<jid> ...]"""
if args is None:
return self.help('impromptu')
jids = set()
current_tab = self.core.tabs.current_tab
if isinstance(current_tab, tabs.ConversationTab):
jids.add(current_tab.general_jid)
for jid in common.shell_split(' '.join(args)):
jids.add(safeJID(jid).bare)
asyncio.ensure_future(self.core.impromptu(jids))
self.core.information('Invited %s to a random room' % (' '.join(jids)), 'Info')
@command_args_parser.quoted(1, 1, ['']) @command_args_parser.quoted(1, 1, [''])
def decline(self, args): def decline(self, args):
"""/decline <room@server.tld> [reason]""" """/decline <room@server.tld> [reason]"""
......
...@@ -289,6 +289,19 @@ class CompletionCore: ...@@ -289,6 +289,19 @@ class CompletionCore:
return Completion( return Completion(
the_input.new_completion, rooms, n, '', quotify=True) the_input.new_completion, rooms, n, '', quotify=True)
def impromptu(self, the_input):
"""Completion for /impromptu"""
n = the_input.get_argument_position(quoted=True)
onlines = []
offlines = []
for barejid in roster.jids():
if len(roster[barejid]):
onlines.append(barejid)
else:
offlines.append(barejid)
comp = sorted(onlines) + sorted(offlines)
return Completion(the_input.new_completion, comp, n, quotify=True)
def activity(self, the_input): def activity(self, the_input):
"""Completion for /activity""" """Completion for /activity"""
n = the_input.get_argument_position(quoted=True) n = the_input.get_argument_position(quoted=True)
......
...@@ -13,12 +13,16 @@ import pipes ...@@ -13,12 +13,16 @@ import pipes
import sys import sys
import shutil import shutil
import time import time
import uuid
from collections import defaultdict from collections import defaultdict
from typing import Callable, Dict, List, Optional, Tuple, Type from typing import Callable, Dict, List, Optional, Set, Tuple, Type
from xml.etree import cElementTree as ET
from functools import partial
from slixmpp import JID from slixmpp import JID
from slixmpp.util import FileSystemPerJidCache from slixmpp.util import FileSystemPerJidCache
from slixmpp.xmlstream.handler import Callback from slixmpp.xmlstream.handler import Callback
from slixmpp.exceptions import IqError, IqTimeout
from poezio import connection from poezio import connection
from poezio import decorators from poezio import decorators
...@@ -868,6 +872,85 @@ class Core: ...@@ -868,6 +872,85 @@ class Core:
self.xmpp.plugin['xep_0030'].get_info( self.xmpp.plugin['xep_0030'].get_info(
jid=jid, timeout=5, callback=callback) jid=jid, timeout=5, callback=callback)
def _impromptu_room_form(self, room):
fields = [
('hidden', 'FORM_TYPE', 'http://jabber.org/protocol/muc#roomconfig'),
('boolean', 'muc#roomconfig_changesubject', True),
('boolean', 'muc#roomconfig_allowinvites', True),
('boolean', 'muc#roomconfig_persistent', True),
('boolean', 'muc#roomconfig_membersonly', True),
('boolean', 'muc#roomconfig_publicroom', False),
('list-single', 'muc#roomconfig_whois', 'anyone'),
# MAM
('boolean', 'muc#roomconfig_enablearchiving', True), # Prosody
('boolean', 'mam', True), # Ejabberd community
('boolean', 'muc#roomconfig_mam', True), # Ejabberd saas
]
form = self.xmpp['xep_0004'].make_form()
form['type'] = 'submit'
for field in fields:
form.add_field(
ftype=field[0],
var=field[1],
value=field[2],
)
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['to'] = room
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
query.append(form.xml)
iq.append(query)
return iq
async def impromptu(self, jids: Set[JID]) -> None:
"""
Generates a new "Impromptu" room with a random localpart on the muc
component of the user who initiated the request. One the room is
created and the first user has joined, send invites for specified
contacts to join in.
"""
results = await self.xmpp['xep_0030'].get_info_from_domain()
muc_from_identity = ''
for info in results:
for identity in info['disco_info']['identities']:
if identity[0] == 'conference' and identity[1] == 'text':
muc_from_identity = info['from'].bare
# Use config.default_muc_service as muc component if available,
# otherwise find muc component by disco#items-ing the user domain.
# If not, give up
default_muc = config.get('default_muc_service', muc_from_identity)
if not default_muc:
self.information(
"Error finding a MUC service to join. If your server does not "
"provide one, set 'default_muc_service' manually to a MUC "
"service that allows room creation.",
'Error'
)
return
nick = self.own_nick
localpart = uuid.uuid4().hex
room = '{!s}@{!s}'.format(localpart, default_muc)
self.open_new_room(room, nick).join()
iq = self._impromptu_room_form(room)
try:
await iq.send()
except (IqError, IqTimeout):
self.information('Failed to configure impromptu room.', 'Info')
# TODO: destroy? leave room.
return None
self.information('Room %s created' % room, 'Info')
for jid in jids:
self.invite(jid, room)
def get_error_message(self, stanza, deprecated: bool = False): def get_error_message(self, stanza, deprecated: bool = False):
""" """
Takes a stanza of the form <message type='error'><error/></message> Takes a stanza of the form <message type='error'><error/></message>
...@@ -1788,6 +1871,13 @@ class Core: ...@@ -1788,6 +1871,13 @@ class Core:
desc='Invite jid in room with reason.', desc='Invite jid in room with reason.',
shortdesc='Invite someone in a room.', shortdesc='Invite someone in a room.',
completion=self.completion.invite) completion=self.completion.invite)
self.register_command(
'impromptu',
self.command.impromptu,
usage='<jid> [jid ...]',
desc='Invite specified JIDs into a newly created room.',
shortdesc='Invite specified JIDs into newly created room.',
completion=self.completion.impromptu)
self.register_command( self.register_command(
'invitations', 'invitations',
self.command.invitations, self.command.invitations,
......
...@@ -97,6 +97,11 @@ class HandlerCore: ...@@ -97,6 +97,11 @@ class HandlerCore:
self.core.xmpp.plugin['xep_0030'].get_info( self.core.xmpp.plugin['xep_0030'].get_info(
jid=self.core.xmpp.boundjid.domain, callback=callback) jid=self.core.xmpp.boundjid.domain, callback=callback)
def find_identities(self, _):
asyncio.ensure_future(
self.core.xmpp['xep_0030'].get_info_from_domain(),
)
def on_carbon_received(self, message): def on_carbon_received(self, message):
""" """
Carbon <received/> received Carbon <received/> received
......
...@@ -79,6 +79,12 @@ class ConversationTab(OneToOneTab): ...@@ -79,6 +79,12 @@ class ConversationTab(OneToOneTab):
' allow you to see his presence, and allow them to' ' allow you to see his presence, and allow them to'
' see your presence.', ' see your presence.',
shortdesc='Add a user to your roster.') shortdesc='Add a user to your roster.')
self.register_command(
'invite',
self.core.command.impromptu,
desc='Invite people into an impromptu room.',
shortdesc='Invite other users to the discussion',
completion=self.core.completion.impromptu)
self.update_commands() self.update_commands()
self.update_keys() self.update_keys()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment