/** * * 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}. *
* 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. *
** 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. *
** 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(); * }
* 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. *
** 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. *
** 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()); * }* *
* 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. *
** 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. *
** 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"); * } * }); * }* *
* A user may want to discover if one of the user's contacts supports the Multi-User Chat protocol. *
** 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. *
** 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); * }* *
* A user may also want to query a contact regarding which rooms the contact is in. *
** 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. *
** 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"); * }
* 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. *
** 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. *
** 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()); * }* *
* 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. *
** 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. *
** 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"); * }* *
* 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. *
** 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_. *
** 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"); * }* * *
* There are four defined roles that an occupant can have: *
** 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. *
** 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: *
*Old | *New | *Events | *
---|---|---|
None | *Visitor | *-- | *
Visitor | *Participant | *voiceGranted | *
Participant | *Moderator | *moderatorGranted | *
None | *Participant | *voiceGranted | *
None | *Moderator | *voiceGranted + moderatorGranted | *
Visitor | *Moderator | *voiceGranted + moderatorGranted | *
* These are the triggered events when the role has been downgraded: *
*Old | *New | *Events | *
---|---|---|
Moderator | *Participant | *moderatorRevoked | *
Participant | *Vistor | *voiceRevoked | *
Visitor | *None | *kicked | *
Moderator | *Visitor | *voiceRevoked + moderatorRevoked | *
Moderator | *None | *kicked | *
Participant | *None | *kicked | *
* 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"); * }* *
* There are five defined affiliations that a user can have in relation to a room: *
** 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. *
** 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: *
*Old | *New | *Events | *
---|---|---|
None | *Member | *membershipGranted | *
Member | *Admin | *membershipRevoked + adminGranted | *
Admin | *Owner | *adminRevoked + ownershipGranted | *
None | *Admin | *adminGranted | *
None | *Owner | *ownershipGranted | *
Member | *Owner | *membershipRevoked + ownershipGranted | *
* These are the triggered events when the affiliation has been downgraded: *
*Owner | *Admin | *ownershipRevoked + adminGranted | *
---|---|---|
Admin | *Member | *adminRevoked + membershipGranted | *
Member | *None | *membershipRevoked | *
Owner | *Member | *ownershipRevoked + membershipGranted | *
Owner | *None | *ownershipRevoked | *
Admin | *None | *adminRevoked | *
Anyone | *Outcast | *banned | *
* 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;