Xmppframework - How to Create a Muc Room and Invite Users

XMPPFramework - How to create a MUC room and invite users?

After exploring various solutions, I've decided to compile and share my implementation here:

  1. Create an XMPP Room:

    XMPPRoomMemoryStorage *roomStorage = [[XMPPRoomMemoryStorage alloc] init];

    /**
    * Remember to add 'conference' in your JID like this:
    * e.g. uniqueRoomJID@conference.yourserverdomain
    */

    XMPPJID *roomJID = [XMPPJID jidWithString:@"chat@conference.shakespeare"];
    XMPPRoom *xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:roomStorage
    jid:roomJID
    dispatchQueue:dispatch_get_main_queue()];

    [xmppRoom activate:[self appDelegate].xmppStream];
    [xmppRoom addDelegate:self
    delegateQueue:dispatch_get_main_queue()];

    [xmppRoom joinRoomUsingNickname:[self appDelegate].xmppStream.myJID.user
    history:nil
    password:nil];
  2. Check if room is successfully created in this delegate:

    - (void)xmppRoomDidCreate:(XMPPRoom *)sender
  3. Check if you've joined the room in this delegate:

    - (void)xmppRoomDidJoin:(XMPPRoom *)sender
  4. After room is created, fetch room configuration form:

    - (void)xmppRoomDidJoin:(XMPPRoom *)sender {
    [sender fetchConfigurationForm];
    }
  5. Configure your room

    /**
    * Necessary to prevent this message:
    * "This room is locked from entry until configuration is confirmed."
    */

    - (void)xmppRoom:(XMPPRoom *)sender didFetchConfigurationForm:(NSXMLElement *)configForm
    {
    NSXMLElement *newConfig = [configForm copy];
    NSArray *fields = [newConfig elementsForName:@"field"];

    for (NSXMLElement *field in fields)
    {
    NSString *var = [field attributeStringValueForName:@"var"];
    // Make Room Persistent
    if ([var isEqualToString:@"muc#roomconfig_persistentroom"]) {
    [field removeChildAtIndex:0];
    [field addChild:[NSXMLElement elementWithName:@"value" stringValue:@"1"]];
    }
    }

    [sender configureRoomUsingOptions:newConfig];
    }

    References: XEP-0045: Multi-User Chat, Implement Group Chat

  6. Invite users

    - (void)xmppRoomDidJoin:(XMPPRoom *)sender 
    {
    /**
    * You can read from an array containing participants in a for-loop
    * and send multiple invites in the same way here
    */

    [sender inviteUser:[XMPPJID jidWithString:@"keithoys"] withMessage:@"Greetings!"];
    }

There, you've created a XMPP multi-user/group chat room, and invited a user. :)

How to add a group in MUC room XMPP - iOS

There is no such feature in XMPP as default. You cannot add a roster group in a MUC room in the MUC specification. You should have your client iterate on your roster to add your needed contact from client code.

MUC How-to with XMPPFramework

Below is how I got my own problem solved. Note that this solution does not involve XMPPRoom at all. First, I created a method that, depending on the situation, either creates or enters a room. (Per XMPP documentation, the XML request for creating is the same is the same as the one you would send to enter a room; that is, if the room has does not exist yet when you enter it, the service will create it for you.)

Here we go. This is the method that creates/enters a room. What this method does is send a presence to the room which you intend to create/enter. If you are the first to enter a room and the room has not been created yet, you automatically become its owner and moderator.

- (void)createOrEnterRoom:(NSString *)roomName
{
//here we enter a room, or if the room does not yet exist, this method creates it
//per XMPP documentation: "If the room does not yet exist, the service SHOULD create the room"
//this method accepts an argument which is what you would baptize the room you wish created
NSXMLElement *presence = [NSXMLElement elementWithName:@"presence"];
NSString *room = [roomName stringByAppendingString:@"@conference.jabber.com/iMac"];
[presence addAttributeWithName:@"to" stringValue:room];
NSXMLElement *x = [NSXMLElement elementWithName:@"x" xmlns:@"http://jabber.org/protocol/muc"];
NSXMLElement *history = [NSXMLElement elementWithName:@"history"];
[history addAttributeWithName:@"maxstanzas" stringValue:@"50"];
[x addChild:history];
[presence addChild:x];
[[self xmppStream] sendElement:presence];
}

Next, in the AppDelegate where XMPPStream methods are declared we filter the XML response we receive in the didReceivePresence method by checking the status code sent by the server. If the status code is 201, bingo! The room creation went just fine. Status codes other than 201 mean different things, but let's focus on 201 for our purpose.

- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
NSXMLElement *x = [presence elementForName:@"x" xmlns:@"http://jabber.org/protocol/muc#user"];
for (NSXMLElement *status in [x elementsForName:@"status"])
{
switch ([status attributeIntValueForName:@"code"])
{
case 201: [self notifyRoomCreationOk:room];
}
}
}

Then, we tell the server that what we are creating a room of the type "instant," which means that we will send an IQ element telling it room defaults. notifyRoomCreationOk is a delegate method called in a different view when the room creation succeeds, after all I have to record the room in a text file to make it persistent so that the next time I open the app the room I created before will be visible. In my notifyRoomCreationOk method, I have sendDefaultRoomConfig which, basically, describes what is stated in the first sentence of this paragraph.

-(void)sendDefaultRoomConfig:(NSString *)room
{
NSXMLElement *x = [NSXMLElement elementWithName:@"x" xmlns:@"jabber:x:data"];
[x addAttributeWithName:@"type" stringValue:@"submit"];
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"http://jabber.org/protocol/muc#owner"];
[query addChild:x];
XMPPIQ *iq = [XMPPIQ iq];
[iq addAttributeWithName:@"id" stringValue:[NSString stringWithFormat:@"inroom-cr%@", room]];
[iq addAttributeWithName:@"to" stringValue:room];
[iq addAttributeWithName:@"type" stringValue:@"set"];
[iq addChild:query];
[[self xmppStream ] sendElement:iq];
}

Make sure that you have XMPPStream enabled on the views that call the above methods, otherwise, these won't work. That's all there is to it. Have fun XMPP-ing!

XMPPFramework - Implement Group Chat (MUC)

to get a list of rooms:

NSString* server = @"chat.shakespeare.lit"; //or whatever the server address for muc is
XMPPJID *servrJID = [XMPPJID jidWithString:server];
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:servJID];
[iq addAttributeWithName:@"from" stringValue:[xmppStream myJID].full];
NSXMLElement *query = [NSXMLElement elementWithName:@"query"];
[query addAttributeWithName:@"xmlns" stringValue:@"http://jabber.org/protocol/disco#items"];
[iq addChild:query];
[xmppStream sendElement:iq];

check for response in delegate method:

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq{
DDLogVerbose(@"%@", [iq description]);
return NO;
}

to join or create room

XMPPRoomMemoryStorage * _roomMemory = [[XMPPRoomMemoryStorage alloc]init];
NSString* roomID = @"roomExample@chat.shakespeare.lit";
XMPPJID * roomJID = [XMPPJID jidWithString:roomID];
XMPPRoom* xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:_roomMemory
jid:roomJID
dispatchQueue:dispatch_get_main_queue()];
[xmppRoom activate:self.xmppStream];
[xmppRoom addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppRoom joinRoomUsingNickname:@"myNickname"
history:nil
password:nil];

check for response in XMPPRoom delegate methods:

- (void)xmppRoomDidCreate:(XMPPRoom *)sender{
DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppRoomDidJoin:(XMPPRoom *)sender{
DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

update

to configure a room:

after:

[xmppRoom joinRoomUsingNickname:self.xmppStream.myJID.user
history:history
password:nil];

add:

[xmppRoom fetchConfigurationForm];

and check response in:

- (void)xmppRoom:(XMPPRoom *)sender didFetchConfigurationForm:(NSXMLElement *)configForm{
DDLogVerbose(@"%@: %@ -> %@", THIS_FILE, THIS_METHOD, sender.roomJID.user);
}

Review the configForm object, and change as needed, then send it with [sender configureRoomUsingOptions:newConfig];

example:
to change the configuration to make the room persistent you can add something like:

NSXMLElement *newConfig = [configForm copy];
NSArray* fields = [newConfig elementsForName:@"field"];
for (NSXMLElement *field in fields) {
NSString *var = [field attributeStringValueForName:@"var"];
if ([var isEqualToString:@"muc#roomconfig_persistentroom"]) {
[field removeChildAtIndex:0];
[field addChild:[NSXMLElement elementWithName:@"value" stringValue:@"1"]];
}
}
[sender configureRoomUsingOptions:newConfig];

(i'm not familiar with NSXMLElement, so maybe there is a better way to change the value)

Accepting chatroom invitation

For room invitations and declines, implement XMPPMUCDelegate and its methods -xmppMUC:didReceiveRoomInvitation: and -xmppMUC:didReceiveRoomInvitationDecline:.

To get the room JID, invoke [message from];

To join the room, instantiate an XMPPRoom and invoke -joinRoomUsingNickname:history:.

Then have your room delegate class implement XMPPRoomDelegate, and implement some of the delegate methods to handle receiving messages in the room.

It looks like there isn't at present a more automatic way to respond to invitations.

Update: The delegate callbacks now receive the room JID as a parameter, clarifying the semantics a bit.

- (void)xmppMUC:(XMPPMUC *)sender roomJID:(XMPPJID *) roomJID didReceiveInvitation:(XMPPMessage *)message;
- (void)xmppMUC:(XMPPMUC *)sender roomJID:(XMPPJID *) roomJID didReceiveInvitationDecline:(XMPPMessage *)message;


Related Topics



Leave a reply



Submit