Add support for receiving and sending polls

This commit is contained in:
AsamK 2025-11-02 18:24:47 +01:00
parent 91701f609a
commit feaee2bfe1
14 changed files with 555 additions and 1 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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());

View File

@ -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.");
}
}
}

View File

@ -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.");
}
}
}

View 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.");
}
}
}

View File

@ -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())),

View File

@ -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);

View 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);
}
}

View 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);
}
}

View 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);
}
}