public static JsGroup fromGroupVM(GroupVM groupVM, JsMessenger messenger) {
    int online = groupVM.getPresence().get();
    String presence = messenger.getFormatter().formatGroupMembers(groupVM.getMembersCount());
    if (online > 0) {
      presence += ", " + messenger.getFormatter().formatGroupOnline(online);
    }
    String fileUrl = null;
    String bigFileUrl = null;
    Avatar avatar = groupVM.getAvatar().get();
    if (avatar != null) {
      if (avatar.getSmallImage() != null) {
        fileUrl = messenger.getFileUrl(avatar.getSmallImage().getFileReference());
      }
      if (avatar.getLargeImage() != null) {
        bigFileUrl = messenger.getFileUrl(avatar.getLargeImage().getFileReference());
      }
    }

    ArrayList<JsGroupMember> convertedMembers = new ArrayList<JsGroupMember>();
    HashSet<GroupMember> groupMembers = groupVM.getMembers().get();
    GroupMember[] members = groupMembers.toArray(new GroupMember[groupMembers.size()]);
    for (GroupMember g : members) {
      JsPeerInfo peerInfo = messenger.buildPeerInfo(Peer.user(g.getUid()));
      Log.d("JsGroup", "PeerInfo: " + peerInfo);
      convertedMembers.add(
          JsGroupMember.create(
              peerInfo,
              g.isAdministrator(),
              g.getInviterUid() == messenger.myUid()
                  || groupVM.getCreatorId() == messenger.myUid()));
    }
    Collections.sort(
        convertedMembers,
        new Comparator<JsGroupMember>() {
          @Override
          public int compare(JsGroupMember o1, JsGroupMember o2) {
            return o1.getPeerInfo().getTitle().compareToIgnoreCase(o2.getPeerInfo().getTitle());
          }
        });
    JsArray<JsGroupMember> jsMembers = JsArray.createArray().cast();
    for (JsGroupMember member : convertedMembers) {
      jsMembers.push(member);
    }
    return create(
        groupVM.getId(),
        groupVM.getName().get(),
        fileUrl,
        bigFileUrl,
        Placeholders.getPlaceholder(groupVM.getId()),
        groupVM.getCreatorId(),
        presence,
        jsMembers);
  }
    @Override
    public void bind(GroupMember data, int position, Context context) {
      UserVM user = users().get(data.getUid());

      avatarView.bind(user);

      userName.setText(user.getName().get());

      if (data.isAdministrator()) {
        admin.setVisibility(View.VISIBLE);
      } else {
        admin.setVisibility(View.GONE);
      }
    }
  public void doSendText(
      @NotNull Peer peer,
      @NotNull String text,
      @Nullable ArrayList<Integer> mentions, /*Ignored*/
      @Nullable String markDownText,
      boolean autoDetect) {

    text = text.trim();

    long rid = RandomUtils.nextRid();
    long date = createPendingDate();
    long sortDate = date + 365 * 24 * 60 * 60 * 1000L;

    if (autoDetect) {
      mentions = new ArrayList<>();
      if (peer.getPeerType() == PeerType.GROUP) {
        Group group = getGroup(peer.getPeerId());
        String lowText = text.toLowerCase();
        if (group.getMembers() != null) {
          for (GroupMember member : group.getMembers()) {
            User user = getUser(member.getUid());
            if (user.getNick() != null) {
              String nick = "@" + user.getNick().toLowerCase();
              // TODO: Better filtering
              if (lowText.contains(nick + ":")
                  || lowText.contains(nick + " ")
                  || lowText.contains(" " + nick)
                  || lowText.endsWith(nick)
                  || lowText.equals(nick)) {
                mentions.add(user.getUid());
              }
            }
          }
        }
      }
    }

    TextContent content = TextContent.create(text, null, mentions);

    Message message = new Message(rid, sortDate, date, myUid(), MessageState.PENDING, content);

    context().getMessagesModule().getRouter().onOutgoingMessage(peer, message);

    pendingMessages.getPendingMessages().add(new PendingMessage(peer, rid, content));
    savePending();

    performSendContent(peer, rid, content);
  }