use of org.javacord.api.entity.message.Message in project Javacord by BtoBastian.
the class MessageBuilderBaseDelegateImpl method checkForAttachmentsAndExecuteRequest.
// //////////////////////////////////////////////////////////////////////////////
// Internal MessageBuilder utility methods
// //////////////////////////////////////////////////////////////////////////////
private CompletableFuture<Message> checkForAttachmentsAndExecuteRequest(TextChannel channel, ObjectNode body, RestRequest<Message> request, boolean clearAttachmentsIfAppropriate) {
if (attachments.isEmpty() && embeds.stream().noneMatch(EmbedBuilder::requiresAttachments)) {
if (clearAttachmentsIfAppropriate) {
body.set("attachments", JsonNodeFactory.instance.objectNode().arrayNode());
}
return executeRequestWithoutAttachments(channel, body, request);
}
CompletableFuture<Message> future = new CompletableFuture<>();
// We access files etc. so this should be async
channel.getApi().getThreadPool().getExecutorService().submit(() -> {
try {
List<FileContainer> tempAttachments = new ArrayList<>(attachments);
// Add the attachments required for the embeds
for (EmbedBuilder embed : embeds) {
tempAttachments.addAll(((EmbedBuilderDelegateImpl) embed.getDelegate()).getRequiredAttachments());
}
addMultipartBodyToRequest(request, body, tempAttachments, channel.getApi());
request.execute(result -> ((DiscordApiImpl) channel.getApi()).getOrCreateMessage(channel, result.getJsonBody())).whenComplete((newMessage, throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
future.complete(newMessage);
}
});
} catch (Throwable t) {
future.completeExceptionally(t);
}
});
return future;
}
use of org.javacord.api.entity.message.Message in project Javacord by BtoBastian.
the class MessageBuilderBaseDelegateImpl method send.
/**
* Send a message to an incoming webhook.
*
* @param webhookId The id of the webhook to send the message to
* @param webhookToken The token of the webhook to send the message to
* @param displayName The display name the webhook should use
* @param avatarUrl The avatar the webhook should use
* @param wait If the completable future will be completed
* @param api The api instance needed to send and return the message
* @return The sent message
*/
protected CompletableFuture<Message> send(String webhookId, String webhookToken, String displayName, URL avatarUrl, boolean wait, DiscordApi api) {
ObjectNode body = JsonNodeFactory.instance.objectNode();
prepareCommonWebhookMessageBodyParts(body);
if (displayName != null) {
body.put("username", displayName);
}
if (avatarUrl != null) {
body.put("avatar_url", avatarUrl.toExternalForm());
}
prepareComponents(body);
if (strBuilder.length() != 0) {
body.put("content", strBuilder.toString());
}
RestRequest<Message> request = new RestRequest<Message>(api, RestMethod.POST, RestEndpoint.WEBHOOK_SEND).addQueryParameter("wait", Boolean.toString(wait)).setUrlParameters(webhookId, webhookToken);
CompletableFuture<Message> future = new CompletableFuture<>();
if (!attachments.isEmpty() || embeds.stream().anyMatch(EmbedBuilder::requiresAttachments)) {
// We access files etc. so this should be async
api.getThreadPool().getExecutorService().submit(() -> {
try {
List<FileContainer> tempAttachments = new ArrayList<>(attachments);
// Add the attachments required for the embeds
for (EmbedBuilder embed : embeds) {
tempAttachments.addAll(((EmbedBuilderDelegateImpl) embed.getDelegate()).getRequiredAttachments());
}
addMultipartBodyToRequest(request, body, tempAttachments, api);
executeWebhookRest(request, wait, future, api);
} catch (Throwable t) {
future.completeExceptionally(t);
}
});
} else {
request.setBody(body);
executeWebhookRest(request, wait, future, api);
}
return future;
}
use of org.javacord.api.entity.message.Message in project Javacord by BtoBastian.
the class UncachedMessageUtilImpl method edit.
@Override
public CompletableFuture<Message> edit(long channelId, long messageId, String content, boolean updateContent, List<EmbedBuilder> embeds, boolean updateEmbed) {
ObjectNode body = JsonNodeFactory.instance.objectNode();
if (updateContent) {
if (content == null || content.isEmpty()) {
body.putNull("content");
} else {
body.put("content", content);
}
}
if (updateEmbed) {
ArrayNode embedArray = body.putArray("embeds");
embeds.stream().map(embedBuilder -> ((EmbedBuilderDelegateImpl) embedBuilder.getDelegate()).toJsonNode()).forEach(embedArray::add);
}
return new RestRequest<Message>(api, RestMethod.PATCH, RestEndpoint.MESSAGE).setUrlParameters(Long.toUnsignedString(channelId), Long.toUnsignedString(messageId)).setBody(body).execute(result -> new MessageImpl(api, api.getTextChannelById(channelId).orElseThrow(() -> new IllegalStateException("TextChannel is missing.")), result.getJsonBody()));
}
use of org.javacord.api.entity.message.Message in project Javacord by BtoBastian.
the class UncachedMessageUtilImpl method delete.
@Override
public CompletableFuture<Void> delete(long channelId, long... messageIds) {
// split by younger than two weeks / older than two weeks
Instant twoWeeksAgo = Instant.now().minus(14, ChronoUnit.DAYS);
Map<Boolean, List<Long>> messageIdsByAge = Arrays.stream(messageIds).distinct().boxed().collect(Collectors.groupingBy(messageId -> DiscordEntity.getCreationTimestamp(messageId).isAfter(twoWeeksAgo)));
AtomicInteger batchCounter = new AtomicInteger();
return CompletableFuture.allOf(Stream.concat(// for messages younger than 2 weeks
messageIdsByAge.getOrDefault(true, Collections.emptyList()).stream().collect(Collectors.groupingBy(messageId -> batchCounter.getAndIncrement() / 100)).values().stream().map(messageIdBatch -> {
// do not use batch deletion for a single message
if (messageIdBatch.size() == 1) {
return Message.delete(api, channelId, messageIdBatch.get(0));
}
ObjectNode body = JsonNodeFactory.instance.objectNode();
ArrayNode messages = body.putArray("messages");
messageIdBatch.stream().map(Long::toUnsignedString).forEach(messages::add);
return new RestRequest<Void>(api, RestMethod.POST, RestEndpoint.MESSAGES_BULK_DELETE).setUrlParameters(Long.toUnsignedString(channelId)).setBody(body).execute(result -> null);
}), // for messages older than 2 weeks use single message deletion
messageIdsByAge.getOrDefault(false, Collections.emptyList()).stream().map(messageId -> Message.delete(api, channelId, messageId))).toArray(CompletableFuture[]::new));
}
use of org.javacord.api.entity.message.Message in project Javacord by BtoBastian.
the class TextChannel method typeContinuouslyAfter.
/**
* Displays the "xyz is typing..." message continuously, starting delayed.
* The message is continuously displayed if not quit using the returned {@code AutoCloseable}.
* Sending a message will make the message go away shortly, but it will return immediately if not cancelled using
* the {@code AutoCloseable}. This can be used in a try-with-resources block like
* <code>try (NonThrowingAutoCloseable typingIndicator = textChannel.typeContinuouslyAfter(500,
* TimeUnit.MILLISECONDS, ExceptionLogger.getConsumer(RatelimitException.class)))
* { /* do lengthy stuff */ } sendReply();</code>.
*
* <p>The typing indicator will be shown delayed. This can be useful if the task you do can be finished in very
* short time which could cause the typing indicator and the response message being sent at the same time and the
* typing indicator could be shown for 10 seconds even if the message was sent already. With the delay this is
* compensated, because if the returned {@code AutoCloseable} is closed before the delay is over, no typing
* indicator will be sent at all.
*
* <p>Any occurring exceptions including ratelimit exceptions are given to the provided {@code exceptionHandler} or
* ignored if it is {@code null}.
*
* @param exceptionHandler The handler that exceptions are given to.
* @param delay The delay to wait until the first typing indicator is sent.
* @param timeUnit The time unit of the delay value.
* @return An auto-closable to stop sending the typing indicator.
* @see #type()
* @see #typeContinuously()
* @see #typeContinuously(Consumer)
* @see #typeContinuouslyAfter(long, TimeUnit)
*/
default NonThrowingAutoCloseable typeContinuouslyAfter(long delay, TimeUnit timeUnit, Consumer<Throwable> exceptionHandler) {
// the delegate that does the actual type indicator sending and error handling
Runnable typeRunnable = () -> {
try {
CompletableFuture<?> typeFuture = type();
if (exceptionHandler != null) {
typeFuture.exceptionally(throwable -> {
exceptionHandler.accept(throwable);
return null;
});
}
} catch (Throwable t) {
ExceptionLogger.getConsumer().accept(t);
}
};
DiscordApi api = getApi();
// schedule regular type indicator sending
Future<?> typingIndicator = api.getThreadPool().getScheduler().scheduleWithFixedDelay(typeRunnable, TimeUnit.NANOSECONDS.convert(delay, timeUnit), 8_000_000_000L, TimeUnit.NANOSECONDS);
// prevent messages from other commands to interrupt the typing indicator too long
ListenerManager<MessageCreateListener> typingInterruptedListenerManager = api.addMessageCreateListener(event -> {
if (event.getMessage().getAuthor().isYourself()) {
typeRunnable.run();
}
});
// auto-closable to cancel the continuously typing indicator
return () -> {
typingInterruptedListenerManager.remove();
typingIndicator.cancel(true);
};
}
Aggregations