Package rocks.xmpp.extensions.muc

Source Code of rocks.xmpp.extensions.muc.ChatRoom

/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package rocks.xmpp.extensions.muc;

import rocks.xmpp.core.Jid;
import rocks.xmpp.core.XmppException;
import rocks.xmpp.core.session.SessionStatusEvent;
import rocks.xmpp.core.session.SessionStatusListener;
import rocks.xmpp.core.session.XmppSession;
import rocks.xmpp.core.stanza.*;
import rocks.xmpp.core.stanza.model.AbstractMessage;
import rocks.xmpp.core.stanza.model.client.IQ;
import rocks.xmpp.core.stanza.model.client.Message;
import rocks.xmpp.core.stanza.model.client.Presence;
import rocks.xmpp.extensions.data.model.DataForm;
import rocks.xmpp.extensions.delay.model.DelayedDelivery;
import rocks.xmpp.extensions.disco.ServiceDiscoveryManager;
import rocks.xmpp.extensions.disco.model.info.Feature;
import rocks.xmpp.extensions.disco.model.info.Identity;
import rocks.xmpp.extensions.disco.model.info.InfoNode;
import rocks.xmpp.extensions.disco.model.items.Item;
import rocks.xmpp.extensions.disco.model.items.ItemNode;
import rocks.xmpp.extensions.muc.conference.model.DirectInvitation;
import rocks.xmpp.extensions.muc.model.*;
import rocks.xmpp.extensions.muc.model.admin.MucAdmin;
import rocks.xmpp.extensions.muc.model.owner.MucOwner;
import rocks.xmpp.extensions.muc.model.user.Decline;
import rocks.xmpp.extensions.muc.model.user.Invite;
import rocks.xmpp.extensions.muc.model.user.MucUser;
import rocks.xmpp.extensions.muc.model.user.Status;
import rocks.xmpp.extensions.register.model.Registration;

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Represents a chat room.
*
* @author Christian Schudt
*/
public final class ChatRoom {

    private static final Logger logger = Logger.getLogger(ChatRoom.class.getName());

    private final Set<InvitationDeclineListener> invitationDeclineListeners = new CopyOnWriteArraySet<>();

    private final Set<SubjectChangeListener> subjectChangeListeners = new CopyOnWriteArraySet<>();

    private final Set<OccupantListener> occupantListeners = new CopyOnWriteArraySet<>();

    private final Set<MessageListener> messageListeners = new CopyOnWriteArraySet<>();

    private final Map<String, Occupant> occupantMap = new HashMap<>();

    private final MessageListener messageListener;

    private final PresenceListener presenceListener;

    private final ServiceDiscoveryManager serviceDiscoveryManager;

    private final String name;

    private final Jid roomJid;

    private final XmppSession xmppSession;

    private volatile String nick;

    private volatile boolean entered;

    ChatRoom(String name, final Jid roomJid, XmppSession xmppSession) {
        this.name = name;
        this.roomJid = roomJid;
        this.xmppSession = xmppSession;
        this.serviceDiscoveryManager = xmppSession.getExtensionManager(ServiceDiscoveryManager.class);

        xmppSession.addSessionStatusListener(new SessionStatusListener() {
            @Override
            public void sessionStatusChanged(SessionStatusEvent e) {
                if (e.getStatus() == XmppSession.Status.CLOSED) {
                    invitationDeclineListeners.clear();
                    subjectChangeListeners.clear();
                    occupantListeners.clear();
                    messageListeners.clear();
                    occupantMap.clear();
                }
            }
        });

        messageListener = new MessageListener() {
            @Override
            public void handle(MessageEvent e) {
                if (e.isIncoming()) {
                    Message message = e.getMessage();
                    if (message.getFrom().asBareJid().equals(roomJid)) {
                        if (message.getType() == AbstractMessage.Type.GROUPCHAT) {
                            // This is a <message/> stanza from the room JID (or from the occupant JID of the entity that set the subject), with a <subject/> element but no <body/> element
                            if (message.getSubject() != null && message.getBody() == null) {
                                Date date;
                                DelayedDelivery delayedDelivery = message.getExtension(DelayedDelivery.class);
                                if (delayedDelivery != null) {
                                    date = delayedDelivery.getTimeStamp();
                                } else {
                                    date = new Date();
                                }
                                notifySubjectChangeListeners(new SubjectChangeEvent(ChatRoom.this, message.getSubject(), message.getFrom().getResource(), delayedDelivery != null, date));
                            } else {
                                notifyMessageListeners(new MessageEvent(ChatRoom.this, message, true));
                            }
                        } else {
                            MucUser mucUser = message.getExtension(MucUser.class);
                            if (mucUser != null) {
                                Decline decline = mucUser.getDecline();
                                if (decline != null) {
                                    notifyInvitationDeclineListeners(new InvitationDeclineEvent(ChatRoom.this, roomJid, decline.getFrom(), decline.getReason()));
                                }
                            }
                        }
                    }
                }
            }
        };

        presenceListener = new PresenceListener() {
            @Override
            public void handle(PresenceEvent e) {
                Presence presence = e.getPresence();
                // If the presence came from the room.
                if (presence.getFrom() != null && presence.getFrom().asBareJid().equals(roomJid)) {
                    if (e.isIncoming()) {
                        MucUser mucUser = presence.getExtension(MucUser.class);
                        if (mucUser != null) {
                            String nick = presence.getFrom().getResource();

                            if (nick != null) {
                                boolean isSelfPresence = isSelfPresence(presence);
                                if (presence.isAvailable()) {
                                    Occupant occupant = new Occupant(presence, isSelfPresence);
                                    Occupant previousOccupant = occupantMap.put(nick, occupant);
                                    // A new occupant entered the room.
                                    if (previousOccupant == null) {
                                        // Only notify about "joins", if it's not our own join and we are already in the room.
                                        if (!isSelfPresence && entered) {
                                            notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.ENTERED, null, null, null));
                                        }
                                    } else {
                                        notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.STATUS_CHANGED, null, null, null));
                                    }
                                } else if (presence.getType() == Presence.Type.UNAVAILABLE) {
                                    // Occupant has exited the room.
                                    Occupant occupant = occupantMap.remove(nick);
                                    if (occupant != null) {
                                        if (mucUser.getItem() != null) {
                                            Actor actor = mucUser.getItem().getActor();
                                            String reason = mucUser.getItem().getReason();
                                            if (!mucUser.getStatusCodes().isEmpty()) {
                                                if (mucUser.getStatusCodes().contains(Status.kicked())) {
                                                    notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.KICKED, actor, reason, null));
                                                } else if (mucUser.getStatusCodes().contains(Status.banned())) {
                                                    notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.BANNED, actor, reason, null));
                                                } else if (mucUser.getStatusCodes().contains(Status.membershipRevoked())) {
                                                    notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.MEMBERSHIP_REVOKED, actor, reason, null));
                                                } else if (mucUser.getStatusCodes().contains(Status.nicknameChanged())) {
                                                    notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.NICKNAME_CHANGED, actor, reason, null));
                                                } else if (mucUser.getStatusCodes().contains(Status.systemShutdown())) {
                                                    notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.SYSTEM_SHUTDOWN, actor, reason, null));
                                                }
                                            } else if (mucUser.getDestroy() != null) {
                                                notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.ROOM_DESTROYED, actor, mucUser.getDestroy().getReason(), mucUser.getDestroy().getJid()));
                                            } else {
                                                notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.EXITED, null, null, null));
                                            }
                                        } else {
                                            notifyOccupantListeners(new OccupantEvent(ChatRoom.this, occupant, OccupantEvent.Type.EXITED, null, null, null));
                                        }
                                    }
                                    if (isSelfPresence) {
                                        userHasExited();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };
    }

    private void userHasExited() {
        xmppSession.removeMessageListener(messageListener);
        xmppSession.removePresenceListener(presenceListener);
    }

    private void notifyInvitationDeclineListeners(InvitationDeclineEvent invitationDeclineEvent) {
        for (InvitationDeclineListener invitationDeclineListener : invitationDeclineListeners) {
            try {
                invitationDeclineListener.invitationDeclined(invitationDeclineEvent);
            } catch (Exception e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }

    private void notifyOccupantListeners(OccupantEvent occupantEvent) {
        for (OccupantListener occupantListener : occupantListeners) {
            try {
                occupantListener.occupantChanged(occupantEvent);
            } catch (Exception e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }

    private void notifySubjectChangeListeners(SubjectChangeEvent subjectChangeEvent) {
        for (SubjectChangeListener subjectChangeListener : subjectChangeListeners) {
            try {
                subjectChangeListener.subjectChanged(subjectChangeEvent);
            } catch (Exception e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }

    private void notifyMessageListeners(MessageEvent messageEvent) {
        for (MessageListener messageListener : messageListeners) {
            try {
                messageListener.handle(messageEvent);
            } catch (Exception e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }

    private boolean isSelfPresence(Presence presence) {
        boolean isSelfPresence = false;
        MucUser mucUser = presence.getExtension(MucUser.class);
        if (mucUser != null) {
            // If the presence is self-presence (110) or if the service assigned another nickname (210) to the user (but didn't include 110).
            isSelfPresence = mucUser.getStatusCodes().contains(Status.self()) || mucUser.getStatusCodes().contains(Status.serviceHasAssignedOrModifiedNick());
        }
        return isSelfPresence || nick != null && presence.getFrom() != null && nick.equals(presence.getFrom().getResource());
    }

    /**
     * Adds a invitation decline listener, which allows to listen for invitation declines.
     *
     * @param invitationDeclineListener The listener.
     * @see #removeInvitationDeclineListener(InvitationDeclineListener)
     */
    public void addInvitationDeclineListener(InvitationDeclineListener invitationDeclineListener) {
        invitationDeclineListeners.add(invitationDeclineListener);
    }

    /**
     * Removes a previously added invitation decline listener.
     *
     * @param invitationDeclineListener The listener.
     * @see #addInvitationDeclineListener(InvitationDeclineListener)
     */
    public void removeInvitationDeclineListener(InvitationDeclineListener invitationDeclineListener) {
        invitationDeclineListeners.remove(invitationDeclineListener);
    }

    /**
     * Adds a subject change listener, which allows to listen for subject changes.
     *
     * @param subjectChangeListener The listener.
     * @see #removeSubjectChangeListener(SubjectChangeListener)
     */
    public void addSubjectChangeListener(SubjectChangeListener subjectChangeListener) {
        subjectChangeListeners.add(subjectChangeListener);
    }

    /**
     * Removes a previously added subject change listener.
     *
     * @param subjectChangeListener The listener.
     * @see #addSubjectChangeListener(SubjectChangeListener)
     */
    public void removeSubjectChangeListener(SubjectChangeListener subjectChangeListener) {
        subjectChangeListeners.remove(subjectChangeListener);
    }

    /**
     * Adds a message listener, which allows to listen for incoming messages in this room.
     *
     * @param messageListener The listener.
     * @see #removeMessageListener(rocks.xmpp.core.stanza.MessageListener)
     */
    public void addMessageListener(MessageListener messageListener) {
        messageListeners.add(messageListener);
    }

    /**
     * Removes a previously added message listener.
     *
     * @param messageListener The listener.
     * @see #addMessageListener(rocks.xmpp.core.stanza.MessageListener)
     */
    public void removeMessageListener(MessageListener messageListener) {
        messageListeners.remove(messageListener);
    }

    /**
     * Adds an occupant listener, which allows to listen for presence changes of occupants, e.g. "joins" and "leaves".
     *
     * @param occupantListener The listener.
     * @see #removeOccupantListener(OccupantListener)
     */
    public void addOccupantListener(OccupantListener occupantListener) {
        occupantListeners.add(occupantListener);
    }

    /**
     * Removes a previously added occupant listener.
     *
     * @param occupantListener The listener.
     * @see #addOccupantListener(OccupantListener)
     */
    public void removeOccupantListener(OccupantListener occupantListener) {
        occupantListeners.remove(occupantListener);
    }

    /**
     * Enters the room.
     *
     * @param nick The nickname.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public void enter(String nick) throws XmppException {
        enter(nick, null, null);
    }

    /**
     * Enters the room with a password.
     *
     * @param nick     The nickname.
     * @param password The password.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public void enter(String nick, String password) throws XmppException {
        enter(nick, password, null);
    }

    /**
     * Enters the room and requests history messages.
     *
     * @param nick    The nickname.
     * @param history The history.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public void enter(String nick, History history) throws XmppException {
        enter(nick, null, history);
    }

    /**
     * Enters the room with a password and requests history messages.
     *
     * @param nick     The nickname.
     * @param password The password.
     * @param history  The history.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public synchronized void enter(final String nick, String password, History history) throws XmppException {
        if (nick == null) {
            throw new IllegalArgumentException("nick must not be null.");
        }

        if (entered) {
            throw new IllegalStateException("You already entered this room.");
        }

        try {
            xmppSession.addMessageListener(messageListener);
            xmppSession.addPresenceListener(presenceListener);

            final Presence enterPresence = new Presence();
            enterPresence.setTo(roomJid.withResource(nick));
            enterPresence.getExtensions().add(new Muc(password, history));
            this.nick = nick;
            xmppSession.sendAndAwaitPresence(enterPresence, new StanzaFilter<Presence>() {
                @Override
                public boolean accept(Presence presence) {
                    Jid room = presence.getFrom().asBareJid();
                    return room.equals(roomJid) && isSelfPresence(presence);
                }
            });
        } catch (XmppException e) {
            xmppSession.removeMessageListener(messageListener);
            xmppSession.removePresenceListener(presenceListener);
            throw e;
        }
        entered = true;
    }

    /**
     * Changes the room subject.
     *
     * @param subject The subject.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public void changeSubject(final String subject) throws XmppException {
        Message message = new Message(roomJid, Message.Type.GROUPCHAT);
        message.setSubject(subject);
        xmppSession.sendAndAwaitMessage(message, new StanzaFilter<Message>() {
            @Override
            public boolean accept(Message message) {
                return message.getSubject() != null && message.getSubject().equals(subject);
            }
        });
    }

    /**
     * Sends a message to the room.
     *
     * @param message The message text.
     */
    public void sendMessage(String message) {
        Message m = new Message(roomJid, Message.Type.GROUPCHAT);
        m.setBody(message);
        xmppSession.send(m);
    }

    /**
     * Sends a message to the room.
     *
     * @param message The message.
     */
    public void sendMessage(Message message) {
        message.setType(Message.Type.GROUPCHAT);
        message.setTo(roomJid);
        xmppSession.send(message);
    }

    /**
     * Changes the nickname.
     *
     * @param newNickname The new nickname.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#changenick">7.6 Changing Nickname</a>
     */
    public synchronized void changeNickname(String newNickname) throws XmppException {
        if (!entered) {
            throw new IllegalStateException("You must have entered the room to change your nickname.");
        }
        final Presence changeNickNamePresence = new Presence();
        changeNickNamePresence.setTo(roomJid.withResource(newNickname));
        xmppSession.sendAndAwaitPresence(changeNickNamePresence, new StanzaFilter<Presence>() {
            @Override
            public boolean accept(Presence presence) {
                return presence.getFrom().equals(changeNickNamePresence.getTo());
            }
        });
    }

    /**
     * Changes the availability status.
     *
     * @param show   The 'show' value.
     * @param status The status.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#changepres">7.7 Changing Availability Status</a>
     */
    public synchronized void changeAvailabilityStatus(Presence.Show show, String status) {
        if (!entered) {
            throw new IllegalStateException("You must have entered the room to change the availability status.");
        }
        Presence presence = new Presence();
        presence.setTo(roomJid.withResource(nick));
        presence.setShow(show);
        presence.setStatus(status);
        xmppSession.send(presence);
    }

    /**
     * Invites another user to the room. The invitation will be mediated by the room.
     *
     * @param invitee The invitee.
     * @param reason  The reason.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#invite">7.8 Inviting Another User to a Room</a>
     */
    public void invite(Jid invitee, String reason) {
        invite(invitee, reason, false);
    }

    /**
     * @param invitee The invitee.
     * @param reason  The reason.
     * @param direct  True, if the message is sent directly to the invitee; false if it is mediated by the room.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#invite-direct">7.8.1 Direct Invitation</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#invite-mediated">7.8.2 Mediated Invitation</a>
     */
    public void invite(Jid invitee, String reason, boolean direct) {
        Message message;
        if (direct) {
            message = new Message(invitee, Message.Type.NORMAL);
            message.getExtensions().add(new DirectInvitation(roomJid, null, reason));
        } else {
            message = new Message(roomJid, Message.Type.NORMAL);
            message.getExtensions().add(MucUser.withInvites(new Invite(invitee, reason)));
        }
        xmppSession.send(message);
    }

    /**
     * Gets the data form necessary to register with the room.
     *
     * @return The data form.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#register">7.10 Registering with a Room</a>
     * @see RoomRegistrationForm
     */
    public DataForm getRegistrationForm() throws XmppException {
        IQ iq = new IQ(roomJid, IQ.Type.GET, new Registration());
        IQ result = xmppSession.query(iq);
        Registration registration = result.getExtension(Registration.class);
        if (registration != null) {
            return registration.getRegistrationForm();
        }
        return null;
    }

    /**
     * Submits the registration form.
     *
     * @param dataForm The data form.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#register">7.10 Registering with a Room</a>
     * @see RoomRegistrationForm
     */
    public void submitRegistrationForm(DataForm dataForm) throws XmppException {
        if (dataForm == null) {
            throw new IllegalArgumentException("dataForm must not be null.");
        }
        if (dataForm.getType() != DataForm.Type.SUBMIT) {
            throw new IllegalArgumentException("Data Form must be of type 'submit'");
        }
        if (!"http://jabber.org/protocol/muc#register".equals(dataForm.getFormType())) {
            throw new IllegalArgumentException("Data Form is not of type 'http://jabber.org/protocol/muc#register'");
        }
        Registration registration = new Registration(dataForm);
        IQ iq = new IQ(roomJid, IQ.Type.SET, registration);
        xmppSession.query(iq);
    }

    /**
     * Gets your reserved room nickname.
     *
     * @return The reserved nickname or null, if you don't have a reserved nickname.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     */
    public String getReservedNickname() throws XmppException {
        ServiceDiscoveryManager serviceDiscoveryManager = xmppSession.getExtensionManager(ServiceDiscoveryManager.class);
        InfoNode infoNode = serviceDiscoveryManager.discoverInformation(roomJid, "x-roomuser-item");
        if (infoNode != null) {
            for (Identity identity : infoNode.getIdentities()) {
                if ("conference".equals(identity.getCategory()) && "text".equals(identity.getType())) {
                    return identity.getName();
                }
            }
        }
        return null;
    }

    /**
     * Requests voice in a moderated room.
     *
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#requestvoice">7.13 Requesting Voice</a>
     */
    public void requestVoice() {
        Message message = new Message(roomJid);
        DataForm dataForm = new DataForm(DataForm.Type.SUBMIT);
        RequestVoiceForm requestVoiceForm = new RequestVoiceForm(dataForm);
        requestVoiceForm.setRole(Role.PARTICIPANT);
        message.getExtensions().add(dataForm);
        xmppSession.send(message);
    }

    /**
     * Exits the room.
     *
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#exit">7.14 Exiting a Room</a>
     */
    public void exit() {
        exit(null);
    }

    /**
     * Exits the room with a custom message.
     *
     * @param message The exit message.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#exit">7.14 Exiting a Room</a>
     */
    public synchronized void exit(String message) {

        if (!entered) {
            throw new IllegalStateException("You can't exit a room, when you didn't enter it.");
        }
        Presence presence = new Presence(Presence.Type.UNAVAILABLE);
        presence.setTo(roomJid.withResource(nick));
        presence.setStatus(message);
        xmppSession.send(presence);
        userHasExited();

        nick = null;
        entered = false;
        occupantMap.clear();
    }

    /**
     * Gets the voice list.
     *
     * @return The voice list.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifyvoice">8.5 Modifying the Voice List</a>
     */
    public List<? extends rocks.xmpp.extensions.muc.model.Item> getVoiceList() throws XmppException {
        IQ result = xmppSession.query(new IQ(roomJid, IQ.Type.GET, MucAdmin.withItem(Role.PARTICIPANT, null, null)));
        MucAdmin mucAdmin = result.getExtension(MucAdmin.class);
        return mucAdmin.getItems();
    }

    /**
     * Changes multiple affiliations or roles.
     *
     * @param items The items.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifyvoice">8.5 Modifying the Voice List</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifymember">9.5 Modifying the Member List</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifymod">9.8 Modifying the Moderator List</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifyowner">10.5 Modifying the Owner List</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifyadmin">10.8 Modifying the Admin List</a>
     */
    public void changeAffiliationsOrRoles(List<rocks.xmpp.extensions.muc.model.Item> items) throws XmppException {
        xmppSession.query(new IQ(roomJid, IQ.Type.SET, MucAdmin.withItems(items)));
    }

    /**
     * Gets the ban list.
     *
     * @return The ban list.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifyban">9.2 Modifying the Ban List</a>
     */
    public List<? extends rocks.xmpp.extensions.muc.model.Item> getBanList() throws XmppException {
        IQ result = xmppSession.query(new IQ(roomJid, IQ.Type.GET, MucAdmin.withItem(Affiliation.OUTCAST, null, null)));
        MucAdmin mucAdmin = result.getExtension(MucAdmin.class);
        return mucAdmin.getItems();
    }

    /**
     * Changes the affiliation for an user.
     * <p>
     * Use this method for one of the following use cases:
     * </p>
     * <ul>
     * <li>Banning a User (affiliation = {@link Affiliation#OUTCAST})</li>
     * <li>Granting Membership (affiliation = {@link Affiliation#MEMBER})</li>
     * <li>Revoking Membership (affiliation = {@link Affiliation#NONE})</li>
     * <li>Granting Admin Status (affiliation = {@link Affiliation#ADMIN})</li>
     * <li>Revoking Admin Status (affiliation = {@link Affiliation#MEMBER})</li>
     * <li>Granting Owner Status (affiliation = {@link Affiliation#OWNER})</li>
     * <li>Revoking Owner Status (affiliation = {@link Affiliation#ADMIN})</li>
     * </ul>
     *
     * @param affiliation The new affiliation for the user.
     * @param user        The user.
     * @param reason      The reason.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#ban">9.1 Banning a User</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#grantmember">9.3 Granting Membership</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#revokemember">9.4 Revoking Membership</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#grantowner">10.3 Granting Owner Status</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#revokeowner">10.4 Revoking Owner Status</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#grantadmin">10.6 Granting Admin Status</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#revokeadmin">10.7 Revoking Admin Status</a>
     */
    public void changeAffiliation(Affiliation affiliation, Jid user, String reason) throws XmppException {
        xmppSession.query(new IQ(roomJid, IQ.Type.SET, MucAdmin.withItem(affiliation, user, reason)));
    }

    /**
     * Changes the role for an occupant.
     * <p>
     * Use this method for one of the following use cases:
     * </p>
     * <ul>
     * <li>Kicking an Occupant (role = {@link Role#NONE})</li>
     * <li>Granting Voice to a Visitor (role = {@link Role#PARTICIPANT})</li>
     * <li>Revoking Voice from a Participant (affiliation = {@link Role#VISITOR})</li>
     * <li>Granting Moderator Status (role = {@link Role#MODERATOR})</li>
     * <li>Revoking Moderator Status (role = {@link Role#PARTICIPANT})</li>
     * </ul>
     *
     * @param role     The new role for the user.
     * @param nickname The occupant's nickname.
     * @param reason   The reason.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#kick">8.2 Kicking an Occupant</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#grantvoice">8.3 Granting Voice to a Visitor</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#revokevoice">8.4 Revoking Voice from a Participant</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#grantmod">9.6 Granting Moderator Status</a>
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#revokemod">9.7 Revoking Moderator Status</a>
     */
    public void changeRole(Role role, String nickname, String reason) throws XmppException {
        xmppSession.query(new IQ(roomJid, IQ.Type.SET, MucAdmin.withItem(role, nickname, reason)));
    }

    /**
     * Gets the members of the room.
     * <p>
     * In the context of a members-only room, the member list is essentially a "whitelist" of people who are allowed to enter the room.
     * </p>
     * <p>
     * In the context of an open room, the member list is simply a list of users (bare JID and reserved nick) who are registered with the room.
     * </p>
     *
     * @return The members.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifymember">9.5 Modifying the Member List</a>
     */
    public List<? extends rocks.xmpp.extensions.muc.model.Item> getMembers() throws XmppException {
        IQ result = xmppSession.query(new IQ(roomJid, IQ.Type.GET, MucAdmin.withItem(Affiliation.MEMBER, null, null)));
        MucAdmin mucAdmin = result.getExtension(MucAdmin.class);
        return mucAdmin.getItems();
    }

    /**
     * Gets the moderators.
     *
     * @return The moderators.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#modifymod">9.8 Modifying the Moderator List</a>
     */
    public List<? extends rocks.xmpp.extensions.muc.model.Item> getModerators() throws XmppException {
        IQ result = xmppSession.query(new IQ(roomJid, IQ.Type.GET, MucAdmin.withItem(Role.MODERATOR, null, null)));
        MucAdmin mucAdmin = result.getExtension(MucAdmin.class);
        return mucAdmin.getItems();
    }

    /**
     * Creates an instant room.
     *
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the entity returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the entity did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#createroom-instant">10.1.2 Creating an Instant Room</a>
     */
    public void createRoom() throws XmppException {
        enter(nick);
        xmppSession.query(new IQ(roomJid, IQ.Type.SET, new MucOwner(new DataForm(DataForm.Type.SUBMIT))));
    }

    /**
     * Gets the room information for this chat room.
     *
     * @return The room info.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#disco-roominfo">6.4 Querying for Room Information</a>
     */
    public RoomInfo getRoomInfo() throws XmppException {
        InfoNode infoNode = serviceDiscoveryManager.discoverInformation(roomJid);

        Identity identity = null;
        Set<MucFeature> mucFeatures = new HashSet<>();
        RoomInfoForm roomInfoForm = null;

        if (infoNode != null) {
            Set<Identity> identities = infoNode.getIdentities();
            Iterator<Identity> iterator = identities.iterator();
            if (iterator.hasNext()) {
                identity = iterator.next();
            }
            for (Feature feature : infoNode.getFeatures()) {
                for (MucFeature mucFeature : MucFeature.values()) {
                    if (mucFeature.getServiceDiscoveryFeature().equals(feature.getVar())) {
                        mucFeatures.add(mucFeature);
                    }
                }
            }

            for (DataForm dataForm : infoNode.getExtensions()) {
                DataForm.Field formType = dataForm.findField("FORM_TYPE");
                if (formType != null && !formType.getValues().isEmpty() && formType.getValues().get(0).equals("http://jabber.org/protocol/muc#roominfo")) {
                    roomInfoForm = new RoomInfoForm(dataForm);
                    break;
                }
            }
        }

        return new RoomInfo(identity, mucFeatures, roomInfoForm);
    }

    /**
     * Gets the occupants in this room, i.e. their nicknames. This method should be used, when you are not yet in the room.
     *
     * @return The occupants.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#disco-roomitems">6.5 Querying for Room Items</a>
     * @see #getOccupants()
     */
    public List<String> discoverOccupants() throws XmppException {
        ItemNode itemNode = serviceDiscoveryManager.discoverItems(roomJid);
        List<String> occupants = new ArrayList<>();
        List<Item> items = itemNode.getItems();
        for (Item item : items) {
            if (item.getJid() != null) {
                String nickname = item.getJid().getResource();
                if (nickname != null) {
                    occupants.add(nickname);
                }
            }
        }
        return occupants;
    }

    /**
     * Gets the occupants, while being in the room.
     *
     * @return The occupants.
     */
    public Collection<Occupant> getOccupants() {
        return occupantMap.values();
    }

    /**
     * Gets an occupant by nickname.
     *
     * @param nickname The occupant's nickname.
     * @return The occupant.
     */
    public Occupant getOccupant(String nickname) {
        return occupantMap.get(nickname);
    }

    /**
     * Gets the configuration form for the room.
     * You can wrap the form into {@link rocks.xmpp.extensions.muc.model.RoomConfigurationForm} for easier processing.
     * <p>
     * Use this method if you want to create a reserved room or configure an existing room.
     * </p>
     *
     * @return The configuration form.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see rocks.xmpp.extensions.muc.model.RoomConfigurationForm
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#createroom-reserved">10.1.3 Creating a Reserved Room</a>
     * @see #submitConfigurationForm(rocks.xmpp.extensions.data.model.DataForm)
     */
    public DataForm getConfigurationForm() throws XmppException {
        IQ result = xmppSession.query(new IQ(roomJid, IQ.Type.GET, new MucOwner()));
        MucOwner mucOwner = result.getExtension(MucOwner.class);
        return mucOwner.getConfigurationForm();
    }

    /**
     * Submits the configuration form for this room.
     *
     * @param dataForm The data form.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#createroom-reserved">10.1.3 Creating a Reserved Room</a>
     * @see #getConfigurationForm()
     */
    public void submitConfigurationForm(DataForm dataForm) throws XmppException {
        if (dataForm == null) {
            throw new IllegalArgumentException("dataForm must not be null.");
        }
        if (dataForm.getType() != DataForm.Type.SUBMIT && dataForm.getType() != DataForm.Type.CANCEL) {
            throw new IllegalArgumentException("Data Form must be of type 'submit' or 'cancel'");
        }
        if (!"http://jabber.org/protocol/muc#roomconfig".equals(dataForm.getFormType())) {
            throw new IllegalArgumentException("Data Form is not of type 'http://jabber.org/protocol/muc#roomconfig'");
        }
        MucOwner mucOwner = new MucOwner(dataForm);
        IQ iq = new IQ(roomJid, IQ.Type.SET, mucOwner);
        xmppSession.query(iq);
    }

    /**
     * Gets the name for this room.
     *
     * @return The room name.
     */
    public String getName() {
        return name;
    }

    /**
     * Destroys the room.
     *
     * @param reason The reason for the room destruction.
     * @throws rocks.xmpp.core.stanza.model.StanzaException If the chat service returned a stanza error.
     * @throws rocks.xmpp.core.session.NoResponseException  If the chat service did not respond.
     * @see <a href="http://xmpp.org/extensions/xep-0045.html#destroyroom">10.9 Destroying a Room</a>
     */
    public void destroy(String reason) throws XmppException {
        MucOwner mucOwner = MucOwner.withDestroy(roomJid, reason);
        IQ iq = new IQ(roomJid, IQ.Type.SET, mucOwner);
        xmppSession.query(iq);
    }


    /**
     * Gets the room address.
     *
     * @return The room address.
     */
    public Jid getAddress() {
        return roomJid;
    }

    @Override
    public String toString() {
        return roomJid.toString();
    }
}
TOP

Related Classes of rocks.xmpp.extensions.muc.ChatRoom

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.