XMPP常用协议(三)

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u011619283/article/details/75975613

这一篇记录与群相关的XML协议格式。

这里需要注意,我们每次登录之后,都需要获取自己的群列表,并且主动加入到群房间里,否则就不能做群操作,也不能收到群消息。

12.获取自己加入的群列表

关于XMPP中群组的概念,需要注意的是:它分为公开群和非公开群。当我们获取自己加入的所有群时,公开群也会被搜索出来,所以,我们创建的群必须是非公开群。
关于xmpp群的相关协议可以查看 XMPP-0045(多人聊天协议)

获取自己加入的所有群列表,发出的XML:

<iq type="get" to="conference.duimy" id="3D3E0E43-78D7-41A0-A809-B121F9C37618">
  <query xmlns="http://jabber.org/protocol/disco#items"></query>
</iq>

群列表,是Openfire服务器发送另外一条IQ消息回来,但是id与我们请求获取群列表的id一样。

返回的结果XML:

<iq xmlns="jabber:client" type="result" id="3D3E0E43-78D7-41A0-A809-B121F9C37618" from="conference.duimy" to="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/disco#items">
    <item jid="[email protected]" name="群1"/>
    <item jid="[email protected]" name="群2"/>
  </query>
</iq>

iOS中的获取群列表,并解析返回的XML的代码段:

- (void)getJoinedGroupsWithCompletion:(void (^)(NSArray *aList, NSError *aError))aCompletionBlock
{
    XMPPJID *JID = [XMPPJID jidWithString:kGroup_Domain];
    XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:JID elementID:[HLCoreManager manager].stream.generateUUID];
    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_disco_item];
    [iq addChild:query];

    [[HLCoreManager manager] sendElement:iq completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if ([NSThread isMainThread]) {
                aCompletionBlock(nil, error);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    aCompletionBlock(nil, error);
                });
            }

            return ;
        }

        NSMutableArray *groups = [NSMutableArray array];
        NSXMLElement *query = [element elementForName:@"query" xmlns:kxmls_disco_item];
        NSArray *items = [query elementsForName:@"item"];
        for (NSXMLElement *item in items) {
            NSString *groupName = [item attributeStringValueForName:@"name"];
            NSString *groupjid = [item attributeStringValueForName:@"jid"];
            XMPPJID *groupJID = [XMPPJID jidWithString:groupjid];
            HLGroup *group = [[HLGroup alloc] init];
            [group setValue:groupJID.user forKey:@"groupId"];
            [group setValue:groupName forKey:@"subject"];
            [groups addObject:group];
            [[HLDBManager DBManager] insertGroup:group];
        }

        if ([NSThread isMainThread]) {
            aCompletionBlock([groups copy], nil);
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                aCompletionBlock([groups copy], nil);
            });
        }
    }];
}

而Android 里因为Smack已经做了一次封装,反而要简单的多:

    public void getJoinedGroupsFromServer(final DMValueCallBack<List<DMGroup>> callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String user = dmCore.getConnection().getUser();
                    Collection<HostedRoom> hostedRooms = multiUserChatManager.getHostedRooms(group_domain);

                    List<DMGroup> groups = new ArrayList<DMGroup>();
                    for (HostedRoom hostedRoom : hostedRooms) {
                        Log.e("房间",hostedRoom.toString());
                        DMGroup group = new DMGroup();
                        group.groupId = hostedRoom.getJid();
                        group.groupName = hostedRoom.getName();
                        groups.add(group);
                    }

                    if (callBack != null) {
                        callBack.onSuccess(groups);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    callBack.onError(-1, "获取失败");
                }
            }
        });
    }

13.创建一个群

创建一个群,简单来说,会有两步,第一步发出一个创建群的XML,服务器返回结果后,再配置群的参数。

发出创建群的XML:

<presence to="[email protected]/1001@duimy" id="F9D05C66-3B57-4029-A312-FD7459042545">
  <x xmlns="http://jabber.org/protocol/muc"></x>
</presence>

然后,会收到服务器返回的一条XML消息:

<presence xmlns="jabber:client" to="1001@duimy/iOS" id="F9D05C66-3B57-4029-A312-FD7459042545" from="[email protected]/1001@duimy">
  <x xmlns="http://jabber.org/protocol/muc#user">
    <item jid="1001@duimy/iOS" affiliation="owner" role="moderator"/>
    <status code="110"/>
    <status code="100"/>
    <status code="201"/>
  </x>
</presence>

然后再配置群信息,配置信息成功后,群就创建好了。

配置群信息里的参数可以看群参数的157,159章节。

有几个关键参数:
x-muc#roomconfig_reservednick 必须设置为0(即为false),不然不能邀请用户。muc#roomconfig_publicroom 必须为为0,非公开群。

然后,发出群配置的XML:

<iq type="set" to="[email protected]" id="3F94CC15-C128-49A2-A5FE-9B39847B9D4C" from="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/muc#owner">
    <x xmlns="jabber:x:data" type="submit">
      <field var="muc#roomconfig_publicroom" type="boolean">
        <value>0</value>
      </field>
      <field var="muc#roomconfig_roomtype" type="boolean">
        <value>0</value>
      </field>
      <field var="muc#roomconfig_persistentroom" type="boolean">
        <value>1</value>
      </field>
      <field var="muc#roomconfig_membersonly" type="boolean">
        <value>1</value>
      </field>
      <field var="muc#roomconfig_allowinvites" type="boolean">
        <value>1</value>
      </field>
      <field var="muc#roomconfig_registration" type="boolean">
        <value>1</value>
      </field>
      <field var="muc#roominfo_subject" type="text-single">
        <value>这是群主题</value>
      </field>
      <field var="muc#roomconfig_roomname" type="text-single">
        <value>群名称</value>
      </field>
      <field var="muc#roomconfig_roomdesc" type="text-single">
        <value>这是群描述信息</value>
      </field>
      <field var="muc#roomconfig_maxusers" type="text-single">
        <value>500</value>
      </field>
      <field var="x-muc#roomconfig_reservednick" type="boolean">
        <value>0</value>
      </field>
      <field var="muc#roomconfig_whois" type="boolean">
        <value>1</value>
      </field>
      <field var="muc#roomconfig_passwordprotectedroom" type="boolean">
        <value>0</value>
      </field>
    </x>
  </query>
</iq>

然后服务器会返回一条type 为result 的XML 消息:

<iq xmlns="jabber:client" type="result" id="3F94CC15-C128-49A2-A5FE-9B39847B9D4C" from="[email protected]" to="1001@duimy/iOS"></iq>

如果有错误,服务器会返回一条type为error 的XML 消息,类似这样:

<iq from='[email protected]'
    id='create2'
    to='[email protected]/desktop'
    type='error'>
  <error type='modify'>
    <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

在iOS中的代码段:

/*!
 *  创建群组
 *
 *  @param aSubject         群组名称
 *  @param aDescription     群组描述
 *  @param aInvitees        群组成员(不包括创建者自己)
 *  @param aMessage         邀请消息
 *  @param aCompletionBlock 完成的回调
 *
 */
- (void)createGroupWithSubject:(NSString *)aSubject
                   description:(NSString *)aDescription
                      invitees:(NSArray *)aInvitees
                       message:(NSString *)aMessage
                    completion:(void (^)(HLGroup *aGroup, NSError *aError))aCompletionBlock
{
    NSString *groupName = aSubject;
    if (groupName.length == 0) {
        groupName = @"未命名";
    }
    NSString *groupId = [NSString stringWithFormat:@"%lu",(unsigned long)[[NSDate date] hash]];
    NSString *roomId = [NSString stringWithFormat:@"%@@%@/%@",groupId,kGroup_Domain,[HLCoreManager manager].stream.myJID.bare];
    XMPPJID *roomJID = [XMPPJID jidWithString:roomId];

    XMPPPresence *presence = [XMPPPresence presenceWithType:nil to:roomJID];
    [presence addAttributeWithName:@"id" stringValue:[HLCoreManager manager].stream.generateUUID];
    NSXMLElement *x = [NSXMLElement elementWithName:@"x" xmlns:kxmls_muc];
    [presence addChild:x];

    // 1、发送创建群的presence
    [[HLCoreManager manager] sendElement:presence completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            aCompletionBlock(nil, error);
            return ;
        }

        XMPPPresence *presence = (XMPPPresence *)element;
        NSXMLElement *x = [presence elementForName:@"x" xmlns:kxmls_muc_user];
        BOOL creatRoom = NO;
        for (NSXMLElement *status in [x elementsForName:@"status"]) {
            int code = [status attributeIntValueForName:@"code"];
            if (code == 201) {
                creatRoom = YES;
                break;
            }
        }

        if (!creatRoom) {
            NSDictionary *userInfo = @{NSLocalizedDescriptionKey:@"创建群失败"};
            NSError *error = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:userInfo];
            aCompletionBlock(nil, error);
            return;
        }

        // 2.发送群配置信息的IQ
        XMPPIQ *roomIQ = [self roomConfigIqTo:presence.from subject:groupName description:aDescription type:@"0"];
        [[HLCoreManager manager] sendElement:roomIQ completion:^(XMPPElement *element, NSError *error) {
            if (error) {
                aCompletionBlock(nil, error);
                return ;
            }

            HLGroup *group = [[HLGroup alloc] init];
            [group setValue:groupId forKey:@"groupId"];
            [group setValue:groupName forKey:@"subject"];
            if (aDescription) {
                [group setValue:aDescription forKey:@"descp"];
            }
            [group setValue:[[HLCoreManager manager] currentUserName] forKey:@"owner"];
            aCompletionBlock(group, nil);

            // 3.邀请其他人进群
            for (NSString *user in aInvitees) {
                XMPPMessage *xmppMessage = [self inviteOne:user toGroup:groupId message:@"join us"];
                [self sendElement:xmppMessage completion:^(XMPPElement *element, NSError *error) {
                    HLLog(@"发送邀请");
                }];
            }

            aCompletionBlock(group, nil);
        }];
    }];
}

//IQ消息的配置
- (XMPPIQ*)roomConfigIqTo:(XMPPJID *)to subject:(NSString*)groupName description:(NSString *)desc type:(NSString *)type
{
    XMPPIQ *configIQ = [XMPPIQ iqWithType:@"set" to:[to bareJID] elementID:[HLCoreManager manager].stream.generateUUID];

    [configIQ addAttributeWithName:@"from" stringValue:[HLCoreManager manager].stream.myJID.full];

    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_muc_owner];

    NSXMLElement *x = [NSXMLElement elementWithName:@"x" xmlns:@"jabber:x:data"];
    [x addAttributeWithName:@"type" stringValue:@"submit"];
    [query addChild:x];

    [configIQ addChild:query];

    NSXMLElement *field = [self fieldWithVarName:@"muc#roomconfig_publicroom" value:@"0" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_roomtype" value:type type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_persistentroom" value:@"1" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_membersonly" value:@"1" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_allowinvites" value:@"1" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_registration" value:@"1" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roominfo_subject" value:@"这是群主题" type:@"text-single"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_roomname" value:groupName type:@"text-single"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_roomdesc" value:desc type:@"text-single"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_maxusers" value:@"500" type:@"text-single"];
    [x addChild:field];

    field = [self fieldWithVarName:@"x-muc#roomconfig_reservednick" value:@"0" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_whois" value:@"1" type:@"boolean"];
    [x addChild:field];

    field = [self fieldWithVarName:@"muc#roomconfig_passwordprotectedroom" value:@"0" type:@"boolean"];
    [x addChild:field];

    return configIQ;
}

而Android 中也更简单一些:

public void createGroup(final String groupName, final String descp, final String[] invitees, final String reason, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                String groupId = new Date().hashCode() + "@" + DMGroupManager.group_domain;
                MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                try {
                    String user = dmCore.getConnection().getUser();
                    group.create(user);
                    Form form = group.getConfigurationForm();
                    Form answerForm = form.createAnswerForm();
                    for(FormField field : form.getFields()) {
                        if (!FormField.Type.hidden.name().equals(field.getType()) && field.getVariable() != null) {
                            answerForm.setDefaultAnswer(field.getVariable());
                        }
                    }

                    answerForm.setAnswer("muc#roomconfig_publicroom", false);
                    answerForm.setAnswer("muc#roomconfig_persistentroom", true);
                    answerForm.setAnswer("muc#roomconfig_membersonly", true);
                    answerForm.setAnswer("muc#roomconfig_allowinvites", true);
                    answerForm.setAnswer("muc#roomconfig_roomname", groupName);
                    answerForm.setAnswer("muc#roomconfig_roomdesc", descp);
                    List<String> maxUsers = new ArrayList<String>();
                    maxUsers.add("500");
                    answerForm.setAnswer("muc#roomconfig_maxusers", maxUsers);
                    answerForm.setAnswer("x-muc#roomconfig_reservednick", false);
                    answerForm.setAnswer("muc#roomconfig_passwordprotectedroom", false);

                    group.sendConfigurationForm(answerForm);
                    group.join(user);

                    if (invitees != null && invitees.length > 0) {
                        //邀请加群
                        inviteMembers(groupId, invitees, reason, null);
                    }

                    if (callBack != null) {
                        callBack.onSuccess();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "创建失败");
                    }
                }
            }
        });
    }

14. 邀请用户加入群

发送的XML:

<message to="[email protected]" id="9316EDB0-DC08-45D3-8187-01B0CE5C6DAA">
  <x xmlns="http://jabber.org/protocol/muc#user">
    <invite to="1002@duimy" creatGroup="1">
      <reason>join us</reason>
    </invite>
  </x>
</message>

目前的群邀请,因为是管理员去邀请,所以群成员是默认同意的。

iOS 中的群邀请,我封装了两个方法:

//邀请多人
- (void)invite:(NSArray *)invitees toGroup:(NSString *)groupId message:(NSString *)message
{
    for (NSString *user in invitees) {
        XMPPMessage *xmppMessage = [self inviteOne:user toGroup:groupId message:message];
        [[HLCoreManager manager] sendElement:xmppMessage completion:^(XMPPElement *element, NSError *error) {
            HLLog(@"error--%@",error);
        }];
    }
}

// 邀请单人的消息
- (XMPPMessage *)inviteOne:(NSString *)user toGroup:(NSString *)groupId message:(NSString *)message
{
    XMPPJID *jid = [[HLCoreManager manager] jidWithUsername:user];

    NSString *elementID = [HLCoreManager manager].stream.generateUUID;
    NSString *tojid = [NSString stringWithFormat:@"%@@%@",groupId,kGroup_Domain];
    XMPPJID *toJID = [XMPPJID jidWithString:tojid];
    XMPPMessage *xmppMessage = [XMPPMessage messageWithType:nil to:toJID elementID:elementID];
    NSXMLElement *x = [NSXMLElement elementWithName:@"x" xmlns:kxmls_muc_user];

    NSXMLElement *inviteElement = [NSXMLElement elementWithName:@"invite"];
    [inviteElement addAttributeWithName:@"to" stringValue:jid.bare];
    [inviteElement addAttributeWithName:@"creatGroup" boolValue:YES];

    [inviteElement addChild:[NSXMLElement elementWithName:@"reason" stringValue:message]];

    [x addChild:inviteElement];
    [xmppMessage addChild:x];

    return xmppMessage;
}

Android 里的群邀请就简单多了:

    public void inviteMembers(final String groupId, final String[] members, final String reason, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                    for (String member : members) {
                        group.invite(member, reason);
                    }
                    if (callBack != null) {
                        callBack.onSuccess();
                    }
                } catch (SmackException.NotConnectedException e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "邀请失败");
                    }
                }
            }
        });
    }

收到的加群邀请XML:

<message xmlns="jabber:client" from="[email protected]" to="1002@duimy">
  <x xmlns="http://jabber.org/protocol/muc#user">
    <invite from="1001@duimy">
      <reason>join us</reason>
    </invite>
  </x>
  <x xmlns="jabber:x:conference" jid="[email protected]"></x>
  <delay xmlns="urn:xmpp:delay" from="duimy" stamp="2017-07-06T06:48:45.218Z"></delay>
</message>

因为我这边的逻辑是普通成员收到群邀请,自己加入群,不需要弹出来,让用户操作,所以直接执行下面这个操作即可。
iOS里的代码段:

- (XMPPPresence *)joinGroup:(XMPPJID *)groupJID since:(NSString *)sinceTime
{
    NSString *groupjid = [NSString stringWithFormat:@"%@/%@",groupJID.bare,[HLCoreManager manager].stream.myJID.bare];
    XMPPJID *fullJID = [XMPPJID jidWithString:groupjid];
    XMPPPresence *presence = [XMPPPresence presenceWithType:nil to:fullJID];
    [presence addAttributeWithName:@"id" stringValue:[HLCoreManager manager].stream.generateUUID];
    NSXMLElement* x = [NSXMLElement elementWithName:@"x" xmlns:kxmls_muc];

    if (sinceTime) {
        NSXMLElement *history = [NSXMLElement elementWithName:@"history"];
        [history addAttributeWithName:@"since" stringValue:sinceTime];
        [x addChild:history];
    }

    [presence addChild:x];

    [[HLCoreManager manager] sendElement:presence needResponse:YES completion:^(XMPPElement *element, NSError *error) {
        HLLog(@"加群");
    }];

    return presence;
}

Android 里是有一个监听的回调,超简单:

public void invitationReceived(XMPPConnection xmppConnection, MultiUserChat multiUserChat, String s, String s1, String s2, Message message) {
                // 目前是默认收到群邀请后,自动加入到群里
                // 上面的s 是发起群邀请的人的jid, s1 是邀请的备注信息
                String user = xmppConnection.getUser();
                try {
                    multiUserChat.join(user);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

15.获取某个群里的普通群成员

发送的XML:

<iq type="get" to="[email protected]" id="B1E022D0-7AEC-4FAD-910E-88D913691EBA">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="member"/>
  </query>
</iq>

然后会受到服务器发过来的另一个iq消息,id 也与我们发送的xml 的id一致:

<iq xmlns="jabber:client" type="result" id="B1E022D0-7AEC-4FAD-910E-88D913691EBA" from="[email protected]" to="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="member" jid="1003@duimy"/>
    <item affiliation="member" jid="1002@duimy"/>
  </query>
</iq>

这里我在iOS中封装了一个方法,传不同的参数,可以和获取普通群成员、群管理员、群主列表。

- (void)getUsersWithGroupId:(NSString *)groupId affiliation:(NSString *)affiliation completion:(void (^)(NSArray *members, NSError *aError))aCompletionBlock
{
    NSString *groupjid = [NSString stringWithFormat:@"%@@%@",groupId,kGroup_Domain];
    XMPPJID *groupJID = [XMPPJID jidWithString:groupjid];
    XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:groupJID elementID:[HLCoreManager manager].stream.generateUUID];

    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_muc_admin];
    NSXMLElement *item = [NSXMLElement elementWithName:@"item"];
    [item addAttributeWithName:@"affiliation" stringValue:affiliation];
    [query addChild:item];
    [iq addChild:query];

    [[HLCoreManager manager] sendElement:iq completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil, error);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil, error);
                    });
                }
            }
            return ;
        }
        XMPPIQ *iq = (XMPPIQ *)element;
        NSMutableArray *memberList = [NSMutableArray array];
        if ([iq isKindOfClass:[XMPPIQ class]] && [iq.type isEqualToString:@"result"]) {
            NSXMLElement *query = [iq elementForName:@"query"];
            NSArray *items = [query elementsForName:@"item"];
            for (NSXMLElement *item in items) {
                NSString *jid = [item attributeStringValueForName:@"jid"];
                XMPPJID *memberJID = [XMPPJID jidWithString:jid];
                NSString *affiliation = [item attributeStringValueForName:@"affiliation"];
                NSDictionary *memberDict = @{@"jid":memberJID.user, @"affiliation":affiliation};
                [memberList addObject:memberDict];
            }
        }
        if (aCompletionBlock) {
            if ([NSThread isMainThread]) {
                aCompletionBlock(memberList, nil);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    aCompletionBlock(memberList, nil);
                });
            }
        }
    }];
}

所以获取群成员就很简单了:

//获取群成员
- (void)getGroupMembersWithGroupId:(NSString *)groupId completion:(void (^)(NSArray *members, NSError *aError))aCompletionBlock
{
    [self getUsersWithGroupId:groupId affiliation:@"member" completion:aCompletionBlock];
}

//获取管理员列表
- (void)getGroupAdminsWithGroupId:(NSString *)groupId completion:(void (^)(NSArray *admins, NSError *aError))aCompletionBlock
{
    [self getUsersWithGroupId:groupId affiliation:@"admin" completion:aCompletionBlock];
}

//获取群主
- (void)getGroupOwnersWithGroupId:(NSString *)groupId completion:(void (^)(NSArray *owners, NSError *aError))aCompletionBlock
{
    [self getUsersWithGroupId:groupId affiliation:@"owner" completion:aCompletionBlock];
}

Android 里也是超级简单:

MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
//获取群普通成员
List<Affiliate> members = group.getMembers();
//获取管理员
List<Affiliate> admins = group.getAdmins();
//获取群主
List<Affiliate> owners = group.getOwners();

16.获取某个群里的主持人(即群主和管理员)

这个地方item 里的属性用的是role,没用岗位的原因是,role 为主持人的对应的岗位是admin和owner。
关于岗位、角色、权限的关系,可以看岗位、角色和权限第5小节

但是这里通过role 只能获取到当前在线的管理员和拥护者列表,所以有一些弊端。

发出的XML:

<iq type="get" to="[email protected]" id="753940CA-E0E1-4AC1-8FF9-2A0AB895670A">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item role="moderator"/>
  </query>
</iq>

虽然没啥用也还是列一下吧:

- (void)getGroupModeratorsWithGroupId:(NSString *)groupId completion:(void (^)(NSArray *moderators, NSError *aError))aCompletionBlock
{
    NSString *groupjid = [NSString stringWithFormat:@"%@@%@",groupId,kGroup_Domain];
    XMPPJID *groupJID = [XMPPJID jidWithString:groupjid];
    XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:groupJID elementID:[HLCoreManager manager].stream.generateUUID];

    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_muc_admin];
    NSXMLElement *item = [NSXMLElement elementWithName:@"item"];
    [item addAttributeWithName:@"role" stringValue:@"moderator"];
    [query addChild:item];
    [iq addChild:query];

    [[HLCoreManager manager] sendElement:iq completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil, error);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil, error);
                    });
                }
            }
            return ;
        }
        XMPPIQ *iq = (XMPPIQ *)element;
        NSMutableArray *moderatorList = [NSMutableArray array];
        if ([iq isKindOfClass:[XMPPIQ class]] && [iq.type isEqualToString:@"result"]) {
            NSXMLElement *query = [iq elementForName:@"query"];
            NSArray *items = [query elementsForName:@"item"];
            for (NSXMLElement *item in items) {
                NSString *jid = [item attributeStringValueForName:@"jid"];
                XMPPJID *moderatorJID = [XMPPJID jidWithString:jid];
                NSString *affiliation = [item attributeStringValueForName:@"affiliation"];
                NSDictionary *moderatorDict = @{@"jid":moderatorJID.user, @"affiliation":affiliation};
                [moderatorList addObject:moderatorDict];
            }
        }
        if (aCompletionBlock) {
            if ([NSThread isMainThread]) {
                aCompletionBlock(moderatorList, nil);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    aCompletionBlock(moderatorList, nil);
                });
            }
        }
    }];
}

返回的XML结果:

<iq xmlns="jabber:client" type="result" id="753940CA-E0E1-4AC1-8FF9-2A0AB895670A" from="[email protected]" to="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item role="moderator" jid="1001@duimy/iOS" nick="1001@duimy" affiliation="owner"/>
  </query>
</iq>

16.1 获取群里的管理员列表

发出的XML:

<iq type="get" to="[email protected]" id="34CD10BF-311E-404D-8191-E33B4D4FAFF7">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="admin"/>
  </query>
</iq>

收到的XML:

<iq xmlns="jabber:client" type="result" id="34CD10BF-311E-404D-8191-E33B4D4FAFF7" from="[email protected]" to="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="admin" jid="1003@duimy"/>
    <item affiliation="admin" jid="1002@duimy"/>
  </query>
</iq>

具体的例子,见15小节。

16.2 获取群里的拥有者列表

发出的XML:

<iq type="get" to="[email protected]" id="08AC753D-5010-4038-929A-6B1EB0F5A014">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="owner"/>
  </query>
</iq>

收到的XML:

<iq xmlns="jabber:client" type="result" id="08AC753D-5010-4038-929A-6B1EB0F5A014" from="[email protected]" to="1001@duimy/iOS">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="owner" jid="1001@duimy" role="moderator" nick="1001@duimy"/>
  </query>
</iq>

具体的例子,见15小节。

17.群主将某人设置为管理员

发出的XML:

<iq type="set" to="[email protected]" id="9A83498C-6B55-4AF7-9155-F4239581D17B" from="1001@duimy">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="admin" jid="1003@duimy"/>
  </query>
</iq>

如果授权成功会受到一条type 为result 的消息:

<iq xmlns="jabber:client" type="result" id="9A83498C-6B55-4AF7-9155-F4239581D17B" from="[email protected]" to="1001@duimy/iOS"/>

iOS 中的代码段:

- (void)grantAdmin:(NSArray *)aUsers inGroup:(NSString *)groupId completion:(void (^)(NSError *aError))aCompletionBlock
{
    XMPPIQ *iq = [self affiliationIQ:@"admin" invitees:aUsers inGroup:groupId];

    [[HLCoreManager manager] sendElement:iq needResponse:YES completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(error);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(error);
                    });
                }
            }
            return ;
        }

        XMPPIQ *iq = (XMPPIQ *)element;
        if ([iq.type isEqualToString:@"result"]) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil);
                    });
                }
            }
        } else {
            NSDictionary *dict = @{NSLocalizedDescriptionKey:@"设置管理员失败"};
            NSError *setError = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:dict];
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(setError);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(setError);
                    });
                }
            }
        }
    }];
}

Android 里的代码段:

    public void addGroupAdmin(final String groupId, final String user, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                try {
                    group.grantAdmin(user);
                    if (callBack != null) {
                        callBack.onSuccess();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "设置管理员失败啦");
                    }
                }
            }
        });
    }

当然了,如果失败(比如你没有授予管理员的权力),你也会收到一条type 为error 的XML 消息。

18. 撤销管理员权限

其实就是把岗位由admin改为 member

<iq type="set" to="[email protected]" id="FEF3F7B9-7CC5-460E-ADD0-9092204411D6" from="1001@duimy">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="member" jid="1003@duimy"/>
  </query>
</iq>

如果成功会受到如下的XML:

<iq xmlns="jabber:client" type="result" id="FEF3F7B9-7CC5-460E-ADD0-9092204411D6" from="[email protected]" to="1001@duimy/iOS"/>

iOS 中的代码段:

- (void)removeAdmin:(NSArray *)aUsers inGroup:(NSString *)groupId completion:(void (^)(NSError *aError))aCompletionBlock
{
    XMPPIQ *iq = [self affiliationIQ:@"member" invitees:aUsers inGroup:groupId];

    [[HLCoreManager manager] sendElement:iq needResponse:YES completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(error);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(error);
                    });
                }
            }
            return ;
        }

        XMPPIQ *iq = (XMPPIQ *)element;
        if ([iq.type isEqualToString:@"result"]) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil);
                    });
                }
            }
        } else {
            NSDictionary *dict = @{NSLocalizedDescriptionKey:@"移除管理员权限失败"};
            NSError *removeError = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:dict];
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(removeError);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(removeError);
                    });
                }
            }
        }
    }];
}

Android 代码段:

    public void revokeGroupAdmin(final String groupId, final String user, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                try {
                    group.revokeAdmin(user);
                    if (callBack != null) {
                        callBack.onSuccess();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "取消管理员失败啦");
                    }
                }
            }
        });
    }

如果失败,则会收到一条typeerror 的XML消息。

19.管理员及以上权限踢人

只有管理员和owner 才可以踢人,否则会收到type 为 error 的消息。

踢人,其实是将岗位由member改为 none

<iq type="set" to="[email protected]" id="D5516126-1D35-4499-8B07-5F17DADE974D" from="1001@duimy">
  <query xmlns="http://jabber.org/protocol/muc#admin">
    <item affiliation="none" jid="1003@duimy"/>
  </query>
</iq>

如果成功则会收到如下XML消息:

<iq xmlns="jabber:client" type="result" id="D5516126-1D35-4499-8B07-5F17DADE974D" from="[email protected]" to="1001@duimy/iOS"></iq>

iOS中的代码段:

- (void)kickMembers:(NSArray *)aUsers inGroup:(NSString *)groupId completion:(void (^)(NSError *aError))aCompletionBlock
{
    XMPPIQ *iq = [self affiliationIQ:@"none" invitees:aUsers inGroup:groupId];

    [[HLCoreManager manager] sendElement:iq needResponse:YES completion:^(XMPPElement *element, NSError *error) {
        if (error) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(error);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(error);
                    });
                }
            }
            return ;
        }

        XMPPIQ *iq = (XMPPIQ *)element;
        if ([iq.type isEqualToString:@"result"]) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil);
                    });
                }
            }
        } else {
            NSDictionary *dict = @{NSLocalizedDescriptionKey:@"踢人失败"};
            NSError *kickError = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:dict];
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(kickError);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(kickError);
                    });
                }
            }
        }
    }];
}

Android 中的代码段:

    public void removeMemberFromGroup(final String groupId, final String user, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                try {
                    group.revokeMembership(user);
                    if (callBack != null) {
                        callBack.onSuccess();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "踢人失败");
                    }
                }
            }
        });
    }

如果失败,则会受到一条type 为 error 的消息。

20. 群主解散群

只有群主才能发送解散群的消息,其他人发送解散群的消息,会受到一条type 为 error 的消息。

<iq type="set" to="[email protected]" id="39402B51-224E-46C9-877B-E7B96059C296">
  <query xmlns="http://jabber.org/protocol/muc#owner">
    <destroy/>
  </query>
</iq>

如果成功,则会收到如下的XML消息:

<iq xmlns="jabber:client" type="result" id="39402B51-224E-46C9-877B-E7B96059C296" from="[email protected]" to="1001@duimy/iOS"/>

如果失败,则会受到一条type 为 error 的消息。

iOS 中的代码段:

- (void)destroyGroup:(NSString *)aGroupId completion:(void (^)(NSError *aError))aCompletionBlock
{
    NSString *groupjid = [NSString stringWithFormat:@"%@@%@",aGroupId,kGroup_Domain];
    XMPPJID *groupJID = [XMPPJID jidWithString:groupjid];
    XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:groupJID elementID:[HLCoreManager manager].stream.generateUUID];
    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_muc_owner];
    NSXMLElement *destroy = [NSXMLElement elementWithName:@"destroy"];
    [query addChild:destroy];
    [iq addChild:query];
    [[HLCoreManager manager] sendElement:iq needResponse:YES completion:^(XMPPElement *element, NSError *error) {
        if (error && aCompletionBlock) {
            if ([NSThread isMainThread]) {
                aCompletionBlock(error);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    aCompletionBlock(error);
                });
            }

            return ;
        }
        XMPPIQ *iq = (XMPPIQ *)element;
        if ([iq.type isEqualToString:@"result"]) {
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(nil);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(nil);
                    });
                }
            }
        } else {
            NSDictionary *dict = @{NSLocalizedDescriptionKey:@"解散群失败"};
            NSError *destroyError = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:dict];
            if (aCompletionBlock) {
                if ([NSThread isMainThread]) {
                    aCompletionBlock(destroyError);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        aCompletionBlock(destroyError);
                    });
                }
            }
        }
    }];
}

而Android 中的代码段:

    public void destroyGroup(final String groupId, final DMCallBack callBack) {
        dmCore.execute(new Runnable() {
            @Override
            public void run() {
                MultiUserChat group = multiUserChatManager.getMultiUserChat(groupId);
                try {
                    group.destroy(null, null);
                    if (callBack != null) {
                        callBack.onSuccess();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    if (callBack != null) {
                        callBack.onError(-1, "解散群失败");
                    }
                }
            }
        });
    }

21. 退群

这个是重点,XMPP里怎么才能退出群呢?
退群,说白了,就是要将自己的岗位变更为none,可是变更岗位,只有管理员和拥有者才有权限,而群拥有者又不能退群,只能解散群。
所以这个退群功能,只有管理员可以执行。
而正确的做法应该是发送给群发送一条(IQ或者Message )消息,服务器接收到后,将自己从群里的岗位变更为none即可。
所以这个功能需要服务器来配合操作。

如果服务器端统一处理退群操作,那管理员退群也就没什么用了,其实管理员退群跟踢人的代码是一样的。

- (void)leaveGroup:(NSString *)aGroupId completion:(void (^)(NSError *aError))aCompletionBlock
{
    NSString *groupjid = [NSString stringWithFormat:@"%@@%@",aGroupId,kGroup_Domain];
    XMPPJID *groupJID = [XMPPJID jidWithString:groupjid];

    NSString *myjid = [HLCoreManager manager].stream.myJID.bare;
    NSString *elementID = [HLCoreManager manager].stream.generateUUID;
    XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:groupJID elementID:elementID];
    [iq addAttributeWithName:@"from" stringValue:myjid];

    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:kxmls_muc_admin];
    NSXMLElement *item = [NSXMLElement elementWithName:@"item"];
    [item addAttributeWithName:@"affiliation" stringValue:@"none"];
    [item addAttributeWithName:@"jid" stringValue:myjid];
    [query addChild:item];
    [iq addChild:query];
     [[HLCoreManager manager] sendElement:iq needResponse:YES completion:^(XMPPElement *element, NSError *error) {
         if (error) {
             if (aCompletionBlock) {
                 if ([NSThread isMainThread]) {
                     aCompletionBlock(error);
                 } else {
                     dispatch_async(dispatch_get_main_queue(), ^{
                         aCompletionBlock(error);
                     });
                 }
             }
             return ;
         }

         XMPPIQ *iq = (XMPPIQ *)element;
         if ([iq.type isEqualToString:@"result"]) {
             if (aCompletionBlock) {
                 if ([NSThread isMainThread]) {
                     aCompletionBlock(nil);
                 } else {
                     dispatch_async(dispatch_get_main_queue(), ^{
                         aCompletionBlock(nil);
                     });
                 }
             }
         } else {
             NSDictionary *dict = @{NSLocalizedDescriptionKey:@"退群失败"};
             NSError *kickError = [NSError errorWithDomain:@"HLGroupManager" code:0 userInfo:dict];
             if (aCompletionBlock) {
                 if ([NSThread isMainThread]) {
                     aCompletionBlock(kickError);
                 } else {
                     dispatch_async(dispatch_get_main_queue(), ^{
                         aCompletionBlock(kickError);
                     });
                 }
             }
         }
     }];
}

猜你喜欢

转载自blog.csdn.net/u011619283/article/details/75975613