mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-06-13 17:30:20 +00:00
Add support for receiving and sending polls
This commit is contained in:
parent
91701f609a
commit
feaee2bfe1
@ -1093,7 +1093,7 @@
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredMethods":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",
|
||||
@ -1137,6 +1137,27 @@
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"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",
|
||||
"allDeclaredFields":true,
|
||||
|
||||
@ -235,6 +235,29 @@ public interface Manager extends Closeable {
|
||||
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 deleteRecipient(RecipientIdentifier.Single recipient);
|
||||
|
||||
@ -115,6 +115,9 @@ public record MessageEnvelope(
|
||||
Optional<Long> remoteDeleteId,
|
||||
Optional<Sticker> sticker,
|
||||
List<SharedContact> sharedContacts,
|
||||
Optional<PollCreate> pollCreate,
|
||||
Optional<PollVote> pollVote,
|
||||
Optional<PollTerminate> pollTerminate,
|
||||
List<Mention> mentions,
|
||||
List<Preview> previews,
|
||||
List<TextStyle> textStyles
|
||||
@ -155,6 +158,9 @@ public record MessageEnvelope(
|
||||
.map(sharedContact -> SharedContact.from(sharedContact, fileProvider))
|
||||
.toList())
|
||||
.orElse(List.of()),
|
||||
dataMessage.getPollCreate().map(PollCreate::from),
|
||||
dataMessage.getPollVote().map(p -> PollVote.from(p, recipientResolver, addressResolver)),
|
||||
dataMessage.getPollTerminate().map(PollTerminate::from),
|
||||
dataMessage.getMentions()
|
||||
.map(a -> a.stream().map(m -> Mention.from(m, recipientResolver, addressResolver)).toList())
|
||||
.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) {
|
||||
|
||||
static Preview from(SignalServicePreview preview, final AttachmentFileProvider fileProvider) {
|
||||
|
||||
@ -1058,6 +1058,51 @@ public class ManagerImpl implements Manager {
|
||||
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
|
||||
public void hideRecipient(final RecipientIdentifier.Single recipient) {
|
||||
final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient);
|
||||
|
||||
@ -193,6 +193,27 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||
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) {
|
||||
|
||||
@ -41,6 +41,9 @@ public class Commands {
|
||||
addCommand(new SendContactsCommand());
|
||||
addCommand(new SendMessageRequestResponseCommand());
|
||||
addCommand(new SendPaymentNotificationCommand());
|
||||
addCommand(new SendPollCreateCommand());
|
||||
addCommand(new SendPollVoteCommand());
|
||||
addCommand(new SendPollTerminateCommand());
|
||||
addCommand(new SendReactionCommand());
|
||||
addCommand(new SendReceiptCommand());
|
||||
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();
|
||||
}
|
||||
|
||||
@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) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@ -932,6 +964,9 @@ public class DbusManagerImpl implements Manager {
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
List.of(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
getMentions(extras),
|
||||
List.of(),
|
||||
List.of())),
|
||||
@ -975,6 +1010,9 @@ public class DbusManagerImpl implements Manager {
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
List.of(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
getMentions(extras),
|
||||
List.of(),
|
||||
List.of()))),
|
||||
@ -1050,6 +1088,9 @@ public class DbusManagerImpl implements Manager {
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
List.of(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
getMentions(extras),
|
||||
List.of(),
|
||||
List.of())),
|
||||
|
||||
@ -22,6 +22,9 @@ record JsonDataMessage(
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
|
||||
@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) JsonGroupInfo groupInfo,
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL) JsonStoryContext storyContext
|
||||
@ -61,6 +64,9 @@ record JsonDataMessage(
|
||||
.stream()
|
||||
.map(JsonSharedContact::from)
|
||||
.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()
|
||||
.stream()
|
||||
.map(JsonTextStyle::from)
|
||||
@ -80,6 +86,9 @@ record JsonDataMessage(
|
||||
sticker,
|
||||
remoteDelete,
|
||||
contacts,
|
||||
pollCreate,
|
||||
pollVote,
|
||||
pollTerminate,
|
||||
textStyles,
|
||||
groupInfo,
|
||||
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