-
-
[翻译]XMPP MUC学习
-
发表于: 2017-3-15 17:57 3385
-
最近在写一个基于XMPP协议的聊天程序,发现每次创建房间(也就是群)的时候,总是返回 “确认配置之前已锁住该房间,禁止进入” 的错误,就研究了下XMPP协议的MUC房间创建部分,结果越看越有兴趣,就尝试着翻译了一部分,第一次翻译,不太会组织语句,请见谅,如果有机会,我好想尝试把整个文档都翻译一遍。。。
XMPP
原文链接: https://xmpp.org/extensions/xep-0045.html
Owner Use Cases
每个房间必须拥有至少一个拥有者,这个拥有者(或者一个继承者)在这个房间中长寿的属性只要此房间存在(如拥有者不会因为退出一个长久的房间而失去所有权)。这个文档假设房间拥有者是创建房间的个体,和只有房间拥有者有权限去更改定义房间配置设置,如房间的类型。房间拥有者不光可以指定房间的类型(密码保护,仅限会员等等),而且还包括本文档的Requirements部分列出的房间的某些属性。另外,如果实现支持,一个拥有者也可以指定其他拥有者的JID。
为了给广泛的配置选项提供必要的灵活性。数据表(XEP-0004)被用来房间配置,它通过使用'http://jabber.org/protocol/muc'
的命名空间触发。如果一个实体不包含MUC命名空间在他们的加入或创建的请求中,服务会创建房间,而不是在房间创建之前等待通过数据表单进行配置(这样保证了还在用groupchat 1.0协议的兼容性);然而如果房间的加入或创建请求包含MUC扩展,服务就会在房间创建和解锁房间之前需要通过数据表单来进行配置。
注意:下面显示的配置选项列出了本文档需求部分中所有的功能和房间类型;然而,确切的配置选项和表单布局应因地制宜。并且,这些只是例子,并不旨在定义房间的唯一、必需的配置选项。一个具体的实现或部署获取选择提供额外的配置选项(清除级别、亵渎过滤器、支持的语言、消息日志记录等),这就是为什么要用'jabber:x:data'
协议的价值所在。
创建一个房间
General Considerations 总则
创建房间的特权可以限制为某些用户或者可以预留给服务的管理员。如果权限被限制并且一个用户试图创建房间,服务必须返回一个<not-allowed/>
错误:
服务通知用户没有能力去创建房间
<presence from='coven@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error by='coven@chat.shakespeare.lit' type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence>
如果权限没有被限制,服务会允许用户创建以下描述的房间。
大体上有两种房间
- 即时房间:可以直接访问和基于默认设置自动创建的房间
- 保留房间:在允许任何人进入之前需要创建者手动配置
创建和配置此类房间的工作流程如下:
用户发送
presence
出席到<room@service/nick>
并且通过由http://jabber.org/protocol/muc
命名空间限定的空的<x/>
子元素中的扩展出席信息表明他或她支持MUC协议。如果这个用户被允许创建房间并且此房间不存在,服务必须根据默认的一些配置创建这个房间,分配给发送这个请求的用户作为房间的拥有者,然后添加拥有者到这个房间但是目前还不允许任何人进入这个房间(effectively "locking" the room)。一开始的出席节(presence stanza)通过拥有者从房间中收到,出席节必需包括扩展出席信息用来表明用户作为一个所有者的状态并且承认房间已经被创建和正在等待配置。
如果这个初始的房间拥有者想要创建和配置一个保留的房间,这个拥有者必需请求一个配置表单通过发送一个
type
为get
的IQ
节给房间,它包含一个被'http://jabber.org/protocol/muc#owner'
命名空间限定的(qualified)空的<query/>
元素,然后完成步骤4和5.如果拥有者更喜欢去创建一个即时的房间,拥有者就必需发送一个被'http://jabber.org/protocol/muc#owner'
命名空间限定的query
元素,它包含一个被jabber:x:data
命名空间限制的type
是submit
的空<x/>
元素,然后直接跳到步骤6。如果房间拥有者请求一个配置表单,服务必须发送一个包含被
jabber:x:data
命名空间限定的配置表单的IQ-result
给拥有者。如果这里没有可用的配置操作,这个房间必须返回一个空的query
元素给房间拥有者。房间拥有者应该提供一个启动配置(starting configuration)给房间(或者接受默认的配置)通过发送一个包含完成的配置信息表单
IQ-set
。可供选择的,房间拥有者也可以cancel
这个配置过程。(一个安装启动也可以设置一个超市给初始的配置,如此使得如果房间拥有者在超时区间内没有配置房间,房间拥有者会被认为接收默认的配置或者取消配置的过程。)一旦服务接收到从房间拥有者完成好的配置表单(或者接收到一个即时房间的请求),服务必须解锁这个房间(允许其他用户进入房间)并且发送一个
type
为result
的IQ
给房间拥有者。如果服务接收到取消,它就会销毁这个房间。
这个协议的工作流程会显示在下面的例子中。
首先,用户必须发送出席信息到房间,包括一个由'http://jabber.org/protocol/muc'
命名空间描述的空的<x/>
元素(当试图进入房间时也会发送相同的节)。
例子 用户创建房间和表明支持Multi-User Chat
<presence from='crone1@shakespeare.lit/desktop' to='coven@chat.shakespeare.lit/firstwitch'> <x xmlns='http://jabber.org/protocol/muc'/></presence>
如果房间不存在,服务应该创建房间(受限于当前关于房间创建的策略),分配bare JID给作为拥有者的请求者,给房间添加拥有者,和通过发送一个出席节来承认成功创建房间:
服务承认房间创建
<presence from='coven@chat.shakespeare.lit/firstwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner' role='moderator'/> <status code='110'/> <status code='201'/> </x></presence>
接收到通知之后,这个房间就算已经创建了,房间的拥有者需要决定是否接受默认的房间配置(创建一个即时房间)或者用一些除了默认配置的配置来配置此房间(创建一个保留房间)。以下部分展示这个协议流完成这两种用例。
注意:如果出席节发送到一个不存在的房间,如上所示,出席节不包括被http://jabber.org/protocol/muc#owner
命名空间描述的<x/>
元素,服务应及时创建一个默认的房间(服务必须假设客户端支持 groupchat 1.0 而不是MUC,因此服务必须不能锁住房间同时等待房间创建者接收即时房间或配置保留房间)。
创建一个即时房间
如果这个初始的房间拥有者想要接收默认的房间配置(创建一个即时房间),房间拥有者必需拒绝初始配置表单,通过发送一个IQ-set
给<room@service>
,这个请求包含一个被'http://jabber.org/protocol/muc#owner'
命名空间描述的<query/>
元素,<query/>
元素包含一个被'jabber:x:data'
命名空间描述和type
值为submit
的空的<x/>
元素:
所有者请求即时房间
<iq from='crone1@shakespeare.lit/desktop' id='create1' to='coven@chat.shakespeare.lit' type='set'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='submit'/> </query> </iq>
服务必需打开房间,然后允许其他实体加入。
创建保留房间
如果初始的房间拥有者想要创建和配置一个保留房间,房间拥有者必需请求一个初始配置表单,通过发送一个IQ-get
给<room@service>
,它包含一个被'http://jabber.org/protocol/muc#owner'
命名空间描述的空的<query/>
元素。
拥有者请求配置表单
<iq from='crone1@shakespeare.lit/desktop' id='create1' to='coven@chat.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/muc#owner'/></iq>
如果房间不存在,服务必需返回一个初始房间配置表单给用户。(注意:下面例子显示一个有代表性的配置操作例子。注册用于房间的创建和配置一个完整的x:data
字段列表是由XMPP注册维护的;see the XMPP Registrar Considerations section of this document.)
服务发送配置表单
<iq from='coven@chat.shakespeare.lit' id='create1' to='crone1@shakespeare.lit/desktop' type='result'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='form'> <title>Configuration for "coven" Room</title> <instructions> Your room coven@macbeth has been created! The default configuration is as follows: - No logging - No moderation - Up to 20 occupants - No password required - No invitation required - Room is not persistent - Only admins may change the subject - Presence broadcasted for all users To accept the default configuration, click OK. To select a different configuration, please complete this form. </instructions> <field type='hidden' var='FORM_TYPE'> <value>http://jabber.org/protocol/muc#roomconfig</value> </field> <field label='Natural-Language Room Name' type='text-single' var='muc#roomconfig_roomname'/> <field label='Short Description of Room' type='text-single' var='muc#roomconfig_roomdesc'/> <field label='Natural Language for Room Discussions' type='text-single' var='muc#roomconfig_lang'/> <field label='Enable Public Logging?' type='boolean' var='muc#roomconfig_enablelogging'> <value>0</value> </field> <field label='Allow Occupants to Change Subject?' type='boolean' var='muc#roomconfig_changesubject'> <value>0</value> </field> <field label='Allow Occupants to Invite Others?' type='boolean' var='muc#roomconfig_allowinvites'> <value>0</value> </field> <field label='Who Can Send Private Messages?' type='list-single' var='muc#roomconfig_allowpm'> <value>anyone</value> <option label='Anyone'> <value>anyone</value> </option> <option label='Anyone with Voice'> <value>participants</value> </option> <option label='Moderators Only'> <value>moderators</value> </option> <option label='Nobody'> <value>none</value> </option> </field> <field label='Maximum Number of Occupants' type='list-single' var='muc#roomconfig_maxusers'> <value>20</value> <option label='10'><value>10</value></option> <option label='20'><value>20</value></option> <option label='30'><value>30</value></option> <option label='50'><value>50</value></option> <option label='100'><value>100</value></option> <option label='None'><value>none</value></option> </field> <field label='Roles for which Presence is Broadcasted' type='list-multi' var='muc#roomconfig_presencebroadcast'> <value>moderator</value> <value>participant</value> <value>visitor</value> <option label='Moderator'><value>moderator</value></option> <option label='Participant'><value>participant</value></option> <option label='Visitor'><value>visitor</value></option> </field> <field label='Roles and Affiliations that May Retrieve Member List' type='list-multi' var='muc#roomconfig_getmemberlist'> <value>moderator</value> <value>participant</value> <value>visitor</value> <option label='Moderator'><value>moderator</value></option> <option label='Participant'><value>participant</value></option> <option label='Visitor'><value>visitor</value></option> </field> <field label='Make Room Publicly Searchable?' type='boolean' var='muc#roomconfig_publicroom'> <value>1</value> </field> <field label='Make Room Persistent?' type='boolean' var='muc#roomconfig_persistentroom'> <value>0</value> </field> <field label='Make Room Moderated?' type='boolean' var='muc#roomconfig_moderatedroom'> <value>0</value> </field> <field label='Make Room Members-Only?' type='boolean' var='muc#roomconfig_membersonly'> <value>0</value> </field> <field label='Password Required to Enter?' type='boolean' var='muc#roomconfig_passwordprotectedroom'> <value>0</value> </field> <field type='fixed'> <value> If a password is required to enter this room, you must specify the password below. </value> </field> <field label='Password' type='text-private' var='muc#roomconfig_roomsecret'/> <field label='Who May Discover Real JIDs?' type='list-single' var='muc#roomconfig_whois'> <option label='Moderators Only'> <value>moderators</value> </option> <option label='Anyone'> <value>anyone</value> </option> </field> <field label='Maximum Number of History Messages Returned by Room' type='text-single' var='muc#maxhistoryfetch'> <value>50</value> </field> <field type='fixed'> <value> You may specify additional people who have admin status in the room. Please provide one Jabber ID per line. </value> </field> <field label='Room Admins' type='jid-multi' var='muc#roomconfig_roomadmins'/> <field type='fixed'> <value> You may specify additional owners for this room. Please provide one Jabber ID per line. </value> </field> <field label='Room Owners' type='jid-multi' var='muc#roomconfig_roomowners'/> </x> </query></iq>
注意:_whois
配置选项指明该房间是否是不匿名的(一个值为anyone
),半匿名(一个值为moderators
),或者完全匿名(一个值为none
)
如果这里没有有用的配置选项,服务必需返回一个空的query
元素给房间拥有者:
服务通知拥有者没有配置是合理的
<iq from='coven@chat.shakespeare.lit' id='create1' to='crone1@shakespeare.lit/desktop' type='result'> <query xmlns='http://jabber.org/protocol/muc#owner'/></iq>
房间拥有者应该填写好表单并且确认给服务。
拥有者确认配置表单
<iq from='crone1@shakespeare.lit/desktop' id='create2' to='coven@chat.shakespeare.lit' type='set'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE'> <value>http://jabber.org/protocol/muc#roomconfig</value> </field> <field var='muc#roomconfig_roomname'> <value>A Dark Cave</value> </field> <field var='muc#roomconfig_roomdesc'> <value>The place for all good witches!</value> </field> <field var='muc#roomconfig_enablelogging'> <value>0</value> </field> <field var='muc#roomconfig_changesubject'> <value>1</value> </field> <field var='muc#roomconfig_allowinvites'> <value>0</value> </field> <field var='muc#roomconfig_allowpm'> <value>anyone</value> </field> <field var='muc#roomconfig_maxusers'> <value>10</value> </field> <field var='muc#roomconfig_publicroom'> <value>0</value> </field> <field var='muc#roomconfig_persistentroom'> <value>0</value> </field> <field var='muc#roomconfig_moderatedroom'> <value>0</value> </field> <field var='muc#roomconfig_membersonly'> <value>0</value> </field> <field var='muc#roomconfig_passwordprotectedroom'> <value>1</value> </field> <field var='muc#roomconfig_roomsecret'> <value>cauldronburn</value> </field> <field var='muc#roomconfig_whois'> <value>moderators</value> </field> <field var='muc#maxhistoryfetch'> <value>50</value> </field> <field var='muc#roomconfig_roomadmins'> <value>wiccarocks@shakespeare.lit</value> <value>hecate@shakespeare.lit</value> </field> </x> </query></iq>
如果房间创建成功,服务必需通知这个新房间拥有者成功了:
服务通知新房间拥有者房间创建成功
<iq from='coven@chat.shakespeare.lit' id='create2' to='crone1@shakespeare.lit/desktop' type='result'/>
如果房间创建失败由于指定的房间配置选项违反一条或多条服务策略(例子:由于有密码保护的房间的密码为空),服务必需返回一个<not-acceptable/>
错误。
服务通知拥有者请求的配置选项不被接受
<iq from='coven@chat.shakespeare.lit' id='create2' to='crone1@shakespeare.lit/desktop' type='error'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
作为选择,房间拥有者也可能取消配置过程:
拥有者取消初始的配置
<iq from='crone1@shakespeare.lit/desktop' id='create2' to='coven@chat.shakespeare.lit' type='set'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='cancel'/> </query> </iq>
如果房间拥有者取消初始配置,服务必需销毁该房间,确保向房间拥有者发送unavailable
出席信息(参考销毁房间协议详细信息的用例)。
如果房间拥有者因为任何原因在确认表单之前变成unavailable
(如失去链接),服务会收到一个从拥有者到拥有者的<room@service/nick
的type
为unavailable
的出席信息节。服务必需销毁房间,并且发送一个从房间到房间拥有者的类型为unavailable
和包括一个<destroy/>
元素和原因(如果提供的话)的出席信息的节(原因在本文档Destroying a Room章节中定义)。
随后的房间配置
在指定房间的初始配置之后的任何时间,房间拥有者也学想要改变配置选项。为了启动此过程,房间拥有者从房间请求一个新的配置表单,通过给<room@service>
发送一个IQ-get
,它包含一个空的被'http://jabber.org/protocol/muc#owner'
命名空间描述的<query/>
元素。
拥有者请求配置表单
<iq from='crone1@shakespeare.lit/desktop' id='config1' to='coven@chat.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/muc#owner'/></iq>
如果这个from
中的地址和房间拥有者的bare JID不匹配的话,服务需要返回一个<forbidden/>
错误给发送者:
服务拒绝非拥有者的配置权限
<iq from='coven@chat.shakespeare.lit' id='configures' to='wiccarocks@shakespeare.lit/laptop' type='error'> <query xmlns='http://jabber.org/protocol/muc#owner'/> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
否则,服务需要给房间拥有者发送配置表单,并将当前的配置选项作为默认值:
服务发送配置表单给拥有者
<iq from='coven@chat.shakespeare.lit' id='config1' to='crone1@shakespeare.lit/desktop' type='result'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='form'> <title>Configuration for "coven" Room</title> <instructions> Complete this form to modify the configuration of your room. </instructions> <field type='hidden' var='FORM_TYPE'> <value>http://jabber.org/protocol/muc#roomconfig</value> </field> <field label='Natural-Language Room Name' type='text-single' var='muc#roomconfig_roomname'> <value>A Dark Cave</value> </field> <field label='Short Description of Room' type='text-single' var='muc#roomconfig_roomdesc'> <value>The place for all good witches!</value> </field> <field label='Enable Public Logging?' type='boolean' var='muc#roomconfig_enablelogging'> <value>0</value> </field> <field label='Allow Occupants to Change Subject?' type='boolean' var='muc#roomconfig_changesubject'> <value>0</value> </field> <field label='Allow Occupants to Invite Others?' type='boolean' var='muc#roomconfig_allowinvites'> <value>0</value> </field> <field label='Who Can Send Private Messages?' type='list-single' var='muc#roomconfig_allowpm'> <value>anyone</value> <option label='Anyone'> <value>anyone</value> </option> <option label='Anyone with Voice'> <value>participants</value> </option> <option label='Moderators Only'> <value>moderators</value> </option> <option label='Nobody'> <value>none</value> </option> </field> <field label='Maximum Number of Occupants' type='list-single' var='muc#roomconfig_maxusers'> <value>10</value> <option label='10'><value>10</value></option> <option label='20'><value>20</value></option> <option label='30'><value>30</value></option> <option label='50'><value>50</value></option> <option label='100'><value>100</value></option> <option label='None'><value>none</value></option> </field> <field label='Roles for which Presence is Broadcasted' type='list-multi' var='muc#roomconfig_presencebroadcast'> <value>moderator</value> <value>participant</value> <value>visitor</value> <option label='Moderator'><value>moderator</value></option> <option label='Participant'><value>participant</value></option> <option label='Visitor'><value>visitor</value></option> </field> <field label='Roles and Affiliations that May Retrieve Member List' type='list-multi' var='muc#roomconfig_getmemberlist'> <value>moderator</value> <value>participant</value> <value>visitor</value> <option label='Moderator'><value>moderator</value></option> <option label='Participant'><value>participant</value></option> <option label='Visitor'><value>visitor</value></option> </field> <field label='Make Room Publicly Searchable?' type='boolean' var='muc#roomconfig_publicroom'> <value>0</value> </field> <field label='Make Room Persistent?' type='boolean' var='muc#roomconfig_persistentroom'> <value>0</value> </field> <field label='Make Room Moderated?' type='boolean' var='muc#roomconfig_moderatedroom'> <value>0</value> </field> <field label='Make Room Members Only?' type='boolean' var='muc#roomconfig_membersonly'> <value>0</value> </field> <field label='Password Required for Entry?' type='boolean' var='muc#roomconfig_passwordprotectedroom'> <value>1</value> </field> <field type='fixed'> <value> If a password is required to enter this room, you must specify the password below. </value> </field> <field label='Password' type='text-private' var='muc#roomconfig_roomsecret'> <value>cauldronburn</value> </field> <field label='Who May Discover Real JIDs?' type='list-single' var='muc#roomconfig_whois'> <value>moderators</value> <option label='Moderators Only'> <value>moderators</value> </option> <option label='Anyone'> <value>anyone</value> </option> </field> <field label='Maximum Number of History Messages Returned by Room' type='text-single' var='muc#maxhistoryfetch'> <value>50</value> </field> <field type='fixed'> <value> You may specify additional people who have admin status in the room. Please provide one Jabber ID per line. </value> </field> <field label='Room Admins' type='jid-multi' var='muc#roomconfig_roomadmins'> <value>wiccarocks@shakespeare.lit</value> <value>hecate@shakespeare.lit</value> </field> <field type='fixed'> <value> You may specify additional owners for this room. Please provide one Jabber ID per line. </value> </field> <field label='Room Owners' type='jid-multi' var='muc#roomconfig_roomowners'/> </x> </query></iq>
如果这里没有可用的配置选项,服务需要返回一个空的query
元素给房间拥有者,如前面的用例所示。
房间拥有者然后提交具有更新信息的配置表单。
作为选择,房间拥有者也可以取消配置过程:
拥有者取消随后的配置
<iq from='crone1@shakespeare.lit/desktop' id='config2' to='coven@chat.shakespeare.lit' type='set'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='cancel'/> </query> </iq>
如果房间拥有者取消了随后的配置,服务必需离开当前房间的配置,因为它是房间拥有者发起后续配置之前的配置。
如果房间配置的改变的结果,使房间管理员失去了管理状态,这个房间必须给所有的住户个体发送更新的出席状态,通过一个被'http://jabber.org/protocol/muc#user'
命名空间描述的<x/>
元素和属性affiliation
值为member
或者none
、属性role
的值为participant
或visitor
的<item/>
子元素来表示状态的改变。
服务说明管理员关联的丢失
<presence from='coven@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='wiccarocks@shakespeare.lit/laptop' role='participant'/> </x></presence>[ ... ]
如果房间配置的改变的结果,使一个用户在房间收益到管理员状态,这个房间必须给所有的住户个体发送跟新的出席状态,通过一个被'http://jabber.org/protocol/muc#user'
命名空间描述的<x/>
元素和一个包含属性affiliation
值为admin
、属性role
值为moderator
的<item/>
子元素来表示状态的改变。
服务说明管理员关联收益给所有用户
<presence from='coven@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='admin' jid='wiccarocks@shakespeare.lit/laptop' role='moderator'/> </x></presence>[ ... ]
如果房间配置的改变的结果,使一个房间拥有者在该房间中失去拥有者状态,房间必须给所有的住户个体发送更新的出席信息,通过一个被'http://jabber.org/protocol/muc#user'
命名空间描述的<x/>
元素和一个包含属性affiliation
值为admin
、role
属性值设置为给定关联和房间类型适当的值(推荐moderator
)的<item/>
子元素来表示状态的改变。
服务说明拥有者关联的失去
<presence from='coven@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='admin' jid='wiccarocks@shakespeare.lit/laptop' role='moderator'/> </x></presence>[ ... ]
如果这里没有其他的拥有者,服务必须不允许拥有者撤回他们的拥有者状态;如果一个拥有者尝试这样去做的话,服务必须返回一个<conflict/>
错误给拥有者。然而,如果这里有其他拥有者的话,服务应该允许一个拥有者撤回他们的拥有者状态。
如果房间配置的改变的结果,使一个用户受益到拥有者状态,房间必须给所有的住户个体发送更新的出席信息,通过一个被'http://jabber.org/protocol/muc#user'
命名空间描述的<x/>
元素和一个包含属性affiliation
值为owner
、role
属性值设置为给定关联和房间类型适当的值(推荐moderator
)的<item/>
子元素来表示状态的改变。
服务说明拥有者关联的受益给所有人
<presence from='coven@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner' jid='wiccarocks@shakespeare.lit/laptop' role='moderator'/> </x></presence>[ ... ]
如果房间配置的改变的结果,使得房间类型被改变为仅限会员,而且房间里也没有会员,服务必需从房间中删除所有的非会员,和发送给那些用户以及所有剩余的居住着一个unavailable
的出席信息节,其中状态码为332
。
未完待续......
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)