feat(xep): Handle a user changing their nickname
This commit is contained in:
parent
007cdce53d
commit
93e9d6ca22
@ -56,3 +56,17 @@ class MemberLeftEvent extends XmppEvent {
|
|||||||
/// The nick of the user who left.
|
/// The nick of the user who left.
|
||||||
final String nick;
|
final String nick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Triggered when an entity changes their nick.
|
||||||
|
class MemberChangedNickEvent extends XmppEvent {
|
||||||
|
MemberChangedNickEvent(this.roomJid, this.oldNick, this.newNick);
|
||||||
|
|
||||||
|
/// The JID of the room.
|
||||||
|
final JID roomJid;
|
||||||
|
|
||||||
|
/// The original nick.
|
||||||
|
final String oldNick;
|
||||||
|
|
||||||
|
/// The new nick.
|
||||||
|
final String newNick;
|
||||||
|
}
|
||||||
|
2
packages/moxxmpp/lib/src/xeps/xep_0045/status_codes.dart
Normal file
2
packages/moxxmpp/lib/src/xeps/xep_0045/status_codes.dart
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
const selfPresenceStatus = '110';
|
||||||
|
const nicknameChangedStatus = '303';
|
@ -14,6 +14,7 @@ import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
|||||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0045/errors.dart';
|
import 'package:moxxmpp/src/xeps/xep_0045/errors.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0045/events.dart';
|
import 'package:moxxmpp/src/xeps/xep_0045/events.dart';
|
||||||
|
import 'package:moxxmpp/src/xeps/xep_0045/status_codes.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0045/types.dart';
|
import 'package:moxxmpp/src/xeps/xep_0045/types.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||||
import 'package:synchronized/synchronized.dart';
|
import 'package:synchronized/synchronized.dart';
|
||||||
@ -304,7 +305,7 @@ class MUCManager extends XmppManagerBase {
|
|||||||
item.attributes['affiliation']! as String,
|
item.attributes['affiliation']! as String,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (statuses.contains('110')) {
|
if (statuses.contains(selfPresenceStatus)) {
|
||||||
if (room.joined) {
|
if (room.joined) {
|
||||||
if (room.nick != from.resource ||
|
if (room.nick != from.resource ||
|
||||||
room.affiliation != affiliation ||
|
room.affiliation != affiliation ||
|
||||||
@ -335,19 +336,49 @@ class MUCManager extends XmppManagerBase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (presence.attributes['type'] == 'unavailable' && role == Role.none) {
|
if (presence.attributes['type'] == 'unavailable') {
|
||||||
// Cannot happen while joining, so we assume we are joined
|
if (role == Role.none) {
|
||||||
assert(
|
// Cannot happen while joining, so we assume we are joined
|
||||||
room.joined,
|
assert(
|
||||||
'Should not receive unavailable with role="none" while joining',
|
room.joined,
|
||||||
);
|
'Should not receive unavailable with role="none" while joining',
|
||||||
room.members.remove(from.resource);
|
);
|
||||||
getAttributes().sendEvent(
|
room.members.remove(from.resource);
|
||||||
MemberLeftEvent(
|
getAttributes().sendEvent(
|
||||||
bareFrom,
|
MemberLeftEvent(
|
||||||
from.resource,
|
bareFrom,
|
||||||
),
|
from.resource,
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
} else if (statuses.contains(nicknameChangedStatus)) {
|
||||||
|
assert(
|
||||||
|
room.joined,
|
||||||
|
'Should not receive nick change while joining',
|
||||||
|
);
|
||||||
|
final newNick = item.attributes['nick']! as String;
|
||||||
|
final member = RoomMember(
|
||||||
|
newNick,
|
||||||
|
Affiliation.fromString(
|
||||||
|
item.attributes['affiliation']! as String,
|
||||||
|
),
|
||||||
|
role,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the old member.
|
||||||
|
room.members.remove(from.resource);
|
||||||
|
|
||||||
|
// Add the "new" member".
|
||||||
|
room.members[newNick] = member;
|
||||||
|
|
||||||
|
// Trigger an event.
|
||||||
|
getAttributes().sendEvent(
|
||||||
|
MemberChangedNickEvent(
|
||||||
|
bareFrom,
|
||||||
|
from.resource,
|
||||||
|
newNick,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final member = RoomMember(
|
final member = RoomMember(
|
||||||
from.resource,
|
from.resource,
|
||||||
|
@ -658,7 +658,7 @@ void main() {
|
|||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Now a new user joins the room.
|
// Now a user leaves the room.
|
||||||
MemberLeftEvent? event;
|
MemberLeftEvent? event;
|
||||||
conn.asBroadcastStream().listen((e) {
|
conn.asBroadcastStream().listen((e) {
|
||||||
if (e is MemberLeftEvent) {
|
if (e is MemberLeftEvent) {
|
||||||
@ -689,4 +689,186 @@ void main() {
|
|||||||
expect(roomAfterLeave.members.length, 1);
|
expect(roomAfterLeave.members.length, 1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Test a user changing their nick name',
|
||||||
|
() async {
|
||||||
|
final fakeSocket = StubTCPSocket([
|
||||||
|
StringExpectation(
|
||||||
|
"<stream:stream xmlns='jabber:client' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' to='test.server' from='polynomdivision@test.server' xml:lang='en'>",
|
||||||
|
'''
|
||||||
|
<stream:stream
|
||||||
|
xmlns="jabber:client"
|
||||||
|
version="1.0"
|
||||||
|
xmlns:stream="http://etherx.jabber.org/streams"
|
||||||
|
from="test.server"
|
||||||
|
xml:lang="en">
|
||||||
|
<stream:features xmlns="http://etherx.jabber.org/streams">
|
||||||
|
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
|
||||||
|
<mechanism>PLAIN</mechanism>
|
||||||
|
</mechanisms>
|
||||||
|
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
||||||
|
<required/>
|
||||||
|
</bind>
|
||||||
|
</stream:features>''',
|
||||||
|
),
|
||||||
|
StringExpectation(
|
||||||
|
"<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AHBvbHlub21kaXZpc2lvbgBhYWFh</auth>",
|
||||||
|
'<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl" />',
|
||||||
|
),
|
||||||
|
StringExpectation(
|
||||||
|
"<stream:stream xmlns='jabber:client' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' to='test.server' from='polynomdivision@test.server' xml:lang='en'>",
|
||||||
|
'''
|
||||||
|
<stream:stream
|
||||||
|
xmlns="jabber:client"
|
||||||
|
version="1.0"
|
||||||
|
xmlns:stream="http://etherx.jabber.org/streams"
|
||||||
|
from="test.server"
|
||||||
|
xml:lang="en">
|
||||||
|
<stream:features xmlns="http://etherx.jabber.org/streams">
|
||||||
|
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
||||||
|
<required/>
|
||||||
|
</bind>
|
||||||
|
<session xmlns="urn:ietf:params:xml:ns:xmpp-session">
|
||||||
|
<optional/>
|
||||||
|
</session>
|
||||||
|
<csi xmlns="urn:xmpp:csi:0"/>
|
||||||
|
<sm xmlns="urn:xmpp:sm:3"/>
|
||||||
|
</stream:features>
|
||||||
|
''',
|
||||||
|
),
|
||||||
|
StanzaExpectation(
|
||||||
|
'<iq xmlns="jabber:client" type="set" id="a"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/></iq>',
|
||||||
|
'<iq xmlns="jabber:client" type="result" id="a"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>polynomdivision@test.server/MU29eEZn</jid></bind></iq>',
|
||||||
|
ignoreId: true,
|
||||||
|
),
|
||||||
|
StanzaExpectation(
|
||||||
|
'<presence to="channel@muc.example.org/test" xmlns="jabber:client"><x xmlns="http://jabber.org/protocol/muc"><history maxstanzas="0"/></x></presence>',
|
||||||
|
'',
|
||||||
|
ignoreId: true,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
final conn = XmppConnection(
|
||||||
|
TestingSleepReconnectionPolicy(1),
|
||||||
|
AlwaysConnectedConnectivityManager(),
|
||||||
|
ClientToServerNegotiator(),
|
||||||
|
fakeSocket,
|
||||||
|
)
|
||||||
|
..connectionSettings = ConnectionSettings(
|
||||||
|
jid: JID.fromString('polynomdivision@test.server'),
|
||||||
|
password: 'aaaa',
|
||||||
|
)
|
||||||
|
..setResource('test-resource', triggerEvent: false);
|
||||||
|
await conn.registerManagers([
|
||||||
|
DiscoManager([]),
|
||||||
|
MUCManager(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await conn.registerFeatureNegotiators([
|
||||||
|
SaslPlainNegotiator(),
|
||||||
|
ResourceBindingNegotiator(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await conn.connect(
|
||||||
|
waitUntilLogin: true,
|
||||||
|
shouldReconnect: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Join a groupchat
|
||||||
|
final roomJid = JID.fromString('channel@muc.example.org');
|
||||||
|
final joinResult = conn.getManagerById<MUCManager>(mucManager)!.joinRoom(
|
||||||
|
roomJid,
|
||||||
|
'test',
|
||||||
|
maxHistoryStanzas: 0,
|
||||||
|
);
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
fakeSocket
|
||||||
|
..injectRawXml(
|
||||||
|
'''
|
||||||
|
<presence
|
||||||
|
from='channel@muc.example.org/firstwitch'
|
||||||
|
id='3DCB0401-D7CF-4E31-BE05-EDF8D057BFBD'>
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<item affiliation='owner' role='moderator'/>
|
||||||
|
</x>
|
||||||
|
</presence>
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
..injectRawXml(
|
||||||
|
'''
|
||||||
|
<presence
|
||||||
|
from='channel@muc.example.org/secondwitch'
|
||||||
|
id='C2CD9EE3-8421-431E-854A-A2AD0CE2E23D'>
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<item affiliation='admin' role='moderator'/>
|
||||||
|
</x>
|
||||||
|
</presence>
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
..injectRawXml(
|
||||||
|
'''
|
||||||
|
<presence
|
||||||
|
from='channel@muc.example.org/test'
|
||||||
|
id='C2CD9EE3-8421-431E-854A-A2AD0CE2E23E'>
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<item affiliation='member' role='none'/>
|
||||||
|
<status code='110' />
|
||||||
|
</x>
|
||||||
|
</presence>
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
..injectRawXml(
|
||||||
|
'''
|
||||||
|
<message from="channel@muc.example.org" type="groupchat" xmlns="jabber:client">
|
||||||
|
<subject/>
|
||||||
|
</message>
|
||||||
|
''',
|
||||||
|
);
|
||||||
|
|
||||||
|
await joinResult;
|
||||||
|
final room = (await conn
|
||||||
|
.getManagerById<MUCManager>(mucManager)!
|
||||||
|
.getRoomState(roomJid))!;
|
||||||
|
expect(room.joined, true);
|
||||||
|
expect(
|
||||||
|
room.members.length,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now a new user changes their nick.
|
||||||
|
MemberChangedNickEvent? event;
|
||||||
|
conn.asBroadcastStream().listen((e) {
|
||||||
|
if (e is MemberChangedNickEvent) {
|
||||||
|
event = e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fakeSocket.injectRawXml(
|
||||||
|
'''
|
||||||
|
<presence
|
||||||
|
from='channel@muc.example.org/firstwitch'
|
||||||
|
id='3DCB0401-D7CF-4E31-BE05-EDF8D057BFBD'
|
||||||
|
type='unavailable'>
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<item affiliation='owner' role='moderator' nick='papatutuwawa'/>
|
||||||
|
<status code='303'/>
|
||||||
|
</x>
|
||||||
|
</presence>
|
||||||
|
''',
|
||||||
|
);
|
||||||
|
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 2));
|
||||||
|
expect(event != null, true);
|
||||||
|
expect(event!.oldNick, 'firstwitch');
|
||||||
|
expect(event!.newNick, 'papatutuwawa');
|
||||||
|
|
||||||
|
final roomAfterChange = (await conn
|
||||||
|
.getManagerById<MUCManager>(mucManager)!
|
||||||
|
.getRoomState(roomJid))!;
|
||||||
|
expect(roomAfterChange.members.length, 2);
|
||||||
|
expect(roomAfterChange.members['firstwitch'], null);
|
||||||
|
expect(roomAfterChange.members['papatutuwawa'] != null, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user