mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Add sendMessageRequestResponse command
This commit is contained in:
parent
2c0ad7feb7
commit
d84362eb0f
11 changed files with 210 additions and 0 deletions
|
@ -277,6 +277,14 @@ pub enum CliCommands {
|
||||||
#[arg(short = 's', long)]
|
#[arg(short = 's', long)]
|
||||||
stop: bool,
|
stop: bool,
|
||||||
},
|
},
|
||||||
|
SendMessageRequestResponse {
|
||||||
|
recipient: Vec<String>,
|
||||||
|
|
||||||
|
#[arg(short = 'g', long = "group-id")]
|
||||||
|
group_id: Vec<String>,
|
||||||
|
|
||||||
|
r#type: MessageRequestResponseType,
|
||||||
|
},
|
||||||
SetPin {
|
SetPin {
|
||||||
pin: String,
|
pin: String,
|
||||||
},
|
},
|
||||||
|
@ -447,3 +455,10 @@ pub enum GroupPermission {
|
||||||
EveryMember,
|
EveryMember,
|
||||||
OnlyAdmins,
|
OnlyAdmins,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Clone, Debug)]
|
||||||
|
#[value(rename_all = "kebab-case")]
|
||||||
|
pub enum MessageRequestResponseType {
|
||||||
|
Accept,
|
||||||
|
Delete,
|
||||||
|
}
|
||||||
|
|
|
@ -247,6 +247,15 @@ pub trait Rpc {
|
||||||
stop: bool,
|
stop: bool,
|
||||||
) -> Result<Value, ErrorObjectOwned>;
|
) -> Result<Value, ErrorObjectOwned>;
|
||||||
|
|
||||||
|
#[method(name = "sendMessageRequestResponse", param_kind = map)]
|
||||||
|
fn send_message_request_response(
|
||||||
|
&self,
|
||||||
|
account: Option<String>,
|
||||||
|
recipients: Vec<String>,
|
||||||
|
#[allow(non_snake_case)] groupIds: Vec<String>,
|
||||||
|
r#type: String,
|
||||||
|
) -> Result<Value, ErrorObjectOwned>;
|
||||||
|
|
||||||
#[method(name = "setPin", param_kind = map)]
|
#[method(name = "setPin", param_kind = map)]
|
||||||
fn set_pin(&self, account: Option<String>, pin: String) -> Result<Value, ErrorObjectOwned>;
|
fn set_pin(&self, account: Option<String>, pin: String) -> Result<Value, ErrorObjectOwned>;
|
||||||
|
|
||||||
|
|
|
@ -437,6 +437,23 @@ async fn handle_command(
|
||||||
.start_change_number(cli.account, number, voice, captcha)
|
.start_change_number(cli.account, number, voice, captcha)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
CliCommands::SendMessageRequestResponse {
|
||||||
|
recipient,
|
||||||
|
group_id,
|
||||||
|
r#type,
|
||||||
|
} => {
|
||||||
|
client
|
||||||
|
.send_message_request_response(
|
||||||
|
cli.account,
|
||||||
|
recipient,
|
||||||
|
group_id,
|
||||||
|
match r#type {
|
||||||
|
cli::MessageRequestResponseType::Accept => "accept".to_owned(),
|
||||||
|
cli::MessageRequestResponseType::Delete => "delete".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,10 @@ public interface Manager extends Closeable {
|
||||||
|
|
||||||
SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
|
SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
|
||||||
|
|
||||||
|
SendMessageResults sendMessageRequestResponse(
|
||||||
|
MessageEnvelope.Sync.MessageRequestResponse.Type type, Set<RecipientIdentifier> recipientIdentifiers
|
||||||
|
);
|
||||||
|
|
||||||
void hideRecipient(RecipientIdentifier.Single recipient);
|
void hideRecipient(RecipientIdentifier.Single recipient);
|
||||||
|
|
||||||
void deleteRecipient(RecipientIdentifier.Single recipient);
|
void deleteRecipient(RecipientIdentifier.Single recipient);
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import org.asamk.signal.manager.api.Contact;
|
import org.asamk.signal.manager.api.Contact;
|
||||||
import org.asamk.signal.manager.api.GroupId;
|
import org.asamk.signal.manager.api.GroupId;
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope;
|
||||||
import org.asamk.signal.manager.api.TrustLevel;
|
import org.asamk.signal.manager.api.TrustLevel;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
||||||
|
@ -28,6 +29,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
|
||||||
|
@ -365,6 +367,25 @@ public class SyncHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SendMessageResult sendMessageRequestResponse(
|
||||||
|
final MessageEnvelope.Sync.MessageRequestResponse.Type type, final GroupId groupId
|
||||||
|
) {
|
||||||
|
final var response = MessageRequestResponseMessage.forGroup(groupId.serialize(), localToRemoteType(type));
|
||||||
|
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendMessageResult sendMessageRequestResponse(
|
||||||
|
final MessageEnvelope.Sync.MessageRequestResponse.Type type, final RecipientId recipientId
|
||||||
|
) {
|
||||||
|
final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
|
||||||
|
if (address.serviceId().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var response = MessageRequestResponseMessage.forIndividual(address.serviceId().get(),
|
||||||
|
localToRemoteType(type));
|
||||||
|
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
private SendMessageResult requestSyncData(final SyncMessage.Request.Type type) {
|
private SendMessageResult requestSyncData(final SyncMessage.Request.Type type) {
|
||||||
var r = new SyncMessage.Request.Builder().type(type).build();
|
var r = new SyncMessage.Request.Builder().type(type).build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
|
@ -389,4 +410,17 @@ public class SyncHelper {
|
||||||
logger.warn("Failed to download avatar for contact {}, ignoring: {}", address, e.getMessage());
|
logger.warn("Failed to download avatar for contact {}, ignoring: {}", address, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MessageRequestResponseMessage.Type localToRemoteType(final MessageEnvelope.Sync.MessageRequestResponse.Type type) {
|
||||||
|
return switch (type) {
|
||||||
|
case UNKNOWN -> MessageRequestResponseMessage.Type.UNKNOWN;
|
||||||
|
case ACCEPT -> MessageRequestResponseMessage.Type.ACCEPT;
|
||||||
|
case DELETE -> MessageRequestResponseMessage.Type.DELETE;
|
||||||
|
case BLOCK -> MessageRequestResponseMessage.Type.BLOCK;
|
||||||
|
case BLOCK_AND_DELETE -> MessageRequestResponseMessage.Type.BLOCK_AND_DELETE;
|
||||||
|
case UNBLOCK_AND_ACCEPT -> MessageRequestResponseMessage.Type.UNBLOCK_AND_ACCEPT;
|
||||||
|
case SPAM -> MessageRequestResponseMessage.Type.SPAM;
|
||||||
|
case BLOCK_AND_SPAM -> MessageRequestResponseMessage.Type.BLOCK_AND_SPAM;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.asamk.signal.manager.api.InvalidUsernameException;
|
||||||
import org.asamk.signal.manager.api.LastGroupAdminException;
|
import org.asamk.signal.manager.api.LastGroupAdminException;
|
||||||
import org.asamk.signal.manager.api.Message;
|
import org.asamk.signal.manager.api.Message;
|
||||||
import org.asamk.signal.manager.api.MessageEnvelope;
|
import org.asamk.signal.manager.api.MessageEnvelope;
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope.Sync.MessageRequestResponse;
|
||||||
import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException;
|
import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException;
|
||||||
import org.asamk.signal.manager.api.NotAGroupMemberException;
|
import org.asamk.signal.manager.api.NotAGroupMemberException;
|
||||||
import org.asamk.signal.manager.api.NotPrimaryDeviceException;
|
import org.asamk.signal.manager.api.NotPrimaryDeviceException;
|
||||||
|
@ -874,6 +875,41 @@ public class ManagerImpl implements Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendMessageRequestResponse(
|
||||||
|
final MessageRequestResponse.Type type, final Set<RecipientIdentifier> recipients
|
||||||
|
) {
|
||||||
|
var results = new HashMap<RecipientIdentifier, List<SendMessageResult>>();
|
||||||
|
for (final var recipient : recipients) {
|
||||||
|
if (recipient instanceof RecipientIdentifier.NoteToSelf || (
|
||||||
|
recipient instanceof RecipientIdentifier.Single single
|
||||||
|
&& new RecipientAddress(single.toPartialRecipientAddress()).matches(account.getSelfRecipientAddress())
|
||||||
|
)) {
|
||||||
|
final var result = context.getSyncHelper()
|
||||||
|
.sendMessageRequestResponse(type, account.getSelfRecipientId());
|
||||||
|
if (result != null) {
|
||||||
|
results.put(recipient, List.of(toSendMessageResult(result)));
|
||||||
|
}
|
||||||
|
results.put(recipient, List.of(toSendMessageResult(result)));
|
||||||
|
} else if (recipient instanceof RecipientIdentifier.Single single) {
|
||||||
|
try {
|
||||||
|
final var recipientId = context.getRecipientHelper().resolveRecipient(single);
|
||||||
|
final var result = context.getSyncHelper().sendMessageRequestResponse(type, recipientId);
|
||||||
|
if (result != null) {
|
||||||
|
results.put(recipient, List.of(toSendMessageResult(result)));
|
||||||
|
}
|
||||||
|
} catch (UnregisteredRecipientException e) {
|
||||||
|
results.put(recipient,
|
||||||
|
List.of(SendMessageResult.unregisteredFailure(single.toPartialRecipientAddress())));
|
||||||
|
}
|
||||||
|
} else if (recipient instanceof RecipientIdentifier.Group group) {
|
||||||
|
final var result = context.getSyncHelper().sendMessageRequestResponse(type, group.groupId());
|
||||||
|
results.put(recipient, List.of(toSendMessageResult(result)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new SendMessageResults(0, results);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
||||||
final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient);
|
final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient);
|
||||||
|
@ -929,6 +965,10 @@ public class ManagerImpl implements Manager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
context.getContactHelper().setContactBlocked(recipientId, blocked);
|
context.getContactHelper().setContactBlocked(recipientId, blocked);
|
||||||
|
context.getSyncHelper()
|
||||||
|
.sendMessageRequestResponse(blocked
|
||||||
|
? MessageRequestResponse.Type.BLOCK
|
||||||
|
: MessageRequestResponse.Type.UNBLOCK_AND_ACCEPT, recipientId);
|
||||||
// if we don't have a common group with the blocked contact we need to rotate the profile key
|
// if we don't have a common group with the blocked contact we need to rotate the profile key
|
||||||
shouldRotateProfileKey = blocked && (
|
shouldRotateProfileKey = blocked && (
|
||||||
shouldRotateProfileKey || account.getGroupStore()
|
shouldRotateProfileKey || account.getGroupStore()
|
||||||
|
@ -957,6 +997,10 @@ public class ManagerImpl implements Manager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
context.getGroupHelper().setGroupBlocked(groupId, blocked);
|
context.getGroupHelper().setGroupBlocked(groupId, blocked);
|
||||||
|
context.getSyncHelper()
|
||||||
|
.sendMessageRequestResponse(blocked
|
||||||
|
? MessageRequestResponse.Type.BLOCK
|
||||||
|
: MessageRequestResponse.Type.UNBLOCK_AND_ACCEPT, groupId);
|
||||||
shouldRotateProfileKey = blocked;
|
shouldRotateProfileKey = blocked;
|
||||||
}
|
}
|
||||||
if (shouldRotateProfileKey) {
|
if (shouldRotateProfileKey) {
|
||||||
|
|
|
@ -346,6 +346,19 @@ Clear session state and send end session message.
|
||||||
*--edit-timestamp*::
|
*--edit-timestamp*::
|
||||||
Specify the timestamp of a previous message with the recipient or group to send an edited message.
|
Specify the timestamp of a previous message with the recipient or group to send an edited message.
|
||||||
|
|
||||||
|
=== sendMessageRequestResponse
|
||||||
|
|
||||||
|
Send response to a message request to linked devices.
|
||||||
|
|
||||||
|
RECIPIENT::
|
||||||
|
Specify the recipients’ phone number.
|
||||||
|
|
||||||
|
*-g* GROUP, *--group-id* GROUP::
|
||||||
|
Specify the recipient group ID in base64 encoding.
|
||||||
|
|
||||||
|
*--type* TYPE::
|
||||||
|
Type of message request response (accept, delete)
|
||||||
|
|
||||||
=== sendPaymentNotification
|
=== sendPaymentNotification
|
||||||
|
|
||||||
Send a payment notification.
|
Send a payment notification.
|
||||||
|
|
|
@ -39,6 +39,7 @@ public class Commands {
|
||||||
addCommand(new RemoteDeleteCommand());
|
addCommand(new RemoteDeleteCommand());
|
||||||
addCommand(new SendCommand());
|
addCommand(new SendCommand());
|
||||||
addCommand(new SendContactsCommand());
|
addCommand(new SendContactsCommand());
|
||||||
|
addCommand(new SendMessageRequestResponseCommand());
|
||||||
addCommand(new SendPaymentNotificationCommand());
|
addCommand(new SendPaymentNotificationCommand());
|
||||||
addCommand(new SendReactionCommand());
|
addCommand(new SendReactionCommand());
|
||||||
addCommand(new SendReceiptCommand());
|
addCommand(new SendReceiptCommand());
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.asamk.signal.commands;
|
||||||
|
|
||||||
|
enum MessageRequestResponseType {
|
||||||
|
ACCEPT {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "accept";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DELETE {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "delete";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package org.asamk.signal.commands;
|
||||||
|
|
||||||
|
import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope.Sync.MessageRequestResponse.Type;
|
||||||
|
import org.asamk.signal.output.OutputWriter;
|
||||||
|
import org.asamk.signal.util.CommandUtil;
|
||||||
|
|
||||||
|
public class SendMessageRequestResponseCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sendMessageRequestResponse";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help("Send response to a message request to linked devices.");
|
||||||
|
subparser.addArgument("-g", "--group-id", "--group").help("Specify the recipient group ID.").nargs("*");
|
||||||
|
subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*");
|
||||||
|
subparser.addArgument("-u", "--username").help("Specify the recipient username or username link.").nargs("*");
|
||||||
|
subparser.addArgument("--type")
|
||||||
|
.help("Type of message request response")
|
||||||
|
.type(Arguments.enumStringType(MessageRequestResponseType.class))
|
||||||
|
.required(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
|
) throws CommandException {
|
||||||
|
final var recipientStrings = ns.<String>getList("recipient");
|
||||||
|
final var groupIdStrings = ns.<String>getList("group-id");
|
||||||
|
final var usernameStrings = ns.<String>getList("username");
|
||||||
|
final var type = ns.<MessageRequestResponseType>get("type");
|
||||||
|
|
||||||
|
final var recipientIdentifiers = CommandUtil.getRecipientIdentifiers(m,
|
||||||
|
false,
|
||||||
|
recipientStrings,
|
||||||
|
groupIdStrings,
|
||||||
|
usernameStrings);
|
||||||
|
m.sendMessageRequestResponse(type == MessageRequestResponseType.ACCEPT ? Type.ACCEPT : Type.DELETE,
|
||||||
|
recipientIdentifiers);
|
||||||
|
}
|
||||||
|
}
|
|
@ -472,6 +472,14 @@ public class DbusManagerImpl implements Manager {
|
||||||
return new SendMessageResults(0, Map.of());
|
return new SendMessageResults(0, Map.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendMessageRequestResponse(
|
||||||
|
final MessageEnvelope.Sync.MessageRequestResponse.Type type,
|
||||||
|
final Set<RecipientIdentifier> recipientIdentifiers
|
||||||
|
) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue