docs: private Presence enter(MucEnterConfiguration conf) throws NotConnectedException, NoResponseException,
                      XMPPErrorException, InterruptedException, NotAMucServiceException {
          final DomainBareJid mucService = room.asDomainBareJid();
          if (!KNOWN_MUC_SERVICES.containsKey(mucService)) {
              if (multiUserChatManager.providesMucService(mucService)) {
                  KNOWN_MUC_SERVICES.put(mucService, null);
              } else {
                  throw new NotAMucServiceException(this);
              }
          }
          // We enter a room by sending a presence packet where the "to"
          // field is in the form "roomName@service/nickname"
          Presence joinPresence = conf.getJoinPresence(this);
  
          // Setup the messageListeners and presenceListeners *before* the join presence is send.
          connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
          StanzaFilter presenceFromRoomFilter = new AndFilter(fromRoomFilter,
                          StanzaTypeFilter.PRESENCE,
                          PossibleFromTypeFilter.ENTITY_FULL_JID);
          connection.addSyncStanzaListener(presenceListener, presenceFromRoomFilter);
          // @formatter:off
          connection.addSyncStanzaListener(subjectListener,
                          new AndFilter(fromRoomFilter,
                                        MessageWithSubjectFilter.INSTANCE,
                                        new NotFilter(MessageTypeFilter.ERROR),
                                        // According to XEP-0045 § 8.1 "A message with a <subject/> and a <body/> or a <subject/> and a <thread/> is a
                                        // legitimate message, but it SHALL NOT be interpreted as a subject change."
                                        new NotFilter(MessageWithBodiesFilter.INSTANCE),
                                        new NotFilter(MessageWithThreadFilter.INSTANCE))
                          );
          // @formatter:on
          connection.addSyncStanzaListener(declinesListener, new AndFilter(fromRoomFilter, DECLINE_FILTER));
          connection.addStanzaInterceptor(presenceInterceptor, new AndFilter(ToMatchesFilter.create(room),
                          StanzaTypeFilter.PRESENCE));
          messageCollector = connection.createStanzaCollector(fromRoomGroupchatFilter);
  
          // Wait for a presence packet back from the server.
          // @formatter:off
          StanzaFilter responseFilter = new AndFilter(StanzaTypeFilter.PRESENCE,
                          new OrFilter(
                              // We use a bare JID filter for positive responses, since the MUC service/room may rewrite the nickname.
                              new AndFilter(FromMatchesFilter.createBare(getRoom()), MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF),
                              // In case there is an error reply, we match on an error presence with the same stanza id and from the full
                              // JID we send the join presence to.
                              new AndFilter(FromMatchesFilter.createFull(joinPresence.getTo()), new StanzaIdFilter(joinPresence), PresenceTypeFilter.ERROR)
                          )
                      );
          // @formatter:on
          StanzaCollector presenceStanzaCollector = null;
          Presence presence;
          try {
              // This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC.
              StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence);
              StanzaCollector.Configuration presenceStanzaCollectorConfguration = StanzaCollector.newConfiguration().setCollectorToReset(
                              selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter);
              // This stanza collector is used to reset the timeout of the selfPresenceCollector.
              presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration);
              presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout());
          }
          catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
              // Ensure that all callbacks are removed if there is an exception
              removeConnectionCallbacks();
              throw e;
          }
          finally {
              if (presenceStanzaCollector != null) {
                  presenceStanzaCollector.cancel();
              }
          }
  
          // This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may
          // performed roomnick rewriting
          Resourcepart receivedNickname = presence.getFrom().getResourceOrThrow();
          setNickname(receivedNickname);
  
          joined = true;
  
          // Update the list of joined rooms
          multiUserChatManager.addJoinedRoom(room);
          return presence;
      }