/** * * Copyright 2015-2023 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Smack API for Multi-User Chat (MUC, XEP-0045). This API allows configuration of, participation in, and administration * of individual text-based conference rooms. The two key API classes are {@link MultiUserChatManager} and * {@link MultiUserChat}. *

Create a new Room

*

Description

*

* Allowed users may create new rooms. There are two types of rooms that you can create. **Instant rooms** which are * available for immediate access and are automatically created based on some default configuration and **Reserved * rooms** which are manually configured by the room creator before anyone is allowed to enter. *

*

Usage

*

* In order to create a room you will need to first create an instance of _* *MultiUserChat**_. In order to do so, get a * instance of `MultiUserChatManager` and call `getMultiUserChat(String)` to retrieve a `MultiUserChat` instance. The * next step is to send **create(String nickname)** to the _**MultiUserChat**_ instance where nickname is the nickname * to use when joining the room. Depending on the type of room that you want to create you will have to use different * configuration forms. In order to create an Instant room just use `MucCreateConfigFormHandle.makeInstant()`. But if * you want to create a Reserved room then you should first get the room's configuration form, complete the form and * finally send it back to the server. *

*

Examples

*

* In this example we can see how to create an instant room: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 * // Get a MultiUserChat using MultiUserChatManager
 * MultiUserChat muc = manager.getMultiUserChat(mucJid);
 * // Create the room and send an empty configuration form to make this an instant room
 * muc.create(nickname).makeInstant();
 * }
*

* In this example we can see how to create a reserved room. The form is completed with default values: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 * // Create a MultiUserChat using an XMPPConnection for a room
 * MultiUserChat muc = manager.getMultiUserChat(mucJid);
 * // Prepare a list of owners of the new room
 * Set owners = JidUtil.jidSetFrom(new String[] { "me@example.org", "juliet@example.org" });
 * // Create the room
 * muc.create(nickname).getConfigFormManger().setRoomOwners(owners).submitConfigurationForm();
 * }
* *

Join a room

*

Description

*

* Your usual first step in order to send messages to a room is to join the room. Multi User Chat allows to specify * several parameter while joining a room. Basically you can control the amount of history to receive after joining the * room as well as provide your nickname within the room and a password if the room is password protected. *

*

Usage

*

* In order to join a room you will need to first get an instance of _**MultiUserChat**_. In order to do so, get a * instance of `MultiUserChatManager` and call `getMultiUserChat(String)` to retrieve a `MultiUserChat` instance. The * next step is to send **join(...)** to the _**MultiUserChat**_ instance. But first you will have to decide which join * message to send. If you want to just join the room without a password and without specifying the amount of history to * receive then you could use join(String nickname)** where nickname if your nickname in the room. In case the room * requires a password in order to join you could then use **join(String nickname, String password)**. And finally, the * most complete way to join a room is to send **join(String nickname, String password, DiscussionHistory history, long * timeout)** where nickname is your nickname in the room, , password is your password to join the room, history is an * object that specifies the amount of history to receive and timeout is the milliseconds to wait for a response from * the server. *

*

Examples

*

* In this example we can see how to join a room with a given nickname: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 * // Create a MultiUserChat using an XMPPConnection for a room
 * MultiUserChat muc2 = manager.getMultiUserChat(mucJid);
 * // User2 joins the new room
 * // The room service will decide the amount of history to send
 * muc2.join(nickname);
 * }
*

* In this example we can see how to join a room with a given nickname and password: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 * // Create a MultiUserChat using an XMPPConnection for a room
 * MultiUserChat muc2 = manager.getMultiUserChat(mucJid);
 * // User2 joins the new room using a password
 * // The room service will decide the amount of history to send
 * muc2.join(nickname, "password");
 * }
*

* In this example we can see how to join a room with a given nickname specifying the amount of history to receive: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 *
 * // Create a MultiUserChat using an XMPPConnection for a room
 * MultiUserChat muc2 = manager.getMultiUserChat(mucJid);
 *
 * // User2 joins the new room using a password and specifying
 * // the amount of history to receive. In this example we are requesting the last 5 messages.
 * DiscussionHistory history = new DiscussionHistory();
 * history.setMaxStanzas(5);
 * muc2.join(nickname, "password", history, conn1.getPacketReplyTimeout());
 * }
* *

Manage room invitations

*

Description

*

* It can be useful to invite another user to a room in which one is an occupant. Depending on the room's type the * invitee could receive a password to use to join the room and/or be added to the member list if the room is of type * members-only. Smack allows to send room invitations and let potential invitees to listening for room invitations and * inviters to listen for invitees' rejections. *

*

Usage

*

* In order to invite another user to a room you must be already joined to the room. Once you are joined just send * **invite(String participant, String reason)** to the _**MultiUserChat**_ where participant is the user to invite to * the room (e.g. hecate@shakespeare.lit) and reason is the reason why the user is being invited. *

*

* If potential invitees want to listen for room invitations then the invitee must add an _**InvitationListener**_ to * the _**MultiUserChatManager**_ class. Since the _**InvitationListener**_ is an _interface_, it is necessary to create * a class that implements this _interface_. If an inviter wants to listen for room invitation rejections, just add an * _**InvitationRejectionListener**_ to the _**MultiUserChat**_. _**InvitationRejectionListener**_ is also an interface * so you will need to create a class that implements this interface. *

*

Examples

*

* In this example we can see how to invite another user to the room and lister for possible rejections: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 *
 * // Create a MultiUserChat using an XMPPConnection for a room
 * MultiUserChat muc2 = manager.getMultiUserChat(mucJid);
 *
 * muc2.join(nickname);
 * // User2 listens for invitation rejections
 * muc2.addInvitationRejectionListener(new InvitationRejectionListener() {
 *     public void invitationDeclined(String invitee, String reason) {
 *         // Do whatever you need here...
 *     }
 * });
 * // User2 invites user3 to join to the room
 * muc2.invite(otherJid, "Meet me in this excellent room");
 * }
*

* In this example we can see how to listen for room invitations and decline invitations: *

* *
{@code
 * // User3 listens for MUC invitations
 * MultiUserChatManager.getInstanceFor(connection).addInvitationListener(new InvitationListener() {
 *     public void invitationReceived(XMPPConnection conn, String room, EntityFullJid inviter, String reason,
 *                     String password) {
 *         // Reject the invitation
 *         MultiUserChat.decline(conn, room, inviter.asBareJid(), "I'm busy right now");
 *     }
 * });
 * }
* *

Discover MUC support

*

Description

*

* A user may want to discover if one of the user's contacts supports the Multi-User Chat protocol. *

*

Usage

*

* In order to discover if one of the user's contacts supports MUC just send isServiceEnabled(String user)** to the * _**MultiUserChatManager**_ class where user is a fully qualified XMPP ID, e.g. jdoe@example.com. You will receive a * boolean indicating whether the user supports MUC or not. *

*

Examples

*

* In this example we can see how to discover support of MUC: *

* *
{@code
 * // Discover whether user3@host.org supports MUC or not
 * boolean supportsMuc = MultiUserChatManager.getInstanceFor(connection).isServiceEnabled(otherJid);
 * }
* *

Discover joined rooms

*

Description

*

* A user may also want to query a contact regarding which rooms the contact is in. *

*

Usage

*

* In order to get the rooms where a user is in just send getJoinedRooms(String user)** to the * _**MultiUserChatManager**_ class where user is a fully qualified XMPP ID, e.g. jdoe@example.com. You will get an * Iterator of Strings as an answer where each String represents a room name. *

*

Examples

*

* In this example we can see how to get the rooms where a user is in: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 *
 * // Get the rooms where user3@host.org has joined
 * List joinedRooms = manager.getJoinedRooms("user3@host.org/Smack");
 * }
* *

Discover room information

*

Description

*

* A user may need to discover information about a room without having to actually join the room. The server will * provide information only for public rooms. *

*

Usage

*

* In order to discover information about a room just send getRoomInfo(String room)** to the _**MultiUserChatManager**_ * class where room is the XMPP ID of the room, e.g. roomName@conference.myserver. You will get a RoomInfo object that * contains the discovered room information. *

*

Examples

*

* In this example we can see how to discover information about a room: *

* *
{@code
 * // Get the MultiUserChatManager
 * MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
 *
 * // Discover information about the room roomName@conference.myserver
 * RoomInfo info = manager.getRoomInfo("roomName@conference.myserver");
 * System.out.println("Number of occupants:" + info.getOccupantsCount());
 * System.out.println("Room Subject:" + info.getSubject());
 * }
* *

Start a private chat

*

Description

*

* A room occupant may want to start a private chat with another room occupant even though they don't know the fully * qualified XMPP address (e.g. jdoe@example.com) of each other. *

*

Usage

*

* To create a private chat with another room occupant just send createPrivateChat(String participant)** to the * _**MultiUserChat**_ that you used to join the room. The parameter participant is the occupant unique room JID (e.g. * 'darkcave@macbeth.shakespeare.lit/Paul'). You will receive a regular _**Chat**_ object that you can use to chat with * the other room occupant. *

*

Examples

*

* In this example we can see how to start a private chat with another room occupant: *

* *
{@code
 * // Start a private chat with another participant
 * Chat chat = muc2.createPrivateChat("myroom@conference.jabber.org/johndoe");
 * chat.sendMessage("Hello there");
 * }
* *

Manage changes on room subject

*

Description

*

* A common feature of multi-user chat rooms is the ability to change the subject within the room. As a default, only * users with a role of "moderator" are allowed to change the subject in a room. Although some rooms may be configured * to allow a mere participant or even a visitor to change the subject. *

*

* Every time the room's subject is changed you may want to be notified of the modification. The new subject could be * used to display an in-room message. *

*

Usage

*

* In order to modify the room's subject just send **changeSubject(String subject)** to the _**MultiUserChat**_ that you * used to join the room where subject is the new room's subject. On the other hand, if you want to be notified whenever * the room's subject is modified you should add a _**SubjectUpdatedListener**_ to the _**MultiUserChat**_ by sending ** addSubjectUpdatedListener(SubjectUpdatedListener listener)** to the _**MultiUserChat**_. Since the * _**SubjectUpdatedListener**_ is an _interface_, it is necessary to create a class that implements this _interface_. *

*

Examples

*

* In this example we can see how to change the room's subject and react whenever the room's subject is modified: *

* *
{@code
 * // An occupant wants to be notified every time the room's subject is changed
 * muc3.addSubjectUpdatedListener(new SubjectUpdatedListener() {
 *      public void subjectUpdated(String subject, String from) {
 *          ....
 *      }
 * });
 * // A room's owner changes the room's subject
 * muc2.changeSubject("New Subject");
 * }
* *

*

Description

*

* There are four defined roles that an occupant can have: *

*
    *
  1. Moderator
  2. *
  3. Participant
  4. *
  5. Visitor
  6. *
  7. None (the absence of a role)
  8. *
*

* These roles are temporary in that they do not persist across a user's visits to the room and can change during the * course of an occupant's visit to the room. *

*

* A moderator is the most powerful occupant within the context of the room, and can to some extent manage other * occupants' roles in the room. A participant has fewer privileges than a moderator, although he or she always has the * right to speak. A visitor is a more restricted role within the context of a moderated room, since visitors are not * allowed to send messages to all occupants. *

*

* Roles are granted, revoked, and maintained based on the occupant's room nickname or full JID. Whenever an occupant's * role is changed Smack will trigger specific events. *

*

Usage

*

* In order to grant voice (i.e. make someone a _participant_) just send the message **grantVoice(String nickname)** to * _**MultiUserChat**_. Use revokeVoice(String nickname)** to revoke the occupant's voice (i.e. make the occupant a * _visitor_). *

*

* In order to grant moderator privileges to a participant or visitor just send the message **grantModerator(String * nickname)** to _**MultiUserChat**_. Use revokeModerator(String nickname)** to revoke the moderator privilege from the * occupant thus making the occupant a participant. *

*

* Smack allows you to listen for role modification events. If you are interested in listening role modification events * of any occupant then use the listener _ParticipantStatusListener_**. But if you are interested in listening for your * own role modification events, use the listener **_UserStatusListener_**. Both listeners should be added to the * _**MultiUserChat**_ by using addParticipantStatusListener(ParticipantStatusListener listener)** or ** addUserStatusListener(UserStatusListener listener)** respectively. These listeners include several notification * events but you may be interested in just a few of them. Smack provides default implementations for these listeners * avoiding you to implement all the interfaces' methods. The default implementations are * **_DefaultUserStatusListener_** and _DefaultParticipantStatusListener_**. Below you will find the sent messages to * the listeners whenever an occupant's role has changed. *

*

* These are the triggered events when the role has been upgraded: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Role Upgrade paths
OldNewEvents
NoneVisitor--
VisitorParticipantvoiceGranted
ParticipantModeratormoderatorGranted
NoneParticipantvoiceGranted
NoneModeratorvoiceGranted + moderatorGranted
VisitorModeratorvoiceGranted + moderatorGranted
*

* These are the triggered events when the role has been downgraded: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Role Downgrade paths
OldNewEvents
ModeratorParticipantmoderatorRevoked
ParticipantVistorvoiceRevoked
VisitorNonekicked
ModeratorVisitorvoiceRevoked + moderatorRevoked
ModeratorNonekicked
ParticipantNonekicked
*

Examples

*

* In this example we can see how to grant voice to a visitor and listen for the notification events: *

* *
{@code
 * // User1 creates a room
 * muc = manager.getMultiUserChat("myroom@conference.jabber.org");
 * muc.create("testbot");
 * // User1 (which is the room owner) configures the room as a moderated room
 * Form form = muc.getConfigurationForm();
 * FillableForm answerForm = configForm.getFillableForm();
 * answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
 * muc.sendConfigurationForm(answerForm);
 *
 * // User2 joins the new room (as a visitor)
 * MultiUserChat muc2 = manager2.getMultiUserChat("myroom@conference.jabber.org");
 * muc2.join("testbot2");
 * // User2 will listen for his own "voice" notification events
 * muc2.addUserStatusListener(new DefaultUserStatusListener() {
 *      public void voiceGranted() {
 *          super.voiceGranted();
 *          ...
 *      }
 *      public void voiceRevoked() {
 *          super.voiceRevoked();
 *          ...
 *      }
 * });
 *
 * // User3 joins the new room (as a visitor)
 * MultiUserChat muc3 = manager3.getMultiUserChat("myroom@conference.jabber.org");
 * muc3.join("testbot3");
 * // User3 will lister for other occupants "voice" notification events
 * muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() {
 *      public void voiceGranted(String participant) {
 *          super.voiceGranted(participant);
 *          ...
 *      }
 *      public void voiceRevoked(String participant) {
 *          super.voiceRevoked(participant);
 *          ...
 *      }
 * });
 *
 * // The room's owner grants voice to user2
 * muc.grantVoice("testbot2");
 * }
* *

Manage affiliation modifications

*

Description

*

* There are five defined affiliations that a user can have in relation to a room: *

*
    *
  1. Owner
  2. *
  3. Admin
  4. *
  5. Member
  6. *
  7. Outcast
  8. *
  9. None (the absence of an affiliation)
  10. *
*

* These affiliations are semi-permanent in that they persist across a user's visits to the room and are not affected by * happenings in the room. Affiliations are granted, revoked, and maintained based on the user's bare JID. *

*

* If a user without a defined affiliation enters a room, the user's affiliation is defined as "none"; however, this * affiliation does not persist across visits. *

*

* Owners and admins are by definition immune from certain actions. Specifically, an owner or admin cannot be kicked * from a room and cannot be banned from a room. An admin must first lose his or her affiliation (i.e., have an * affiliation of "none" or "member") before such actions could be performed on them. *

*

* The member affiliation provides a way for a room owner or admin to specify a "whitelist" of users who are allowed to * enter a members-only room. When a member enters a members-only room, his or her affiliation does not change, no * matter what his or her role is. The member affiliation also provides a way for users to effectively register with an * open room and thus be permanently associated with that room in some way (one result may be that the user's nickname * is reserved in the room). *

*

* An outcast is a user who has been banned from a room and who is not allowed to enter the room. Whenever a user's * affiliation is changed Smack will trigger specific events. *

*

Usage

*

* In order to grant membership to a room, administrator privileges or owner priveliges just send * **grantMembership(String jid)**, **grantAdmin(String jid)** or **grantOwnership(String jid)** to _**MultiUserChat**_ * respectively. Use **revokeMembership(String jid)**, **revokeAdmin(String jid)** or revokeOwnership(String jid)** to * revoke the membership to a room, administrator privileges or owner priveliges respectively. *

*

* In order to ban a user from the room just send the message **banUser(String jid, String reason)** to * _**MultiUserChat**_. *

*

* Smack allows you to listen for affiliation modification events. If you are interested in listening affiliation * modification events of any user then use the listener **_ParticipantStatusListener_**. But if you are interested in * listening for your own affiliation modification events, use the listener _UserStatusListener_**. Both listeners * should be added to the _**MultiUserChat**_ by using addParticipantStatusListener(ParticipantStatusListener * listener)** or addUserStatusListener(UserStatusListener listener)** respectively. These listeners include several * notification events but you may be interested in just a few of them. Smack provides default implementations for these * listeners avoiding you to implement all the interfaces' methods. The default implementations are * **_DefaultUserStatusListener_** and _DefaultParticipantStatusListener_**. Below you will find the sent messages to * the listeners whenever a user's affiliation has changed. *

*

* These are the triggered events when the affiliation has been upgraded: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Affiliation Upgrade paths
OldNewEvents
NoneMembermembershipGranted
MemberAdminmembershipRevoked + adminGranted
AdminOwneradminRevoked + ownershipGranted
NoneAdminadminGranted
NoneOwnerownershipGranted
MemberOwnermembershipRevoked + ownershipGranted
*

* These are the triggered events when the affiliation has been downgraded: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Affiliation Downgrade paths
OwnerAdminownershipRevoked + adminGranted
AdminMemberadminRevoked + membershipGranted
MemberNonemembershipRevoked
OwnerMemberownershipRevoked + membershipGranted
OwnerNoneownershipRevoked
AdminNoneadminRevoked
AnyoneOutcastbanned
*

Examples

*

* In this example we can see how to grant admin privileges to a user and listen for the notification events: *

* *
{@code
 * // User1 creates a room
 * muc = manager.getMultiUserChat("myroom@conference.jabber.org");
 * muc.create("testbot");
 * // User1 (which is the room owner) configures the room as a moderated room
 * Form form = muc.getConfigurationForm();
 * FillableForm answerForm = configForm.getFillableForm();
 * answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
 * muc.sendConfigurationForm(answerForm);
 *
 * // User2 joins the new room (as a visitor)
 * MultiUserChat muc2 = manager2.getMultiUserChat("myroom@conference.jabber.org");
 * muc2.join("testbot2");
 * // User2 will listen for his own admin privileges
 * muc2.addUserStatusListener(new DefaultUserStatusListener() {
 *      public void membershipRevoked() {
 *          super.membershipRevoked();
 *          ...
 *      }
 *      public void adminGranted() {
 *          super.adminGranted();
 *          ...
 *      }
 * });
 *
 * // User3 joins the new room (as a visitor)
 * MultiUserChat muc3 = manager3.getMultiUserChat("myroom@conference.jabber.org");
 * muc3.join("testbot3");
 * // User3 will lister for other users admin privileges
 * muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() {
 *      public void membershipRevoked(String participant) {
 *          super.membershipRevoked(participant);
 *          ...
 *      }
 *      public void adminGranted(String participant) {
 *          super.adminGranted(participant);
 *          ...
 *      }
 * });
 * // The room's owner grants admin privileges to user2
 * muc.grantAdmin("user2@jabber.org");
 * }
* * @see XEP-0045: Multi-User Chat */ package org.jivesoftware.smackx.muc;