mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-09-19 05:58:57 +00:00
Add support for receiving and sending polls
This commit is contained in:
parent
91701f609a
commit
feaee2bfe1
@ -1093,7 +1093,7 @@
|
|||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
"allDeclaredMethods":true,
|
"allDeclaredMethods":true,
|
||||||
"allDeclaredConstructors":true,
|
"allDeclaredConstructors":true,
|
||||||
"methods":[{"name":"attachments","parameterTypes":[] }, {"name":"contacts","parameterTypes":[] }, {"name":"expiresInSeconds","parameterTypes":[] }, {"name":"groupInfo","parameterTypes":[] }, {"name":"isExpirationUpdate","parameterTypes":[] }, {"name":"mentions","parameterTypes":[] }, {"name":"message","parameterTypes":[] }, {"name":"payment","parameterTypes":[] }, {"name":"previews","parameterTypes":[] }, {"name":"quote","parameterTypes":[] }, {"name":"reaction","parameterTypes":[] }, {"name":"remoteDelete","parameterTypes":[] }, {"name":"sticker","parameterTypes":[] }, {"name":"storyContext","parameterTypes":[] }, {"name":"textStyles","parameterTypes":[] }, {"name":"timestamp","parameterTypes":[] }, {"name":"viewOnce","parameterTypes":[] }]
|
"methods":[{"name":"attachments","parameterTypes":[] }, {"name":"contacts","parameterTypes":[] }, {"name":"expiresInSeconds","parameterTypes":[] }, {"name":"groupInfo","parameterTypes":[] }, {"name":"isExpirationUpdate","parameterTypes":[] }, {"name":"mentions","parameterTypes":[] }, {"name":"message","parameterTypes":[] }, {"name":"payment","parameterTypes":[] }, {"name":"pollCreate","parameterTypes":[] }, {"name":"pollTerminate","parameterTypes":[] }, {"name":"pollVote","parameterTypes":[] }, {"name":"previews","parameterTypes":[] }, {"name":"quote","parameterTypes":[] }, {"name":"reaction","parameterTypes":[] }, {"name":"remoteDelete","parameterTypes":[] }, {"name":"sticker","parameterTypes":[] }, {"name":"storyContext","parameterTypes":[] }, {"name":"textStyles","parameterTypes":[] }, {"name":"timestamp","parameterTypes":[] }, {"name":"viewOnce","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name":"org.asamk.signal.json.JsonEditMessage",
|
"name":"org.asamk.signal.json.JsonEditMessage",
|
||||||
@ -1137,6 +1137,27 @@
|
|||||||
"queryAllDeclaredConstructors":true,
|
"queryAllDeclaredConstructors":true,
|
||||||
"methods":[{"name":"note","parameterTypes":[] }, {"name":"receipt","parameterTypes":[] }]
|
"methods":[{"name":"note","parameterTypes":[] }, {"name":"receipt","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"org.asamk.signal.json.JsonPollCreate",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"queryAllDeclaredConstructors":true,
|
||||||
|
"methods":[{"name":"allowMultiple","parameterTypes":[] }, {"name":"options","parameterTypes":[] }, {"name":"question","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"org.asamk.signal.json.JsonPollTerminate",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"queryAllDeclaredConstructors":true,
|
||||||
|
"methods":[{"name":"targetSentTimestamp","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"org.asamk.signal.json.JsonPollVote",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"queryAllDeclaredConstructors":true,
|
||||||
|
"methods":[{"name":"author","parameterTypes":[] }, {"name":"authorNumber","parameterTypes":[] }, {"name":"authorUuid","parameterTypes":[] }, {"name":"optionIndexes","parameterTypes":[] }, {"name":"targetSentTimestamp","parameterTypes":[] }, {"name":"voteCount","parameterTypes":[] }]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.asamk.signal.json.JsonPreview",
|
"name":"org.asamk.signal.json.JsonPreview",
|
||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
|
|||||||
@ -235,6 +235,29 @@ public interface Manager extends Closeable {
|
|||||||
Set<RecipientIdentifier> recipientIdentifiers
|
Set<RecipientIdentifier> recipientIdentifiers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SendMessageResults sendPollCreateMessage(
|
||||||
|
final String question,
|
||||||
|
final boolean allowMultiple,
|
||||||
|
final List<String> options,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException;
|
||||||
|
|
||||||
|
SendMessageResults sendPollVoteMessage(
|
||||||
|
final RecipientIdentifier.Single targetAuthor,
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final List<Integer> optionIndexes,
|
||||||
|
final int voteCount,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException;
|
||||||
|
|
||||||
|
SendMessageResults sendPollTerminateMessage(
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException;
|
||||||
|
|
||||||
void hideRecipient(RecipientIdentifier.Single recipient);
|
void hideRecipient(RecipientIdentifier.Single recipient);
|
||||||
|
|
||||||
void deleteRecipient(RecipientIdentifier.Single recipient);
|
void deleteRecipient(RecipientIdentifier.Single recipient);
|
||||||
|
|||||||
@ -115,6 +115,9 @@ public record MessageEnvelope(
|
|||||||
Optional<Long> remoteDeleteId,
|
Optional<Long> remoteDeleteId,
|
||||||
Optional<Sticker> sticker,
|
Optional<Sticker> sticker,
|
||||||
List<SharedContact> sharedContacts,
|
List<SharedContact> sharedContacts,
|
||||||
|
Optional<PollCreate> pollCreate,
|
||||||
|
Optional<PollVote> pollVote,
|
||||||
|
Optional<PollTerminate> pollTerminate,
|
||||||
List<Mention> mentions,
|
List<Mention> mentions,
|
||||||
List<Preview> previews,
|
List<Preview> previews,
|
||||||
List<TextStyle> textStyles
|
List<TextStyle> textStyles
|
||||||
@ -155,6 +158,9 @@ public record MessageEnvelope(
|
|||||||
.map(sharedContact -> SharedContact.from(sharedContact, fileProvider))
|
.map(sharedContact -> SharedContact.from(sharedContact, fileProvider))
|
||||||
.toList())
|
.toList())
|
||||||
.orElse(List.of()),
|
.orElse(List.of()),
|
||||||
|
dataMessage.getPollCreate().map(PollCreate::from),
|
||||||
|
dataMessage.getPollVote().map(p -> PollVote.from(p, recipientResolver, addressResolver)),
|
||||||
|
dataMessage.getPollTerminate().map(PollTerminate::from),
|
||||||
dataMessage.getMentions()
|
dataMessage.getMentions()
|
||||||
.map(a -> a.stream().map(m -> Mention.from(m, recipientResolver, addressResolver)).toList())
|
.map(a -> a.stream().map(m -> Mention.from(m, recipientResolver, addressResolver)).toList())
|
||||||
.orElse(List.of()),
|
.orElse(List.of()),
|
||||||
@ -509,6 +515,44 @@ public record MessageEnvelope(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record PollCreate(
|
||||||
|
String question, boolean allowMultiple, List<String> options
|
||||||
|
) {
|
||||||
|
|
||||||
|
static PollCreate from(
|
||||||
|
SignalServiceDataMessage.PollCreate pollCreate
|
||||||
|
) {
|
||||||
|
return new PollCreate(pollCreate.getQuestion(), pollCreate.getAllowMultiple(), pollCreate.getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PollVote(
|
||||||
|
RecipientAddress targetAuthor, long targetSentTimestamp, List<Integer> optionIndexes, int voteCount
|
||||||
|
) {
|
||||||
|
|
||||||
|
static PollVote from(
|
||||||
|
SignalServiceDataMessage.PollVote pollVote,
|
||||||
|
RecipientResolver recipientResolver,
|
||||||
|
RecipientAddressResolver addressResolver
|
||||||
|
) {
|
||||||
|
return new PollVote(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(pollVote.getTargetAuthor()))
|
||||||
|
.toApiRecipientAddress(),
|
||||||
|
pollVote.getTargetSentTimestamp(),
|
||||||
|
pollVote.getOptionIndexes(),
|
||||||
|
pollVote.getVoteCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PollTerminate(long targetSentTimestamp) {
|
||||||
|
|
||||||
|
static PollTerminate from(SignalServiceDataMessage.PollTerminate pollTerminate) {
|
||||||
|
return new PollTerminate(pollTerminate.getTargetSentTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public record Preview(String title, String description, long date, String url, Optional<Attachment> image) {
|
public record Preview(String title, String description, long date, String url, Optional<Attachment> image) {
|
||||||
|
|
||||||
static Preview from(SignalServicePreview preview, final AttachmentFileProvider fileProvider) {
|
static Preview from(SignalServicePreview preview, final AttachmentFileProvider fileProvider) {
|
||||||
|
|||||||
@ -1058,6 +1058,51 @@ public class ManagerImpl implements Manager {
|
|||||||
return new SendMessageResults(0, results);
|
return new SendMessageResults(0, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollCreateMessage(
|
||||||
|
final String question,
|
||||||
|
final boolean allowMultiple,
|
||||||
|
final List<String> options,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
final var pollCreate = new SignalServiceDataMessage.PollCreate(question, allowMultiple, options);
|
||||||
|
final var messageBuilder = SignalServiceDataMessage.newBuilder().withPollCreate(pollCreate);
|
||||||
|
return sendMessage(messageBuilder, recipients, notifySelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollVoteMessage(
|
||||||
|
final RecipientIdentifier.Single targetAuthor,
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final List<Integer> optionIndexes,
|
||||||
|
final int voteCount,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
final var targetAuthorRecipientId = context.getRecipientHelper().resolveRecipient(targetAuthor);
|
||||||
|
final var authorServiceId = context.getRecipientHelper()
|
||||||
|
.resolveSignalServiceAddress(targetAuthorRecipientId)
|
||||||
|
.getServiceId();
|
||||||
|
final var pollVote = new SignalServiceDataMessage.PollVote(authorServiceId,
|
||||||
|
targetSentTimestamp,
|
||||||
|
optionIndexes,
|
||||||
|
voteCount);
|
||||||
|
final var messageBuilder = SignalServiceDataMessage.newBuilder().withPollVote(pollVote);
|
||||||
|
return sendMessage(messageBuilder, recipients, notifySelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollTerminateMessage(
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
final var pollTerminate = new SignalServiceDataMessage.PollTerminate(targetSentTimestamp);
|
||||||
|
final var messageBuilder = SignalServiceDataMessage.newBuilder().withPollTerminate(pollTerminate);
|
||||||
|
return sendMessage(messageBuilder, recipients, notifySelf);
|
||||||
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
|||||||
@ -193,6 +193,27 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
|||||||
printAttachment(writer.indentedWriter(), attachment);
|
printAttachment(writer.indentedWriter(), attachment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!message.pollCreate().isEmpty()) {
|
||||||
|
final var pollCreate = message.pollCreate().get();
|
||||||
|
writer.println("Poll Create: \"{}\" ({})",
|
||||||
|
pollCreate.question(),
|
||||||
|
pollCreate.allowMultiple() ? "multi" : "single");
|
||||||
|
for (final var option : pollCreate.options()) {
|
||||||
|
writer.println("- {}", option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!message.pollVote().isEmpty()) {
|
||||||
|
final var pollVote = message.pollVote().get();
|
||||||
|
writer.println("Poll Vote: \"{}\" ({}) selected {} (vote #{})",
|
||||||
|
formatContact(pollVote.targetAuthor()),
|
||||||
|
DateUtils.formatTimestamp(pollVote.targetSentTimestamp()),
|
||||||
|
pollVote.optionIndexes().stream().map(Object::toString).collect(Collectors.joining(",")),
|
||||||
|
pollVote.voteCount());
|
||||||
|
}
|
||||||
|
if (!message.pollTerminate().isEmpty()) {
|
||||||
|
final var pollTerminate = message.pollTerminate().get();
|
||||||
|
writer.println("Poll Terminate: {}", DateUtils.formatTimestamp(pollTerminate.targetSentTimestamp()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printEditMessage(PlainTextWriter writer, MessageEnvelope.Edit message) {
|
private void printEditMessage(PlainTextWriter writer, MessageEnvelope.Edit message) {
|
||||||
|
|||||||
@ -41,6 +41,9 @@ public class Commands {
|
|||||||
addCommand(new SendContactsCommand());
|
addCommand(new SendContactsCommand());
|
||||||
addCommand(new SendMessageRequestResponseCommand());
|
addCommand(new SendMessageRequestResponseCommand());
|
||||||
addCommand(new SendPaymentNotificationCommand());
|
addCommand(new SendPaymentNotificationCommand());
|
||||||
|
addCommand(new SendPollCreateCommand());
|
||||||
|
addCommand(new SendPollVoteCommand());
|
||||||
|
addCommand(new SendPollTerminateCommand());
|
||||||
addCommand(new SendReactionCommand());
|
addCommand(new SendReactionCommand());
|
||||||
addCommand(new SendReceiptCommand());
|
addCommand(new SendReceiptCommand());
|
||||||
addCommand(new SendSyncRequestCommand());
|
addCommand(new SendSyncRequestCommand());
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
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.commands.exceptions.UnexpectedErrorException;
|
||||||
|
import org.asamk.signal.commands.exceptions.UserErrorException;
|
||||||
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.api.GroupNotFoundException;
|
||||||
|
import org.asamk.signal.manager.api.GroupSendingNotAllowedException;
|
||||||
|
import org.asamk.signal.manager.api.NotAGroupMemberException;
|
||||||
|
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
|
import org.asamk.signal.output.OutputWriter;
|
||||||
|
import org.asamk.signal.util.CommandUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.asamk.signal.util.SendMessageResultUtils.outputResult;
|
||||||
|
|
||||||
|
public class SendPollCreateCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SendPollCreateCommand.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sendPollCreate";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help("Create a poll and send it to another user or group.");
|
||||||
|
subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*");
|
||||||
|
subparser.addArgument("-g", "--group-id", "--group").help("Specify the recipient group ID.").nargs("*");
|
||||||
|
subparser.addArgument("-u", "--username").help("Specify the recipient username or username link.").nargs("*");
|
||||||
|
subparser.addArgument("--note-to-self").help("Send the message to self").action(Arguments.storeTrue());
|
||||||
|
subparser.addArgument("--notify-self")
|
||||||
|
.help("If self is part of recipients/groups send a normal message, not a sync message.")
|
||||||
|
.action(Arguments.storeTrue());
|
||||||
|
|
||||||
|
subparser.addArgument("-q", "--question").help("Specify the poll question.").required(true);
|
||||||
|
subparser.addArgument("--no-multi")
|
||||||
|
.action(Arguments.storeTrue())
|
||||||
|
.help("Allow only one option to be selected by each recipient.");
|
||||||
|
subparser.addArgument("-o", "--option").help("The options for the poll").nargs("+").required(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
final Namespace ns,
|
||||||
|
final Manager m,
|
||||||
|
final OutputWriter outputWriter
|
||||||
|
) throws CommandException {
|
||||||
|
final var notifySelf = Boolean.TRUE.equals(ns.getBoolean("notify-self"));
|
||||||
|
final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self"));
|
||||||
|
final var recipientStrings = ns.<String>getList("recipient");
|
||||||
|
final var groupIdStrings = ns.<String>getList("group-id");
|
||||||
|
final var usernameStrings = ns.<String>getList("username");
|
||||||
|
|
||||||
|
final var recipientIdentifiers = CommandUtil.getRecipientIdentifiers(m,
|
||||||
|
isNoteToSelf,
|
||||||
|
recipientStrings,
|
||||||
|
groupIdStrings,
|
||||||
|
usernameStrings);
|
||||||
|
|
||||||
|
final var question = ns.getString("question");
|
||||||
|
final var noMulti = Boolean.TRUE.equals(ns.getBoolean("no-multi"));
|
||||||
|
final var options = ns.<String>getList("option");
|
||||||
|
if (options.size() < 2) {
|
||||||
|
throw new UserErrorException("Poll needs at least tow options");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var results = m.sendPollCreateMessage(question, !noMulti, options, recipientIdentifiers, notifySelf);
|
||||||
|
outputResult(outputWriter, results);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().contains("No prekeys available")) {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + "), maybe one of the devices of the recipient wasn't online for a while.",
|
||||||
|
e);
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + ")", e);
|
||||||
|
}
|
||||||
|
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
|
||||||
|
throw new UserErrorException(e.getMessage());
|
||||||
|
} catch (UnregisteredRecipientException e) {
|
||||||
|
throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
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.commands.exceptions.UnexpectedErrorException;
|
||||||
|
import org.asamk.signal.commands.exceptions.UserErrorException;
|
||||||
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.api.GroupNotFoundException;
|
||||||
|
import org.asamk.signal.manager.api.GroupSendingNotAllowedException;
|
||||||
|
import org.asamk.signal.manager.api.NotAGroupMemberException;
|
||||||
|
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
|
import org.asamk.signal.output.OutputWriter;
|
||||||
|
import org.asamk.signal.util.CommandUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.asamk.signal.util.SendMessageResultUtils.outputResult;
|
||||||
|
|
||||||
|
public class SendPollTerminateCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SendPollTerminateCommand.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sendPollTerminate";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help("Terminate a poll and send it to another user or group.");
|
||||||
|
subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*");
|
||||||
|
subparser.addArgument("-g", "--group-id", "--group").help("Specify the recipient group ID.").nargs("*");
|
||||||
|
subparser.addArgument("-u", "--username").help("Specify the recipient username or username link.").nargs("*");
|
||||||
|
subparser.addArgument("--note-to-self").help("Send the message to self").action(Arguments.storeTrue());
|
||||||
|
subparser.addArgument("--notify-self")
|
||||||
|
.help("If self is part of recipients/groups send a normal message, not a sync message.")
|
||||||
|
.action(Arguments.storeTrue());
|
||||||
|
|
||||||
|
subparser.addArgument("--poll-timestamp")
|
||||||
|
.type(long.class)
|
||||||
|
.help("Specify the timestamp of the original poll message.")
|
||||||
|
.required(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
final Namespace ns,
|
||||||
|
final Manager m,
|
||||||
|
final OutputWriter outputWriter
|
||||||
|
) throws CommandException {
|
||||||
|
final var notifySelf = Boolean.TRUE.equals(ns.getBoolean("notify-self"));
|
||||||
|
final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self"));
|
||||||
|
final var recipientStrings = ns.<String>getList("recipient");
|
||||||
|
final var groupIdStrings = ns.<String>getList("group-id");
|
||||||
|
final var usernameStrings = ns.<String>getList("username");
|
||||||
|
|
||||||
|
final var recipientIdentifiers = CommandUtil.getRecipientIdentifiers(m,
|
||||||
|
isNoteToSelf,
|
||||||
|
recipientStrings,
|
||||||
|
groupIdStrings,
|
||||||
|
usernameStrings);
|
||||||
|
|
||||||
|
final var pollTimestamp = ns.getLong("poll-timestamp");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var results = m.sendPollTerminateMessage(pollTimestamp, recipientIdentifiers, notifySelf);
|
||||||
|
outputResult(outputWriter, results);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().contains("No prekeys available")) {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + "), maybe one of the devices of the recipient wasn't online for a while.",
|
||||||
|
e);
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + ")", e);
|
||||||
|
}
|
||||||
|
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
|
||||||
|
throw new UserErrorException(e.getMessage());
|
||||||
|
} catch (UnregisteredRecipientException e) {
|
||||||
|
throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/main/java/org/asamk/signal/commands/SendPollVoteCommand.java
Normal file
107
src/main/java/org/asamk/signal/commands/SendPollVoteCommand.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
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.commands.exceptions.UnexpectedErrorException;
|
||||||
|
import org.asamk.signal.commands.exceptions.UserErrorException;
|
||||||
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.api.GroupNotFoundException;
|
||||||
|
import org.asamk.signal.manager.api.GroupSendingNotAllowedException;
|
||||||
|
import org.asamk.signal.manager.api.NotAGroupMemberException;
|
||||||
|
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
|
import org.asamk.signal.output.OutputWriter;
|
||||||
|
import org.asamk.signal.util.CommandUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.asamk.signal.util.SendMessageResultUtils.outputResult;
|
||||||
|
|
||||||
|
public class SendPollVoteCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SendPollVoteCommand.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sendPollVote";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help("Vote on a poll and send it to another user or group.");
|
||||||
|
subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*");
|
||||||
|
subparser.addArgument("-g", "--group-id", "--group").help("Specify the recipient group ID.").nargs("*");
|
||||||
|
subparser.addArgument("-u", "--username").help("Specify the recipient username or username link.").nargs("*");
|
||||||
|
subparser.addArgument("--note-to-self").help("Send the message to self").action(Arguments.storeTrue());
|
||||||
|
subparser.addArgument("--notify-self")
|
||||||
|
.help("If self is part of recipients/groups send a normal message, not a sync message.")
|
||||||
|
.action(Arguments.storeTrue());
|
||||||
|
|
||||||
|
subparser.addArgument("--poll-author").help("Specify the number of the author of the poll message.");
|
||||||
|
subparser.addArgument("--poll-timestamp")
|
||||||
|
.type(long.class)
|
||||||
|
.help("Specify the timestamp of the original poll message.")
|
||||||
|
.required(true);
|
||||||
|
subparser.addArgument("-o", "--option")
|
||||||
|
.type(Integer.class)
|
||||||
|
.help("The option indexes of the poll to vote for")
|
||||||
|
.nargs("*");
|
||||||
|
subparser.addArgument("--vote-count")
|
||||||
|
.type(int.class)
|
||||||
|
.help("Specify the number of this vote (increase by one for every time you vote).")
|
||||||
|
.required(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
final Namespace ns,
|
||||||
|
final Manager m,
|
||||||
|
final OutputWriter outputWriter
|
||||||
|
) throws CommandException {
|
||||||
|
final var notifySelf = Boolean.TRUE.equals(ns.getBoolean("notify-self"));
|
||||||
|
final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self"));
|
||||||
|
final var recipientStrings = ns.<String>getList("recipient");
|
||||||
|
final var groupIdStrings = ns.<String>getList("group-id");
|
||||||
|
final var usernameStrings = ns.<String>getList("username");
|
||||||
|
|
||||||
|
final var recipientIdentifiers = CommandUtil.getRecipientIdentifiers(m,
|
||||||
|
isNoteToSelf,
|
||||||
|
recipientStrings,
|
||||||
|
groupIdStrings,
|
||||||
|
usernameStrings);
|
||||||
|
|
||||||
|
final var selfNumber = m.getSelfNumber();
|
||||||
|
final var pollTimestamp = ns.getLong("poll-timestamp");
|
||||||
|
final var pollAuthorString = ns.getString("poll-author");
|
||||||
|
final var pollAuthor = CommandUtil.getSingleRecipientIdentifier(pollAuthorString, selfNumber);
|
||||||
|
final var options = ns.<Integer>getList("option");
|
||||||
|
final var voteCount = ns.getInt("vote-count");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var results = m.sendPollVoteMessage(pollAuthor,
|
||||||
|
pollTimestamp,
|
||||||
|
options,
|
||||||
|
voteCount,
|
||||||
|
recipientIdentifiers,
|
||||||
|
notifySelf);
|
||||||
|
outputResult(outputWriter, results);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().contains("No prekeys available")) {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + "), maybe one of the devices of the recipient wasn't online for a while.",
|
||||||
|
e);
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
.getSimpleName() + ")", e);
|
||||||
|
}
|
||||||
|
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
|
||||||
|
throw new UserErrorException(e.getMessage());
|
||||||
|
} catch (UnregisteredRecipientException e) {
|
||||||
|
throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -509,6 +509,38 @@ public class DbusManagerImpl implements Manager {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollCreateMessage(
|
||||||
|
final String question,
|
||||||
|
final boolean allowMultiple,
|
||||||
|
final List<String> options,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollVoteMessage(
|
||||||
|
final RecipientIdentifier.Single targetAuthor,
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final List<Integer> optionIndexes,
|
||||||
|
final int voteCount,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SendMessageResults sendPollTerminateMessage(
|
||||||
|
final long targetSentTimestamp,
|
||||||
|
final Set<RecipientIdentifier> recipients,
|
||||||
|
final boolean notifySelf
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
@ -932,6 +964,9 @@ public class DbusManagerImpl implements Manager {
|
|||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
List.of(),
|
List.of(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
getMentions(extras),
|
getMentions(extras),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of())),
|
List.of())),
|
||||||
@ -975,6 +1010,9 @@ public class DbusManagerImpl implements Manager {
|
|||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
List.of(),
|
List.of(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
getMentions(extras),
|
getMentions(extras),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of()))),
|
List.of()))),
|
||||||
@ -1050,6 +1088,9 @@ public class DbusManagerImpl implements Manager {
|
|||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
List.of(),
|
List.of(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
|
Optional.empty(),
|
||||||
getMentions(extras),
|
getMentions(extras),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of())),
|
List.of())),
|
||||||
|
|||||||
@ -22,6 +22,9 @@ record JsonDataMessage(
|
|||||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts,
|
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts,
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonPollCreate pollCreate,
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonPollVote pollVote,
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonPollTerminate pollTerminate,
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonTextStyle> textStyles,
|
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonTextStyle> textStyles,
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo,
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo,
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonStoryContext storyContext
|
@JsonInclude(JsonInclude.Include.NON_NULL) JsonStoryContext storyContext
|
||||||
@ -61,6 +64,9 @@ record JsonDataMessage(
|
|||||||
.stream()
|
.stream()
|
||||||
.map(JsonSharedContact::from)
|
.map(JsonSharedContact::from)
|
||||||
.toList() : null;
|
.toList() : null;
|
||||||
|
final var pollCreate = dataMessage.pollCreate().map(JsonPollCreate::from).orElse(null);
|
||||||
|
final var pollVote = dataMessage.pollVote().map(JsonPollVote::from).orElse(null);
|
||||||
|
final var pollTerminate = dataMessage.pollTerminate().map(JsonPollTerminate::from).orElse(null);
|
||||||
final var textStyles = !dataMessage.textStyles().isEmpty() ? dataMessage.textStyles()
|
final var textStyles = !dataMessage.textStyles().isEmpty() ? dataMessage.textStyles()
|
||||||
.stream()
|
.stream()
|
||||||
.map(JsonTextStyle::from)
|
.map(JsonTextStyle::from)
|
||||||
@ -80,6 +86,9 @@ record JsonDataMessage(
|
|||||||
sticker,
|
sticker,
|
||||||
remoteDelete,
|
remoteDelete,
|
||||||
contacts,
|
contacts,
|
||||||
|
pollCreate,
|
||||||
|
pollVote,
|
||||||
|
pollTerminate,
|
||||||
textStyles,
|
textStyles,
|
||||||
groupInfo,
|
groupInfo,
|
||||||
storyContext);
|
storyContext);
|
||||||
|
|||||||
18
src/main/java/org/asamk/signal/json/JsonPollCreate.java
Normal file
18
src/main/java/org/asamk/signal/json/JsonPollCreate.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package org.asamk.signal.json;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record JsonPollCreate(
|
||||||
|
String question, boolean allowMultiple, List<String> options
|
||||||
|
) {
|
||||||
|
|
||||||
|
static JsonPollCreate from(MessageEnvelope.Data.PollCreate pollCreate) {
|
||||||
|
final var question = pollCreate.question();
|
||||||
|
final var allowMultiple = pollCreate.allowMultiple();
|
||||||
|
final var options = pollCreate.options();
|
||||||
|
|
||||||
|
return new JsonPollCreate(question, allowMultiple, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/org/asamk/signal/json/JsonPollTerminate.java
Normal file
12
src/main/java/org/asamk/signal/json/JsonPollTerminate.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package org.asamk.signal.json;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope;
|
||||||
|
|
||||||
|
public record JsonPollTerminate(long targetSentTimestamp) {
|
||||||
|
|
||||||
|
static JsonPollTerminate from(MessageEnvelope.Data.PollTerminate pollTerminate) {
|
||||||
|
final var targetSentTimestamp = pollTerminate.targetSentTimestamp();
|
||||||
|
|
||||||
|
return new JsonPollTerminate(targetSentTimestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/main/java/org/asamk/signal/json/JsonPollVote.java
Normal file
28
src/main/java/org/asamk/signal/json/JsonPollVote.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package org.asamk.signal.json;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.api.MessageEnvelope;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record JsonPollVote(
|
||||||
|
@Deprecated String author,
|
||||||
|
String authorNumber,
|
||||||
|
String authorUuid,
|
||||||
|
long targetSentTimestamp,
|
||||||
|
List<Integer> optionIndexes,
|
||||||
|
int voteCount
|
||||||
|
) {
|
||||||
|
|
||||||
|
static JsonPollVote from(MessageEnvelope.Data.PollVote pollVote) {
|
||||||
|
final var address = pollVote.targetAuthor();
|
||||||
|
final var author = address.getLegacyIdentifier();
|
||||||
|
final var authorNumber = address.number().orElse(null);
|
||||||
|
final var authorUuid = address.uuid().map(UUID::toString).orElse(null);
|
||||||
|
final var targetSentTimestamp = pollVote.targetSentTimestamp();
|
||||||
|
final var optionIndexes = pollVote.optionIndexes();
|
||||||
|
final var voteCount = pollVote.voteCount();
|
||||||
|
|
||||||
|
return new JsonPollVote(author, authorNumber, authorUuid, targetSentTimestamp, optionIndexes, voteCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user