#[clap(long = "remove-admin")]
remove_admin: Vec<String>,
+ #[clap(long)]
+ ban: Vec<String>,
+
+ #[clap(long)]
+ unban: Vec<String>,
+
#[clap(long = "reset-link")]
reset_link: bool,
#[allow(non_snake_case)] removeMember: Vec<String>,
admin: Vec<String>,
#[allow(non_snake_case)] removeAdmin: Vec<String>,
+ ban: Vec<String>,
+ unban: Vec<String>,
#[allow(non_snake_case)] resetLink: bool,
#[allow(non_snake_case)] link: Option<String>,
#[allow(non_snake_case)] setPermissionAddMember: Option<String>,
remove_member,
admin,
remove_admin,
+ ban,
+ unban,
reset_link,
link,
set_permission_add_member,
remove_member,
admin,
remove_admin,
+ ban,
+ unban,
reset_link,
link.map(|link| match link {
LinkState::Enabled => "enabled".to_owned(),
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
+{
+ "name":"org.whispersystems.signalservice.api.push.ServiceId",
+ "allDeclaredFields":true,
+ "queryAllDeclaredMethods":true,
+ "queryAllDeclaredConstructors":true
+},
{
"name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity",
"allDeclaredFields":true,
updateGroup.getRemoveAdmins() == null
? null
: context.getRecipientHelper().resolveRecipients(updateGroup.getRemoveAdmins()),
+ updateGroup.getBanMembers() == null
+ ? null
+ : context.getRecipientHelper().resolveRecipients(updateGroup.getBanMembers()),
+ updateGroup.getUnbanMembers() == null
+ ? null
+ : context.getRecipientHelper().resolveRecipients(updateGroup.getUnbanMembers()),
updateGroup.isResetGroupLink(),
updateGroup.getGroupLinkState(),
updateGroup.getAddMemberPermission(),
Set<RecipientAddress> pendingMembers,
Set<RecipientAddress> requestingMembers,
Set<RecipientAddress> adminMembers,
+ Set<RecipientAddress> bannedMembers,
boolean isBlocked,
int messageExpirationTimer,
GroupPermission permissionAddMember,
.stream()
.map(recipientStore::resolveRecipientAddress)
.collect(Collectors.toSet()),
+ groupInfo.getBannedMembers()
+ .stream()
+ .map(recipientStore::resolveRecipientAddress)
+ .collect(Collectors.toSet()),
groupInfo.isBlocked(),
groupInfo.getMessageExpirationTimer(),
groupInfo.getPermissionAddMember(),
private final Set<RecipientIdentifier.Single> removeMembers;
private final Set<RecipientIdentifier.Single> admins;
private final Set<RecipientIdentifier.Single> removeAdmins;
+ private final Set<RecipientIdentifier.Single> banMembers;
+ private final Set<RecipientIdentifier.Single> unbanMembers;
private final boolean resetGroupLink;
private final GroupLinkState groupLinkState;
private final GroupPermission addMemberPermission;
removeMembers = builder.removeMembers;
admins = builder.admins;
removeAdmins = builder.removeAdmins;
+ banMembers = builder.banMembers;
+ unbanMembers = builder.unbanMembers;
resetGroupLink = builder.resetGroupLink;
groupLinkState = builder.groupLinkState;
addMemberPermission = builder.addMemberPermission;
}
public static Builder newBuilder(final UpdateGroup copy) {
- Builder builder = new Builder();
- builder.name = copy.getName();
- builder.description = copy.getDescription();
- builder.members = copy.getMembers();
- builder.removeMembers = copy.getRemoveMembers();
- builder.admins = copy.getAdmins();
- builder.removeAdmins = copy.getRemoveAdmins();
- builder.resetGroupLink = copy.isResetGroupLink();
- builder.groupLinkState = copy.getGroupLinkState();
- builder.addMemberPermission = copy.getAddMemberPermission();
- builder.editDetailsPermission = copy.getEditDetailsPermission();
- builder.avatarFile = copy.getAvatarFile();
- builder.expirationTimer = copy.getExpirationTimer();
- builder.isAnnouncementGroup = copy.getIsAnnouncementGroup();
+ Builder builder = new Builder(copy.name,
+ copy.description,
+ copy.members,
+ copy.removeMembers,
+ copy.admins,
+ copy.removeAdmins,
+ copy.banMembers,
+ copy.unbanMembers,
+ copy.resetGroupLink,
+ copy.groupLinkState,
+ copy.addMemberPermission,
+ copy.editDetailsPermission,
+ copy.avatarFile,
+ copy.expirationTimer,
+ copy.isAnnouncementGroup);
return builder;
}
+ public static Builder newBuilder(
+ final String name,
+ final String description,
+ final Set<RecipientIdentifier.Single> members,
+ final Set<RecipientIdentifier.Single> removeMembers,
+ final Set<RecipientIdentifier.Single> admins,
+ final Set<RecipientIdentifier.Single> removeAdmins,
+ final Set<RecipientIdentifier.Single> banMembers,
+ final Set<RecipientIdentifier.Single> unbanMembers,
+ final boolean resetGroupLink,
+ final GroupLinkState groupLinkState,
+ final GroupPermission addMemberPermission,
+ final GroupPermission editDetailsPermission,
+ final File avatarFile,
+ final Integer expirationTimer,
+ final Boolean isAnnouncementGroup
+ ) {
+ return new Builder(name,
+ description,
+ members,
+ removeMembers,
+ admins,
+ removeAdmins,
+ banMembers,
+ unbanMembers,
+ resetGroupLink,
+ groupLinkState,
+ addMemberPermission,
+ editDetailsPermission,
+ avatarFile,
+ expirationTimer,
+ isAnnouncementGroup);
+ }
+
public String getName() {
return name;
}
return removeAdmins;
}
+ public Set<RecipientIdentifier.Single> getBanMembers() {
+ return banMembers;
+ }
+
+ public Set<RecipientIdentifier.Single> getUnbanMembers() {
+ return unbanMembers;
+ }
+
public boolean isResetGroupLink() {
return resetGroupLink;
}
private Set<RecipientIdentifier.Single> removeMembers;
private Set<RecipientIdentifier.Single> admins;
private Set<RecipientIdentifier.Single> removeAdmins;
+ private Set<RecipientIdentifier.Single> banMembers;
+ private Set<RecipientIdentifier.Single> unbanMembers;
private boolean resetGroupLink;
private GroupLinkState groupLinkState;
private GroupPermission addMemberPermission;
private Builder() {
}
+ private Builder(
+ final String name,
+ final String description,
+ final Set<RecipientIdentifier.Single> members,
+ final Set<RecipientIdentifier.Single> removeMembers,
+ final Set<RecipientIdentifier.Single> admins,
+ final Set<RecipientIdentifier.Single> removeAdmins,
+ final Set<RecipientIdentifier.Single> banMembers,
+ final Set<RecipientIdentifier.Single> unbanMembers,
+ final boolean resetGroupLink,
+ final GroupLinkState groupLinkState,
+ final GroupPermission addMemberPermission,
+ final GroupPermission editDetailsPermission,
+ final File avatarFile,
+ final Integer expirationTimer,
+ final Boolean isAnnouncementGroup
+ ) {
+ this.name = name;
+ this.description = description;
+ this.members = members;
+ this.removeMembers = removeMembers;
+ this.admins = admins;
+ this.removeAdmins = removeAdmins;
+ this.banMembers = banMembers;
+ this.unbanMembers = unbanMembers;
+ this.resetGroupLink = resetGroupLink;
+ this.groupLinkState = groupLinkState;
+ this.addMemberPermission = addMemberPermission;
+ this.editDetailsPermission = editDetailsPermission;
+ this.avatarFile = avatarFile;
+ this.expirationTimer = expirationTimer;
+ this.isAnnouncementGroup = isAnnouncementGroup;
+ }
+
public Builder withName(final String val) {
name = val;
return this;
return this;
}
+ public Builder withBanMembers(final Set<RecipientIdentifier.Single> val) {
+ banMembers = val;
+ return this;
+ }
+
+ public Builder withUnbanMembers(final Set<RecipientIdentifier.Single> val) {
+ unbanMembers = val;
+ return this;
+ }
+
public Builder withResetGroupLink(final boolean val) {
resetGroupLink = val;
return this;
final Set<RecipientId> removeMembers,
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
+ final Set<RecipientId> banMembers,
+ final Set<RecipientId> unbanMembers,
final boolean resetGroupLink,
final GroupLinkState groupLinkState,
final GroupPermission addMemberPermission,
removeMembers,
admins,
removeAdmins,
+ banMembers,
+ unbanMembers,
resetGroupLink,
groupLinkState,
addMemberPermission,
removeMembers,
admins,
removeAdmins,
+ banMembers,
+ unbanMembers,
resetGroupLink,
groupLinkState,
addMemberPermission,
final Set<RecipientId> removeMembers,
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
+ final Set<RecipientId> banMembers,
+ final Set<RecipientId> unbanMembers,
final boolean resetGroupLink,
final GroupLinkState groupLinkState,
final GroupPermission addMemberPermission,
if (removeMembers != null) {
var existingRemoveMembers = new HashSet<>(removeMembers);
+ if (banMembers != null) {
+ existingRemoveMembers.addAll(banMembers);
+ }
existingRemoveMembers.retainAll(group.getMembers());
+ if (members != null) {
+ existingRemoveMembers.removeAll(members);
+ }
existingRemoveMembers.remove(account.getSelfRecipientId());// self can be removed with sendQuitGroupMessage
if (existingRemoveMembers.size() > 0) {
var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers);
}
}
+ if (banMembers != null) {
+ final var newlyBannedMembers = new HashSet<>(banMembers);
+ newlyBannedMembers.removeAll(group.getBannedMembers());
+ if (newlyBannedMembers.size() > 0) {
+ var groupGroupChangePair = groupV2Helper.banMembers(group, newlyBannedMembers);
+ result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+ }
+ }
+
+ if (unbanMembers != null) {
+ var existingUnbanMembers = new HashSet<>(unbanMembers);
+ existingUnbanMembers.retainAll(group.getBannedMembers());
+ if (existingUnbanMembers.size() > 0) {
+ var groupGroupChangePair = groupV2Helper.unbanMembers(group, existingUnbanMembers);
+ result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+ }
+ }
+
if (resetGroupLink) {
var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group);
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
credentials,
(uuid, credential) -> new GroupCandidate(uuid, Optional.ofNullable(credential)))
.collect(Collectors.toSet());
+ final var bannedUuids = groupInfoV2.getBannedMembers()
+ .stream()
+ .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid())
+ .collect(Collectors.toSet());
final var aci = getSelfAci();
- final var change = groupOperations.createModifyGroupMembershipChange(candidates, Set.of(), aci.uuid());
+ final var change = groupOperations.createModifyGroupMembershipChange(candidates, bannedUuids, aci.uuid());
change.setSourceUuid(getSelfAci().toByteString());
return revokeInvites(groupInfoV2, memberUuids);
}
+ Pair<DecryptedGroup, GroupChange> banMembers(
+ GroupInfoV2 groupInfoV2, Set<RecipientId> block
+ ) throws IOException {
+ GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
+
+ final var uuids = block.stream()
+ .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid())
+ .collect(Collectors.toSet());
+
+ final var change = groupOperations.createBanUuidsChange(uuids,
+ false,
+ groupInfoV2.getGroup().getBannedMembersList());
+
+ change.setSourceUuid(getSelfAci().toByteString());
+
+ return commitChange(groupInfoV2, change);
+ }
+
+ Pair<DecryptedGroup, GroupChange> unbanMembers(
+ GroupInfoV2 groupInfoV2, Set<RecipientId> block
+ ) throws IOException {
+ GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
+
+ final var uuids = block.stream()
+ .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid())
+ .collect(Collectors.toSet());
+
+ final var change = groupOperations.createUnbanUuidsChange(uuids);
+
+ change.setSourceUuid(getSelfAci().toByteString());
+
+ return commitChange(groupInfoV2, change);
+ }
+
Pair<DecryptedGroup, GroupChange> resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize();
public abstract Set<RecipientId> getMembers();
+ public Set<RecipientId> getBannedMembers() {
+ return Set.of();
+ }
+
public Set<RecipientId> getPendingMembers() {
return Set.of();
}
.collect(Collectors.toSet());
}
+ @Override
+ public Set<RecipientId> getBannedMembers() {
+ if (this.group == null) {
+ return Set.of();
+ }
+ return group.getBannedMembersList()
+ .stream()
+ .map(m -> ServiceId.fromByteString(m.getUuid()))
+ .map(recipientResolver::resolveRecipient)
+ .collect(Collectors.toSet());
+ }
+
@Override
public Set<RecipientId> getPendingMembers() {
if (this.group == null) {
*--remove-admin* [MEMBER [MEMBER ...]]::
Specify one or more members to remove group admin privileges
+*--ban* [MEMBER [MEMBER ...]]::
+Specify one or more members to ban from joining the group.
+Banned members cannot join or request to join via a group link.
+
+*--unban* [MEMBER [MEMBER ...]]::
+Specify one or more members to remove from the ban list
+
*--reset-link*::
Reset group link and create new link password
@DBusProperty(name = "PendingMembers", type = String[].class, access = DBusProperty.Access.READ)
@DBusProperty(name = "RequestingMembers", type = String[].class, access = DBusProperty.Access.READ)
@DBusProperty(name = "Admins", type = String[].class, access = DBusProperty.Access.READ)
+ @DBusProperty(name = "Banned", type = String[].class, access = DBusProperty.Access.READ)
@DBusProperty(name = "PermissionAddMember", type = String.class)
@DBusProperty(name = "PermissionEditDetails", type = String.class)
@DBusProperty(name = "PermissionSendMessage", type = String.class)
final var groupInviteLink = group.groupInviteLinkUrl();
writer.println(
- "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
+ "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Banned: {} Message expiration: {} Link: {}",
group.groupId().toBase64(),
group.title(),
group.description(),
resolveMembers(group.pendingMembers()),
resolveMembers(group.requestingMembers()),
resolveMembers(group.adminMembers()),
+ resolveMembers(group.bannedMembers()),
group.messageExpirationTimer() == 0 ? "disabled" : group.messageExpirationTimer() + "s",
groupInviteLink == null ? '-' : groupInviteLink.getUrl());
} else {
resolveJsonMembers(group.pendingMembers()),
resolveJsonMembers(group.requestingMembers()),
resolveJsonMembers(group.adminMembers()),
+ resolveJsonMembers(group.bannedMembers()),
group.permissionAddMember().name(),
group.permissionEditDetails().name(),
group.permissionSendMessage().name(),
Set<JsonGroupMember> pendingMembers,
Set<JsonGroupMember> requestingMembers,
Set<JsonGroupMember> admins,
+ Set<JsonGroupMember> banned,
String permissionAddMember,
String permissionEditDetails,
String permissionSendMessage,
subparser.addArgument("--remove-admin")
.nargs("*")
.help("Specify one or more members to remove group admin privileges");
+ subparser.addArgument("--ban").nargs("*").help("Specify one or more members to ban from joining the group");
+ subparser.addArgument("--unban").nargs("*").help("Specify one or more members to remove from the ban list");
subparser.addArgument("--reset-link")
.action(Arguments.storeTrue())
var groupRemoveMembers = CommandUtil.getSingleRecipientIdentifiers(ns.getList("remove-member"), localNumber);
var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), localNumber);
var groupRemoveAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("remove-admin"), localNumber);
+ var groupBan = CommandUtil.getSingleRecipientIdentifiers(ns.getList("ban"), localNumber);
+ var groupUnban = CommandUtil.getSingleRecipientIdentifiers(ns.getList("unban"), localNumber);
var groupAvatar = ns.getString("avatar");
var groupResetLink = Boolean.TRUE.equals(ns.getBoolean("reset-link"));
var groupLinkState = getGroupLinkState(ns.getString("link"));
.withRemoveMembers(groupRemoveMembers)
.withAdmins(groupAdmins)
.withRemoveAdmins(groupRemoveAdmins)
+ .withBanMembers(groupBan)
+ .withUnbanMembers(groupUnban)
.withResetGroupLink(groupResetLink)
.withGroupLinkState(groupLinkState)
.withAddMemberPermission(groupAddMemberPermission)
((List<String>) group.get("Admins").getValue()).stream()
.map(m -> new RecipientAddress(null, m))
.collect(Collectors.toSet()),
+ ((List<String>) group.get("Banned").getValue()).stream()
+ .map(m -> new RecipientAddress(null, m))
+ .collect(Collectors.toSet()),
(boolean) group.get("IsBlocked").getValue(),
(int) group.get("MessageExpirationTimer").getValue(),
GroupPermission.valueOf((String) group.get("PermissionAddMember").getValue()),
() -> new Variant<>(getRecipientStrings(getGroup().requestingMembers()), "as")),
new DbusProperty<>("Admins",
() -> new Variant<>(getRecipientStrings(getGroup().adminMembers()), "as")),
+ new DbusProperty<>("Banned",
+ () -> new Variant<>(getRecipientStrings(getGroup().bannedMembers()), "as")),
new DbusProperty<>("PermissionAddMember",
() -> getGroup().permissionAddMember().name(),
this::setGroupPermissionAddMember),