UNPKG

@pazznetwork/ngx-chat

Version:

[![Build status](https://api.travis-ci.com/pazznetwork/ngx-chat.svg?branch=master)](https://travis-ci.com/pazznetwork/ngx-chat) [![Coverage](https://coveralls.io/repos/github/pazznetwork/ngx-chat/badge.svg?branch=master)](https://coveralls.io/github/pazzn

585 lines 112 kB
import { jid as parseJid, xml } from '@xmpp/client'; import { BehaviorSubject, Subject } from 'rxjs'; import { Direction } from '../../../../../core/message'; import { AbstractStanzaBuilder } from '../../abstract-stanza-builder'; import { StanzaBuilder } from '../../stanza-builder'; import { AbstractXmppPlugin } from '../abstract-xmpp-plugin'; import { MessageReceivedEvent } from '../message.plugin'; import { ServiceDiscoveryPlugin } from '../service-discovery.plugin'; import { Presence } from '../../../../../core/presence'; import { Room } from './room'; import { Affiliation } from './affiliation'; import { Role } from './role'; import { FORM_NS, getField, parseForm, serializeToSubmitForm, setFieldValue, } from '../../../../../core/form'; import { XmppResponseError } from '../../xmpp-response.error'; import { mucNs, mucAdminNs, mucOwnerNs, mucRoomConfigFormNs, mucUserNs } from './multi-user-chat-constants'; class QueryAffiliatedMemberListStanzaBuilder extends AbstractStanzaBuilder { constructor(roomJid, queryType, affiliationOrRole) { super(); this.roomJid = roomJid; this.queryType = queryType; this.affiliationOrRole = affiliationOrRole; } static build(roomJid, ...[queryType, affiliationOrRole]) { return new QueryAffiliatedMemberListStanzaBuilder(roomJid, queryType, affiliationOrRole).toStanza(); } toStanza() { return xml('iq', { type: 'get', to: this.roomJid.toString() }, xml('query', { xmlns: mucAdminNs }, xml('item', { [this.queryType]: this.affiliationOrRole }))); } } class QueryOccupantListStanzaBuilder extends AbstractStanzaBuilder { constructor(roomJid) { super(); this.roomJid = roomJid; } static build(roomJid) { return new QueryOccupantListStanzaBuilder(roomJid).toStanza(); } toStanza() { return xml('iq', { type: 'get', to: this.roomJid.toString() }, xml('query', { xmlns: ServiceDiscoveryPlugin.DISCO_ITEMS })); } } class ModifyAffiliationsOrRolesStanzaBuilder extends AbstractStanzaBuilder { constructor(roomJid, modifications) { super(); this.roomJid = roomJid; this.modifications = modifications; } static build(roomJid, modifications) { return new ModifyAffiliationsOrRolesStanzaBuilder(roomJid, modifications).toStanza(); } toStanza() { return xml('iq', { to: this.roomJid.toString(), type: 'set' }, xml('query', { xmlns: mucAdminNs }, ...this.modifications.map(modification => this.buildItem(modification)))); } buildItem(modification) { const { reason, ...attrs } = modification; return xml('item', 'userJid' in attrs ? { jid: attrs.userJid.toString(), affiliation: attrs.affiliation, } : { nick: attrs.nick, role: attrs.role, }, reason ? xml('reason', {}, reason) : null); } } /** * The MultiUserChatPlugin tries to provide the necessary functionality for a multi-user text chat, * whereby multiple XMPP users can exchange messages in the context of a room or channel, similar to Internet Relay Chat (IRC). * For more details see: * @see https://xmpp.org/extensions/xep-0045.html */ export class MultiUserChatPlugin extends AbstractXmppPlugin { constructor(xmppChatAdapter, logService, serviceDiscoveryPlugin) { super(); this.xmppChatAdapter = xmppChatAdapter; this.logService = logService; this.serviceDiscoveryPlugin = serviceDiscoveryPlugin; this.rooms$ = new BehaviorSubject([]); this.message$ = new Subject(); this.onInvitationSubject = new Subject(); this.onInvitation$ = this.onInvitationSubject.asObservable(); } onOffline() { this.rooms$.next([]); } handleStanza(stanza, archiveDelayElement) { if (this.isRoomPresenceStanza(stanza)) { return this.handleRoomPresenceStanza(stanza); } else if (this.isRoomMessageStanza(stanza)) { return this.handleRoomMessageStanza(stanza, archiveDelayElement); } else if (this.isRoomSubjectStanza(stanza)) { return this.handleRoomSubjectStanza(stanza, archiveDelayElement); } else if (this.isRoomInvitationStanza(stanza)) { return this.handleRoomInvitationStanza(stanza); } return false; } /** * Resolves if room could be configured as requested, rejects if room did exist or server did not accept configuration. */ async createRoom(options) { const { roomId, nick } = options; const service = await this.serviceDiscoveryPlugin.findService('conference', 'text'); const occupantJid = parseJid(roomId, service.jid, nick); const { presenceResponse, room } = await this.joinRoomInternal(occupantJid); const itemElement = presenceResponse.getChild('x').getChild('item'); if (itemElement.attrs.affiliation !== Affiliation.owner) { throw new Error('error creating room, user is not owner: ' + presenceResponse.toString()); } try { await this.applyRoomConfiguration(room.roomJid, options); room.name = options.name || undefined; this.rooms$.next(this.rooms$.getValue()); } catch (e) { this.logService.error('room configuration rejected', e); throw e; } return room; } async destroyRoom(roomJid) { let roomDestroyedResponse; try { roomDestroyedResponse = await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'set', to: roomJid.toString() }, xml('query', { xmlns: mucOwnerNs }, xml('destroy')))); } catch (e) { this.logService.error('error destroying room'); throw e; } // TODO: refactor so that we instead listen to the presence destroy stanza const allRoomsWithoutDestroyedRoom = this.rooms$.getValue().filter(room => !room.roomJid.equals(roomJid)); this.rooms$.next(allRoomsWithoutDestroyedRoom); return roomDestroyedResponse; } async joinRoom(occupantJid) { const { room } = await this.joinRoomInternal(occupantJid); this.rooms$.next(this.rooms$.getValue()); return room; } async getRoomInfo(roomJid) { const roomInfoResponse = await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', to: roomJid.toString() }, xml('query', { xmlns: ServiceDiscoveryPlugin.DISCO_INFO }))); const formEl = roomInfoResponse .getChild('query', ServiceDiscoveryPlugin.DISCO_INFO) ?.getChild('x', FORM_NS); if (formEl) { return parseForm(formEl); } return null; } async queryAllRooms() { const conferenceServer = await this.serviceDiscoveryPlugin.findService('conference', 'text'); const to = conferenceServer.jid.toString(); const result = []; let roomQueryResponse = await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', to }, xml('query', { xmlns: ServiceDiscoveryPlugin.DISCO_ITEMS }))); result.push(...this.extractRoomSummariesFromResponse(roomQueryResponse)); let resultSet = this.extractResultSetFromResponse(roomQueryResponse); while (resultSet && resultSet.getChild('last')) { const lastReceivedRoom = resultSet.getChildText('last'); roomQueryResponse = await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', to }, xml('query', { xmlns: ServiceDiscoveryPlugin.DISCO_ITEMS }, xml('set', { xmlns: 'http://jabber.org/protocol/rsm' }, xml('max', {}, '250'), xml('after', {}, lastReceivedRoom))))); result.push(...this.extractRoomSummariesFromResponse(roomQueryResponse)); resultSet = this.extractResultSetFromResponse(roomQueryResponse); } await Promise.all(result.map(async (summary) => { summary.roomInfo = await this.getRoomInfo(summary.jid); })); return result; } /** * Get all members of a MUC-Room with their affiliation to the room using the rooms fullJid * @param roomJid jid of the room */ async queryUserList(roomJid) { const memberQueryResponses = await Promise.all([ ...Object .values(Affiliation) .map(affiliation => this.xmppChatAdapter.chatConnectionService.sendIq(QueryAffiliatedMemberListStanzaBuilder.build(roomJid, 'affiliation', affiliation))), ...Object .values(Role) .map(role => this.xmppChatAdapter.chatConnectionService.sendIq(QueryAffiliatedMemberListStanzaBuilder.build(roomJid, 'role', role))), ]); const members = new Map(); for (const memberQueryResponse of memberQueryResponses) { memberQueryResponse .getChild('query', mucAdminNs) .getChildren('item') .forEach((memberItem) => { const userJid = parseJid(memberItem.attrs.jid); const roomUser = members.get(userJid.bare().toString()) || { userIdentifiers: [], affiliation: Affiliation.none, role: Role.none, }; roomUser.userIdentifiers.push({ userJid, nick: memberItem.attrs.nick && memberItem.attrs.nick, }); // tslint:disable no-unused-expression memberItem.attrs.affiliation && (roomUser.affiliation = memberItem.attrs.affiliation); memberItem.attrs.role && (roomUser.role = memberItem.attrs.role); // tslint:enable no-unused-expression members.set(userJid.bare().toString(), roomUser); }); } return [...members.values()]; } async modifyAffiliationOrRole(roomJid, modification) { return await this.xmppChatAdapter.chatConnectionService.sendIq(ModifyAffiliationsOrRolesStanzaBuilder.build(roomJid, [modification])); } async sendMessage(room, body, thread) { const from = this.xmppChatAdapter.chatConnectionService.userJid.toString(); const roomJid = room.roomJid.toString(); const roomMessageStanza = thread ? StanzaBuilder.buildRoomMessageWithThread(from, roomJid, body, thread) : StanzaBuilder.buildRoomMessageWithBody(from, roomJid, body); for (const plugin of this.xmppChatAdapter.plugins) { plugin.beforeSendMessage(roomMessageStanza); } return await this.xmppChatAdapter.chatConnectionService.send(roomMessageStanza); } /** * requests a configuration form for a room which returns with the default values * for an example see: * https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner */ async getRoomConfiguration(roomJid) { const configurationForm = await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', to: roomJid.toString() }, xml('query', { xmlns: mucOwnerNs }))); const formElement = configurationForm.getChild('query').getChild('x', FORM_NS); if (!formElement) { throw new Error('room not configurable'); } return parseForm(formElement); } async applyRoomConfiguration(roomJid, roomConfiguration) { const roomConfigForm = await this.getRoomConfiguration(roomJid); const formTypeField = getField(roomConfigForm, 'FORM_TYPE'); if (formTypeField.value !== mucRoomConfigFormNs) { throw new Error(`unexpected form type for room configuration form: formType=${formTypeField.value}, formTypeField=${JSON.stringify(formTypeField)}`); } if (typeof roomConfiguration.name === 'string') { setFieldValue(roomConfigForm, 'text-single', 'muc#roomconfig_roomname', roomConfiguration.name); } if (typeof roomConfiguration.nonAnonymous === 'boolean') { setFieldValue(roomConfigForm, 'list-single', 'muc#roomconfig_whois', roomConfiguration.nonAnonymous ? 'anyone' : 'moderators'); } if (typeof roomConfiguration.public === 'boolean') { setFieldValue(roomConfigForm, 'boolean', 'muc#roomconfig_publicroom', roomConfiguration.public); } if (typeof roomConfiguration.membersOnly === 'boolean') { setFieldValue(roomConfigForm, 'boolean', 'muc#roomconfig_membersonly', roomConfiguration.membersOnly); } if (typeof roomConfiguration.persistentRoom === 'boolean') { setFieldValue(roomConfigForm, 'boolean', 'muc#roomconfig_persistentroom', roomConfiguration.persistentRoom); } if (typeof roomConfiguration.allowSubscription === 'boolean') { setFieldValue(roomConfigForm, 'boolean', 'allow_subscription', roomConfiguration.allowSubscription); } await this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'set', to: roomJid.toString() }, xml('query', { xmlns: mucOwnerNs }, serializeToSubmitForm(roomConfigForm)))); } getRoomByJid(jid) { return this.rooms$.getValue().find(room => room.roomJid.equals(jid.bare())); } async banUser(occupantJid, roomJid, reason) { const userJid = await this.getUserJidByOccupantJid(occupantJid, roomJid); const response = await this.modifyAffiliationOrRole(roomJid, { userJid: userJid.bare(), affiliation: Affiliation.outcast, reason, }); this.logService.debug(`ban response ${response.toString()}`); return response; } async unbanUser(occupantJid, roomJid) { const userJid = await this.getUserJidByOccupantJid(occupantJid, roomJid); const banList = (await this.getBanList(roomJid)).map(bannedUser => bannedUser.userJid); this.logService.debug(`ban list: ${JSON.stringify(banList)}`); if (!banList.find(bannedJid => bannedJid.equals(userJid))) { throw new Error(`error unbanning: ${userJid} isn't on the ban list`); } const response = await this.modifyAffiliationOrRole(roomJid, { userJid, affiliation: Affiliation.none }); this.logService.debug('unban response: ' + response.toString()); return response; } async getBanList(roomJid) { const iq = xml('iq', { to: roomJid.toString(), type: 'get' }, xml('query', { xmlns: mucAdminNs }, xml('item', { affiliation: Affiliation.outcast }))); const response = await this.xmppChatAdapter.chatConnectionService.sendIq(iq); return response.getChild('query').getChildren('item').map(item => ({ userJid: parseJid(item.attrs.jid), affiliation: item.attrs.affiliation, reason: item.getChild('reason')?.getText(), })); } async inviteUser(inviteeJid, roomJid, invitationMessage) { const from = this.xmppChatAdapter.chatConnectionService.userJid.toString(); const stanza = xml('message', { to: roomJid.toString(), from }, xml('x', { xmlns: mucUserNs }, xml('invite', { to: inviteeJid.toString() }, invitationMessage ? xml('reason', {}, invitationMessage) : null))); await this.xmppChatAdapter.chatConnectionService.send(stanza); } async declineRoomInvite(occupantJid, reason) { const to = occupantJid.bare().toString(); const from = this.xmppChatAdapter.chatConnectionService.userJid.toString(); const stanza = xml('message', { to, from }, xml('x', { xmlns: mucUserNs }, xml('decline', { to }, reason ? xml('reason', {}, reason) : null))); await this.xmppChatAdapter.chatConnectionService.send(stanza); } async kickOccupant(nick, roomJid, reason) { const response = await this.modifyAffiliationOrRole(roomJid, { nick, role: Role.none, reason }); this.logService.debug(`kick occupant response: ${response.toString()}`); return response; } async changeUserNickname(newNick, roomJid) { const newRoomJid = parseJid(roomJid.toString()); newRoomJid.resource = newNick; const stanza = xml('presence', { to: newRoomJid.toString(), from: this.xmppChatAdapter.chatConnectionService.userJid.toString(), }); await this.xmppChatAdapter.chatConnectionService.send(stanza); } async leaveRoom(occupantJid, status) { const stanza = xml('presence', { to: occupantJid.toString(), from: this.xmppChatAdapter.chatConnectionService.userJid.toString(), type: Presence[Presence.unavailable], }, status ? xml('status', {}, status) : null); await this.xmppChatAdapter.chatConnectionService.send(stanza); this.logService.debug(`occupant left room: occupantJid=${occupantJid.toString()}`); } async changeRoomSubject(roomJid, subject) { const from = this.xmppChatAdapter.chatConnectionService.userJid.toString(); await this.xmppChatAdapter.chatConnectionService.send(xml('message', { to: roomJid.toString(), from, type: 'groupchat' }, xml('subject', {}, subject))); this.logService.debug(`room subject changed: roomJid=${roomJid.toString()}, new subject=${subject}`); } isRoomInvitationStanza(stanza) { let x; return stanza.name === 'message' && (x = stanza.getChild('x', mucUserNs)) != null && (x.getChild('invite') != null || x.getChild('decline') != null); } async grantMembership(userJid, roomJid, reason) { await this.setAffiliation(userJid, roomJid, Affiliation.member, reason); } async revokeMembership(userJid, roomJid, reason) { await this.setAffiliation(userJid, roomJid, Affiliation.none, reason); } async grantAdmin(userJid, roomJid, reason) { await this.setAffiliation(userJid, roomJid, Affiliation.admin, reason); } async revokeAdmin(userJid, roomJid, reason) { await this.setAffiliation(userJid, roomJid, Affiliation.member, reason); } async grantModeratorStatus(occupantNick, roomJid, reason) { await this.setRole(occupantNick, roomJid, Role.moderator, reason); } async revokeModeratorStatus(occupantNick, roomJid, reason) { await this.setRole(occupantNick, roomJid, Role.participant, reason); } isRoomPresenceStanza(stanza) { return stanza.name === 'presence' && (stanza.getChild('x', mucNs) || stanza.getChild('x', mucUserNs)) != null; } handleRoomPresenceStanza(stanza) { const stanzaType = stanza.attrs.type; if (stanzaType === 'error') { this.logService.error(stanza); throw new Error('error handling message, stanza: ' + stanza); } const occupantJid = parseJid(stanza.attrs.from); const roomJid = occupantJid.bare(); const xEl = stanza.getChild('x', mucUserNs); const itemEl = xEl.getChild('item'); const subjectOccupant = { occupantJid, affiliation: itemEl.attrs.affiliation, role: itemEl.attrs.role, nick: occupantJid.resource, }; const room = this.getOrCreateRoom(occupantJid); const statusCodes = xEl.getChildren('status').map(status => status.attrs.code); const isCurrenUser = statusCodes.includes('110'); if (stanzaType === 'unavailable') { const actor = itemEl.getChild('actor')?.attrs.nick; const reason = itemEl.getChild('reason')?.getText(); if (statusCodes.includes('333')) { if (isCurrenUser) { this.rooms$.next(this.rooms$.getValue().filter(r => !r.jidBare.equals(roomJid))); } return room.handleOccupantConnectionError(subjectOccupant, isCurrenUser); } else if (statusCodes.includes('307')) { if (isCurrenUser) { this.rooms$.next(this.rooms$.getValue().filter(r => !r.jidBare.equals(roomJid))); } return room.handleOccupantKicked(subjectOccupant, isCurrenUser, actor, reason); } else if (statusCodes.includes('301')) { if (isCurrenUser) { this.rooms$.next(this.rooms$.getValue().filter(r => !r.jidBare.equals(roomJid))); } return room.handleOccupantBanned(subjectOccupant, isCurrenUser, actor, reason); } else if (statusCodes.includes('303')) { const handled = room.handleOccupantChangedNick(subjectOccupant, isCurrenUser, xEl.getChild('item').attrs.nick); if (handled && isCurrenUser) { this.rooms$.next(this.rooms$.getValue()); } return handled; } else if (statusCodes.includes('321')) { if (isCurrenUser) { this.rooms$.next(this.rooms$.getValue().filter(r => !r.jidBare.equals(roomJid))); } return room.handleOccupantLostMembership(subjectOccupant, isCurrenUser); } else { if (isCurrenUser) { this.rooms$.next(this.rooms$.getValue().filter(r => !r.jidBare.equals(roomJid))); } return room.handleOccupantLeft(subjectOccupant, isCurrenUser); } } else if (!stanzaType) { if (room.hasOccupant(subjectOccupant.occupantJid)) { const oldOccupant = room.getOccupant(subjectOccupant.occupantJid); return room.handleOccupantModified(subjectOccupant, oldOccupant, isCurrenUser); } else { return room.handleOccupantJoined(subjectOccupant, isCurrenUser); } } return false; } getOrCreateRoom(roomJid) { roomJid = roomJid.bare(); let room = this.getRoomByJid(roomJid); if (!room) { room = new Room(roomJid, this.logService); this.rooms$.next([room, ...this.rooms$.getValue()]); } return room; } async joinRoomInternal(roomJid) { if (this.getRoomByJid(roomJid.bare())) { throw new Error('can not join room more than once: ' + roomJid.bare().toString()); } const userJid = this.xmppChatAdapter.chatConnectionService.userJid; const occupantJid = parseJid(roomJid.local, roomJid.domain, roomJid.resource || userJid.local); let roomInfo = null; try { roomInfo = await this.getRoomInfo(occupantJid.bare()); } catch (e) { if (!(e instanceof XmppResponseError) || e.errorCondition !== 'item-not-found') { throw e; } } try { const presenceResponse = await this.xmppChatAdapter.chatConnectionService.sendAwaitingResponse(xml('presence', { to: occupantJid.toString() }, xml('x', { xmlns: mucNs }))); this.handleRoomPresenceStanza(presenceResponse); const room = this.getOrCreateRoom(occupantJid.bare()); room.nick = occupantJid.resource; if (roomInfo) { room.name = getField(roomInfo, 'muc#roomconfig_roomname')?.value; room.description = getField(roomInfo, 'muc#roominfo_description')?.value || ''; } return { presenceResponse, room }; } catch (e) { this.logService.error('error joining room', e); throw e; } } extractRoomSummariesFromResponse(iq) { return iq .getChild('query', ServiceDiscoveryPlugin.DISCO_ITEMS) ?.getChildren('item') ?.reduce((acc, item) => { const { jid, name } = item.attrs; if (typeof jid === 'string' && typeof name === 'string') { acc.push({ jid: parseJid(jid), name, roomInfo: null, }); } return acc; }, []) || []; } extractResultSetFromResponse(iq) { return iq .getChild('query', ServiceDiscoveryPlugin.DISCO_ITEMS) ?.getChild('set', 'http://jabber.org/protocol/rsm'); } isRoomMessageStanza(stanza) { return stanza.name === 'message' && stanza.attrs.type === 'groupchat' && !!stanza.getChildText('body')?.trim(); } handleRoomMessageStanza(messageStanza, archiveDelayElement) { const delayElement = archiveDelayElement ?? messageStanza.getChild('delay'); const datetime = delayElement?.attrs.stamp ? new Date(delayElement.attrs.stamp) : new Date() /* TODO: replace with entity time plugin */; const from = parseJid(messageStanza.attrs.from); const room = this.getRoomByJid(from.bare()); if (!room) { // there are several reasons why we can receive a message for an unknown room: // - this is a message delivered via MAM/MUCSub but the room it was stored for // - is gone (was destroyed) // - user was banned from room // - room wasn't joined yet // - this is some kind of error on developer's side this.logService.warn(`received stanza for unknown room: ${from.bare().toString()}`); return false; } const message = { body: messageStanza.getChildText('body').trim(), datetime, id: messageStanza.attrs.id, from, direction: from.equals(room.occupantJid) ? Direction.out : Direction.in, delayed: !!delayElement, fromArchive: archiveDelayElement != null, }; const messageReceivedEvent = new MessageReceivedEvent(); for (const plugin of this.xmppChatAdapter.plugins) { plugin.afterReceiveMessage(message, messageStanza, messageReceivedEvent); } if (!messageReceivedEvent.discard) { room.addMessage(message); } if (!message.delayed) { this.message$.next(room); } return true; } isRoomSubjectStanza(stanza) { return stanza.name === 'message' && stanza.attrs.type === 'groupchat' && stanza.getChild('subject') != null && stanza.getChild('body') == null; } handleRoomSubjectStanza(stanza, archiveDelayElement) { const roomJid = parseJid(stanza.attrs.from).bare(); const room = this.getRoomByJid(roomJid); if (!room) { throw new Error(`unknown room trying to change room subject: roomJid=${roomJid.toString()}`); } // The archive only stores non-empty subjects. The current value of the subject is sent directly after entering a room by the room, // not the archive. // If a subject was first set, then unset, we would first receive the empty subject on room entry and then overwrite it with the // previous non-empty value from archive. This is why we want to always ignore subjects from archive. // This actually looks like a bug in MAM, it seems that MAM interprets messages with just subject in them as if they were chat // messages and not room metadata. This would explain why empty subjects are not stored. if (archiveDelayElement) { return true; } room.subject = stanza.getChild('subject').getText().trim(); this.rooms$.next(this.rooms$.getValue()); return true; } handleRoomInvitationStanza(stanza) { const xEl = stanza.getChild('x', mucUserNs); const invitationEl = xEl.getChild('invite') ?? xEl.getChild('decline'); this.onInvitationSubject.next({ type: invitationEl.name, roomJid: parseJid(stanza.attrs.from), roomPassword: xEl.getChild('password')?.getText(), from: parseJid(invitationEl.attrs.from), message: invitationEl.getChild('reason')?.getText(), }); return true; } async setAffiliation(occupantJid, roomJid, affiliation, reason) { const userJid = await this.getUserJidByOccupantJid(occupantJid, roomJid); return await this.modifyAffiliationOrRole(roomJid, { userJid, affiliation, reason }); } async setRole(occupantNick, roomJid, role, reason) { return await this.modifyAffiliationOrRole(roomJid, { nick: occupantNick, role, reason }); } async getUserJidByOccupantJid(occupantJid, roomJid) { const users = await this.queryUserList(roomJid); return users.find(roomUser => roomUser.userIdentifiers.find(ids => ids.nick === occupantJid.resource || ids.userJid.bare().equals(occupantJid.bare())))?.userIdentifiers?.[0].userJid; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGktdXNlci1jaGF0LnBsdWdpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvYWRhcHRlcnMveG1wcC9wbHVnaW5zL211bHRpLXVzZXItY2hhdC9tdWx0aS11c2VyLWNoYXQucGx1Z2luLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxHQUFHLElBQUksUUFBUSxFQUFFLEdBQUcsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUdwRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNoRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFHeEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDdEUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRXJELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQzlCLE9BQU8sRUFBRSxXQUFXLEVBQTJCLE1BQU0sZUFBZSxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxJQUFJLEVBQW9CLE1BQU0sUUFBUSxDQUFDO0FBS2hELE9BQU8sRUFFSCxPQUFPLEVBQ1AsUUFBUSxFQUNSLFNBQVMsRUFDVCxxQkFBcUIsRUFDckIsYUFBYSxHQUVoQixNQUFNLDBCQUEwQixDQUFDO0FBQ2xDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzlELE9BQU8sRUFB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