mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-09-23 06:39:04 +00:00
Add --voice-note flag for send command
Add support for marking audio attachments as voice notes when sending messages. Voice notes are displayed inline with a play button in Signal clients, rather than as file attachments. This addresses a longstanding TODO in AttachmentUtils.java and resolves the feature request in #1601. Changes: - Add 'voiceNote' field to Message record - Pass voiceNote flag through AttachmentHelper to AttachmentUtils - Add .withVoiceNote() to SignalServiceAttachmentStream builder - Add --voice-note CLI argument to SendCommand - Support voiceNote parameter in JSON-RPC mode Usage: signal-cli send -a audio.m4a --voice-note +1234567890 JSON-RPC: {"method":"send","params":{"attachment":"audio.m4a","voiceNote":true,...}} Closes #1601
This commit is contained in:
parent
313f5392ef
commit
d8e9a7ab40
@ -7,6 +7,7 @@ public record Message(
|
|||||||
String messageText,
|
String messageText,
|
||||||
List<String> attachments,
|
List<String> attachments,
|
||||||
boolean viewOnce,
|
boolean viewOnce,
|
||||||
|
boolean voiceNote,
|
||||||
List<Mention> mentions,
|
List<Mention> mentions,
|
||||||
Optional<Quote> quote,
|
Optional<Quote> quote,
|
||||||
Optional<Sticker> sticker,
|
Optional<Sticker> sticker,
|
||||||
|
|||||||
@ -44,8 +44,8 @@ public class AttachmentHelper {
|
|||||||
return attachmentStore.retrieveAttachment(id);
|
return attachmentStore.retrieveAttachment(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SignalServiceAttachment> uploadAttachments(final List<String> attachments) throws AttachmentInvalidException, IOException {
|
public List<SignalServiceAttachment> uploadAttachments(final List<String> attachments, boolean voiceNote) throws AttachmentInvalidException, IOException {
|
||||||
final var attachmentStreams = createAttachmentStreams(attachments);
|
final var attachmentStreams = createAttachmentStreams(attachments, voiceNote);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Upload attachments here, so we only upload once even for multiple recipients
|
// Upload attachments here, so we only upload once even for multiple recipients
|
||||||
@ -61,14 +61,18 @@ public class AttachmentHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SignalServiceAttachmentStream> createAttachmentStreams(List<String> attachments) throws AttachmentInvalidException, IOException {
|
public List<SignalServiceAttachment> uploadAttachments(final List<String> attachments) throws AttachmentInvalidException, IOException {
|
||||||
|
return uploadAttachments(attachments, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SignalServiceAttachmentStream> createAttachmentStreams(List<String> attachments, boolean voiceNote) throws AttachmentInvalidException, IOException {
|
||||||
if (attachments == null) {
|
if (attachments == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final var signalServiceAttachments = new ArrayList<SignalServiceAttachmentStream>(attachments.size());
|
final var signalServiceAttachments = new ArrayList<SignalServiceAttachmentStream>(attachments.size());
|
||||||
for (var attachment : attachments) {
|
for (var attachment : attachments) {
|
||||||
final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec();
|
final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec();
|
||||||
signalServiceAttachments.add(AttachmentUtils.createAttachmentStream(attachment, uploadSpec));
|
signalServiceAttachments.add(AttachmentUtils.createAttachmentStream(attachment, voiceNote, uploadSpec));
|
||||||
}
|
}
|
||||||
return signalServiceAttachments;
|
return signalServiceAttachments;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -843,7 +843,7 @@ public class ManagerImpl implements Manager {
|
|||||||
messageBuilder.withBody(message.messageText());
|
messageBuilder.withBody(message.messageText());
|
||||||
}
|
}
|
||||||
if (!message.attachments().isEmpty()) {
|
if (!message.attachments().isEmpty()) {
|
||||||
final var uploadedAttachments = context.getAttachmentHelper().uploadAttachments(message.attachments());
|
final var uploadedAttachments = context.getAttachmentHelper().uploadAttachments(message.attachments(), message.voiceNote());
|
||||||
if (!additionalAttachments.isEmpty()) {
|
if (!additionalAttachments.isEmpty()) {
|
||||||
additionalAttachments.addAll(uploadedAttachments);
|
additionalAttachments.addAll(uploadedAttachments);
|
||||||
messageBuilder.withAttachments(additionalAttachments);
|
messageBuilder.withAttachments(additionalAttachments);
|
||||||
|
|||||||
@ -14,32 +14,49 @@ public class AttachmentUtils {
|
|||||||
|
|
||||||
public static SignalServiceAttachmentStream createAttachmentStream(
|
public static SignalServiceAttachmentStream createAttachmentStream(
|
||||||
String attachment,
|
String attachment,
|
||||||
|
boolean voiceNote,
|
||||||
ResumableUploadSpec resumableUploadSpec
|
ResumableUploadSpec resumableUploadSpec
|
||||||
) throws AttachmentInvalidException {
|
) throws AttachmentInvalidException {
|
||||||
try {
|
try {
|
||||||
final var streamDetails = Utils.createStreamDetails(attachment);
|
final var streamDetails = Utils.createStreamDetails(attachment);
|
||||||
|
|
||||||
return createAttachmentStream(streamDetails.first(), streamDetails.second(), resumableUploadSpec);
|
return createAttachmentStream(streamDetails.first(), streamDetails.second(), voiceNote, resumableUploadSpec);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AttachmentInvalidException(attachment, e);
|
throw new AttachmentInvalidException(attachment, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SignalServiceAttachmentStream createAttachmentStream(
|
||||||
|
String attachment,
|
||||||
|
ResumableUploadSpec resumableUploadSpec
|
||||||
|
) throws AttachmentInvalidException {
|
||||||
|
return createAttachmentStream(attachment, false, resumableUploadSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalServiceAttachmentStream createAttachmentStream(
|
||||||
|
StreamDetails streamDetails,
|
||||||
|
Optional<String> name,
|
||||||
|
boolean voiceNote,
|
||||||
|
ResumableUploadSpec resumableUploadSpec
|
||||||
|
) throws ResumeLocationInvalidException {
|
||||||
|
final var uploadTimestamp = System.currentTimeMillis();
|
||||||
|
return SignalServiceAttachmentStream.newStreamBuilder()
|
||||||
|
.withStream(streamDetails.getStream())
|
||||||
|
.withContentType(streamDetails.getContentType())
|
||||||
|
.withLength(streamDetails.getLength())
|
||||||
|
.withFileName(name.orElse(null))
|
||||||
|
.withVoiceNote(voiceNote)
|
||||||
|
.withUploadTimestamp(uploadTimestamp)
|
||||||
|
.withResumableUploadSpec(resumableUploadSpec)
|
||||||
|
.withUuid(UUID.randomUUID())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
public static SignalServiceAttachmentStream createAttachmentStream(
|
public static SignalServiceAttachmentStream createAttachmentStream(
|
||||||
StreamDetails streamDetails,
|
StreamDetails streamDetails,
|
||||||
Optional<String> name,
|
Optional<String> name,
|
||||||
ResumableUploadSpec resumableUploadSpec
|
ResumableUploadSpec resumableUploadSpec
|
||||||
) throws ResumeLocationInvalidException {
|
) throws ResumeLocationInvalidException {
|
||||||
// TODO maybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option
|
return createAttachmentStream(streamDetails, name, false, resumableUploadSpec);
|
||||||
final var uploadTimestamp = System.currentTimeMillis();
|
|
||||||
return SignalServiceAttachmentStream.newStreamBuilder()
|
|
||||||
.withStream(streamDetails.getStream())
|
|
||||||
.withContentType(streamDetails.getContentType())
|
|
||||||
.withLength(streamDetails.getLength())
|
|
||||||
.withFileName(name.orElse(null))
|
|
||||||
.withUploadTimestamp(uploadTimestamp)
|
|
||||||
.withResumableUploadSpec(resumableUploadSpec)
|
|
||||||
.withUuid(UUID.randomUUID())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,6 +109,9 @@ public class SendCommand implements JsonRpcLocalCommand {
|
|||||||
.action(Arguments.storeTrue())
|
.action(Arguments.storeTrue())
|
||||||
.help("Send the message without the urgent flag, so no push notification is triggered for the recipient. "
|
.help("Send the message without the urgent flag, so no push notification is triggered for the recipient. "
|
||||||
+ "The message will still be delivered in real-time if the recipient's app is active.");
|
+ "The message will still be delivered in real-time if the recipient's app is active.");
|
||||||
|
subparser.addArgument("--voice-note")
|
||||||
|
.action(Arguments.storeTrue())
|
||||||
|
.help("Mark audio attachments as voice notes. Voice notes are displayed inline in Signal clients.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -171,6 +174,7 @@ public class SendCommand implements JsonRpcLocalCommand {
|
|||||||
attachments = List.of();
|
attachments = List.of();
|
||||||
}
|
}
|
||||||
final var viewOnce = Boolean.TRUE.equals(ns.getBoolean("view-once"));
|
final var viewOnce = Boolean.TRUE.equals(ns.getBoolean("view-once"));
|
||||||
|
final var voiceNote = Boolean.TRUE.equals(ns.getBoolean("voice-note"));
|
||||||
|
|
||||||
final var selfNumber = m.getSelfNumber();
|
final var selfNumber = m.getSelfNumber();
|
||||||
|
|
||||||
@ -247,6 +251,7 @@ public class SendCommand implements JsonRpcLocalCommand {
|
|||||||
final var message = new Message(messageText,
|
final var message = new Message(messageText,
|
||||||
attachments,
|
attachments,
|
||||||
viewOnce,
|
viewOnce,
|
||||||
|
voiceNote,
|
||||||
mentions,
|
mentions,
|
||||||
Optional.ofNullable(quote),
|
Optional.ofNullable(quote),
|
||||||
Optional.ofNullable(sticker),
|
Optional.ofNullable(sticker),
|
||||||
|
|||||||
@ -238,6 +238,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
final var message = new Message(messageText,
|
final var message = new Message(messageText,
|
||||||
attachments,
|
attachments,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
List.of(),
|
List.of(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
@ -404,6 +405,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
final var message = new Message(messageText,
|
final var message = new Message(messageText,
|
||||||
attachments,
|
attachments,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
List.of(),
|
List.of(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
@ -451,6 +453,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
final var message = new Message(messageText,
|
final var message = new Message(messageText,
|
||||||
attachments,
|
attachments,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
List.of(),
|
List.of(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user