Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b82bac6
Start adding message search
freya022 Dec 11, 2025
b6ff773
Improve docs about missing objects
freya022 Mar 23, 2026
fe0fe5d
Add `channels` methods accepting IDs to allow searching archived threads
freya022 Mar 23, 2026
77a47b0
Parse thread channels and self thread members
freya022 Mar 23, 2026
3b82ae4
Improve docs
freya022 Mar 28, 2026
c6666e8
Limit channels to 500
freya022 Mar 28, 2026
546a661
Add minId/maxId with `String`
freya022 Mar 28, 2026
ba806df
Split author types into inclusion/exclusion
freya022 Mar 28, 2026
ad58a7d
Split "has" types into inclusion/exclusion
freya022 Mar 28, 2026
7c69a14
Check channel IDs are snowflake
freya022 Mar 28, 2026
1bb7e50
Docs
freya022 Mar 28, 2026
16b6bc0
Add arrays to query parameters by duplicating query parameters
freya022 Mar 28, 2026
158c318
MISSING_ACCESS is returned when filtering on a single inaccessible ch…
freya022 Mar 28, 2026
d43e321
author/has types: Make included and excluded cancel each other
freya022 Mar 28, 2026
43bdb06
Docs
freya022 Mar 28, 2026
a55a518
Accept `null` in `mentionsEveryone`
freya022 Mar 28, 2026
e573b11
Docs
freya022 Mar 28, 2026
02e4e2f
Accept `null` in `pinned`
freya022 Mar 28, 2026
6620206
Add missing checks
freya022 Mar 28, 2026
1692ac7
Make sort algo and sort order not-null
freya022 Mar 28, 2026
e0437f9
A lot of docs
freya022 Mar 28, 2026
6f16f83
Move actions to correct package
freya022 Mar 28, 2026
c4c41bc
Finally found how embed providers are defined
freya022 Mar 28, 2026
99f6bab
More docs
freya022 Mar 28, 2026
8a8c366
Merge branch 'refs/heads/master' into feature/message-search
freya022 Mar 28, 2026
1524f3b
Improve docs
freya022 Mar 29, 2026
7f80d25
Rename `hasFailed` => `isNotReady`
freya022 Mar 29, 2026
129a983
Add example in docs
freya022 Mar 29, 2026
98a3c73
Merge branch 'refs/heads/master' into feature/message-search
freya022 Jun 14, 2026
f6fa00d
Apply docs review comments
freya022 Jun 14, 2026
785a120
Add missing checks
freya022 Jun 14, 2026
e66861a
Fix setting "has" types
freya022 Jun 14, 2026
7a30c27
Use `Duration`
freya022 Jun 14, 2026
fcb60f2
Use non-null primitives when `String` overload exists
freya022 Jun 14, 2026
901617d
Add constants
freya022 Jun 14, 2026
a7305c3
Don't make `NotReady`/`Results` extend `MessageSearchResponse`
freya022 Jun 14, 2026
af103fc
Fix HTML
freya022 Jun 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import net.dv8tion.jda.api.entities.channel.concrete.*;
import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildChannel;
import net.dv8tion.jda.api.entities.channel.unions.DefaultGuildChannelUnion;
import net.dv8tion.jda.api.entities.detached.IDetachableEntity;
Expand Down Expand Up @@ -5980,6 +5981,54 @@ ScheduledEventAction createScheduledEvent(
ScheduledEventAction createScheduledEvent(
@Nonnull String name, @Nonnull GuildChannel channel, @Nonnull OffsetDateTime startTime);

/**
* Searches for messages in this guild.
* The {@link GatewayIntent#MESSAGE_CONTENT MESSAGE_CONTENT} intent must be enabled on the dev portal,
* but the bot does not necessarily need to start with it.
Comment thread
freya022 marked this conversation as resolved.
Outdated
* <br>If all you need is to iterate messages of a channel, use {@link MessageChannel#getIterableHistory()} instead.
*
* <p>Any invalid entity referenced by the search query, will be ignored.
*
* <p><b>Note:</b> The search may return fewer results when messages have not been accessed for a long time.
*
* <p>Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
* the returned {@link RestAction} include the following:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* <br>You are missing the {@link GatewayIntent#MESSAGE_CONTENT MESSAGE_CONTENT} intent, or the search is filtered on a single channel which you don't have access to</li>
* </ul>
*
* <h4>Example - Finding {@code cat.png} attachments sent by users:</h4>
* {@snippet lang=java:
* guild.searchMessages()
* .attachmentFilenames("cat.png")
* // You can import AuthorType for better readability
Comment thread
freya022 marked this conversation as resolved.
Outdated
* .includeAuthorTypes(MessageSearchAction.AuthorType.USER)
* .queue(response -> {
* if (response.isNotReady()) {
* var notReady = response.asNotReady();
* int retryAfter = notReady.getRetryAfter();
* // Reply
Comment thread
freya022 marked this conversation as resolved.
Outdated
* return;
* }
*
* var results = response.asResults();
* var messages = results.getMessages();
* // Handle messages
* });
* }
*
* @throws InsufficientPermissionException
* If the {@linkplain #getSelfMember() current member} does not have the {@link Permission#MESSAGE_HISTORY MESSAGE_HISTORY} permission
* @throws net.dv8tion.jda.api.exceptions.DetachedEntityException
* If this entity is {@link #isDetached() detached}
*
* @return {@link MessageSearchAction}, which, when completed, returns a {@link net.dv8tion.jda.api.entities.messages.MessageSearchResponse MessageSearchResponse}
Comment thread
freya022 marked this conversation as resolved.
Outdated
*/
@Nonnull
@CheckReturnValue
MessageSearchAction searchMessages();

/**
* Modifies the positional order of {@link net.dv8tion.jda.api.entities.Guild#getCategories() Guild.getCategories()}
* using a specific {@link RestAction RestAction} extension to allow moving Channels
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/net/dv8tion/jda/api/entities/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ default Message getReferencedMessage() {
* <br>You can check the type of channel this message was sent from using {@link #isFromType(ChannelType)} or {@link #getChannelType()}.
*
* <p>Discord does not provide a member object for messages returned by {@link RestAction RestActions} of any kind.
* This will return null if the message was retrieved through {@link MessageChannel#retrieveMessageById(long)} or similar means,
* unless the member is already cached.
* This will return null if the message was retrieved through {@link MessageChannel#retrieveMessageById(long)},
* the {@linkplain Guild#searchMessages() search API} or similar means, unless the member is already cached.
*
* @return Message author, or {@code null} if the message was not sent in a GuildMessageChannel, or if the message was sent by a Webhook.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ default ThreadMember getSelfThreadMember() {
* <li>the bot must have be online to receive the update</li>
* </ul>
*
* <p>If this thread was referenced by the {@linkplain Guild#searchMessages() search API},
* only the {@linkplain Guild#getSelfMember() current member} can be present in this list.
*
* @throws net.dv8tion.jda.api.exceptions.DetachedEntityException
* If this entity is {@link #isDetached() detached}
*
Expand All @@ -332,6 +335,9 @@ default ThreadMember getSelfThreadMember() {
* <p>Note that this operation relies on the {@link #getThreadMembers() ThreadMember cache} for this ThreadChannel.
* As the cache is likely to be unpopulated, this method is likely to return null.
*
* <p>If this thread was referenced by the {@linkplain Guild#searchMessages() search API},
* only the {@linkplain Guild#getSelfMember() current member} can be retrieved.
*
* <p>Use of {@link #retrieveThreadMember(Member)} is preferred instead, once it is released.
*
* @param member
Expand All @@ -358,6 +364,9 @@ default ThreadMember getThreadMember(@Nonnull Member member) {
* <p>Note that this operation relies on the {@link #getThreadMembers() ThreadMember cache} for this ThreadChannel.
* As the cache is likely to be unpopulated, this method is likely to return null.
*
* <p>If this thread was referenced by the {@linkplain Guild#searchMessages() search API},
* only the {@linkplain Guild#getSelfMember() current member} can be retrieved.
*
* <p>Use of {@link #retrieveThreadMember(Member)} is preferred instead, once it is released.
*
* @param user
Expand All @@ -384,6 +393,9 @@ default ThreadMember getThreadMember(@Nonnull User user) {
* <p>Note that this operation relies on the {@link #getThreadMembers() ThreadMember cache} for this ThreadChannel.
* As the cache is likely to be unpopulated, this method is likely to return null.
*
* <p>If this thread was referenced by the {@linkplain Guild#searchMessages() search API},
* only the {@linkplain Guild#getSelfMember() current member} can be retrieved.
*
* <p>Use of {@link #retrieveThreadMember(Member)} is preferred instead, once it is released.
*
* @param id
Expand All @@ -409,6 +421,9 @@ default ThreadMember getThreadMemberById(@Nonnull String id) {
* <p>Note that this operation relies on the {@link #getThreadMembers() ThreadMember cache} for this ThreadChannel.
* As the cache is likely to be unpopulated, this method is likely to return null.
*
* <p>If this thread was referenced by the {@linkplain Guild#searchMessages() search API},
* only the {@linkplain Guild#getSelfMember() current member} can be retrieved.
*
* <p>Use of {@link #retrieveThreadMember(Member)} is preferred instead, once it is released.
*
* @param id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.dv8tion.jda.api.entities.messages;

import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import org.jetbrains.annotations.Unmodifiable;

import java.util.List;

import javax.annotation.Nonnull;

/**
* Base interface for message search responses.
*
* @see Guild#searchMessages()
*/
public interface MessageSearchResponse {
/**
* Whether the message search results are not ready yet.
*
* <p>If this returns {@code true}, you must use {@link #asNotReady()}
* to retry the request according to the returned {@linkplain NotReady#getRetryAfter() delay}.
*
* <p>If this returns {@code false}, you must use {@link #asResults()} to read the results.
*
* <p><b>Tip:</b> You can use this in a guard condition:
* {@snippet lang=java:
* MessageSearchResponse searchResponse; // Assuming you have a response
* if (searchResponse.isNotReady()) {
* // Reply
* return;
* }
* }
Comment thread
freya022 marked this conversation as resolved.
Outdated
*
* @return {@code true} if the index is not ready yet, {@code false} otherwise
*/
boolean isNotReady();

/**
* Returns this instance as {@link NotReady NotReady}, if it is one.
*
* @throws IllegalStateException
* If the search has succeeded
*
* @return The {@link NotReady NotReady} state
*/
@Nonnull
NotReady asNotReady();

/**
* Returns this instance as {@link Results Results}, if it is one.
*
* @throws IllegalStateException
* If the search is not ready yet
*
* @return The {@link Results Results} state
*/
@Nonnull
Results asResults();

/**
* Represents a response indicating the search index is not yet ready.
*
* @see #asNotReady()
*/
interface NotReady extends MessageSearchResponse {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use a union here, since these two response types have nothing in common. Having asNotReady().asResults() is just confusing.

Suggested change
interface NotReady extends MessageSearchResponse {
interface NotReady {

@freya022 freya022 Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah I forgot to remove the extends, it's fine otherwise, right? maybe NotReady could have a better name

/**
* The number of documents that has been indexed thus far.
*
* @return Number of currently indexed documents
*/
int getDocumentsIndexed();

/**
* The delay (in seconds) after which you should retry the message search.
* <br>If the value is {@code 0}, you should retry the request after a short delay.
*
* @return Delay (in seconds) before retrying the request
*/
int getRetryAfter();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably should have a utility to automatically do this retry logic, maybe as a followup. Our rate-limiter can already handle retries.

Comment thread
freya022 marked this conversation as resolved.
Outdated
}

/**
* Represents a response with the found messages.
*
* @see #asResults()
*/
interface Results extends MessageSearchResponse {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interface Results extends MessageSearchResponse {
interface Results {

/**
* The messages satisfying the search query.
*
* <p>The returned messages will be missing reactions, member objects (unless cached)
* and the containing thread's members will only contain the {@linkplain net.dv8tion.jda.api.entities.Guild#getSelfMember() current member},
* if it has joined the thread.
*
* @return The matching messages
*/
@Nonnull
@Unmodifiable
List<Message> getMessages();

/**
* Whether the guild is still being indexed.
*
* @return {@code true} if the indexing is still being done
*/
boolean isDoingDeepHistoricalIndex();

/**
* The total number of messages. May not be accurate as messages are created/deleted.
*
* @return The total results
*/
int getTotalResults();
}
}
2 changes: 2 additions & 0 deletions src/main/java/net/dv8tion/jda/api/requests/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ public static class Guilds {
public static final Route GET_WELCOME_SCREEN = new Route(GET, "guilds/{guild_id}/welcome-screen");
public static final Route MODIFY_WELCOME_SCREEN = new Route(PATCH, "guilds/{guild_id}/welcome-screen");
public static final Route MODIFY_GUILD_INCIDENTS = new Route(PUT, "guilds/{guild_id}/incident-actions");

public static final Route SEARCH_MESSAGES = new Route(GET, "guilds/{guild_id}/messages/search");
}

public static class Emojis {
Expand Down
Loading