Skip to content

Commit

Permalink
Merge pull request #628 from Bastian/control-user-waiting
Browse files Browse the repository at this point in the history
Control user waiting
  • Loading branch information
Bastian committed Sep 28, 2020
2 parents 954ac2a + f15f583 commit 8fb3481
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 14 deletions.
16 changes: 16 additions & 0 deletions javacord-api/src/main/java/org/javacord/api/DiscordApi.java
Expand Up @@ -237,6 +237,13 @@ default String createBotInvite(Permissions permissions) {
*/
boolean isWaitingForServersOnStartup();

/**
* Checks if Javacord is waiting for all servers to get cached on startup.
*
* @return Whether Javacord is waiting for all users to get cached on startup or not.
*/
boolean isWaitingForUsersOnStartup();

/**
* Updates the status of this bot.
* The update might not be visible immediately as it's through the websocket and only a limited amount of
Expand Down Expand Up @@ -577,6 +584,15 @@ default CompletableFuture<Void> updateAvatar(InputStream avatar, String fileType
*/
boolean isUserCacheEnabled();

/**
* Checks if all users of available servers are in the cache.
*
* @return Whether or not all users of available servers are in the cache.
*/
default boolean hasAllUsersInCache() {
return !getServers().stream().anyMatch(Server::hasAllMembersInCache);
}

/**
* Gets a collection with all currently cached users.
*
Expand Down
24 changes: 24 additions & 0 deletions javacord-api/src/main/java/org/javacord/api/DiscordApiBuilder.java
Expand Up @@ -300,6 +300,30 @@ public boolean isWaitingForServersOnStartup() {
return delegate.isWaitingForServersOnStartup();
}

/**
* Sets if Javacord should wait for all users to be cached.
* If this is set to {@code true}, Javacord will consider Servers with uncached users as unavailable.
*
* <p>Requires the {@link Intent#GUILD_MEMBERS} intent to be set.
*
* @param waitForUsersOnStartup Whether Javacord should wait for all users to be cached or not.
* @return The current instance in order to chain call methods.
*/
public DiscordApiBuilder setWaitForUsersOnStartup(boolean waitForUsersOnStartup) {
delegate.setWaitForUsersOnStartup(waitForUsersOnStartup);
return this;
}

/**
* Checks if Javacord should wait for all users to be cached.
*
* @return If Javacord should wait.
* @see #setWaitForUsersOnStartup(boolean)
*/
public boolean isWaitingForUsersOnStartup() {
return delegate.isWaitingForUsersOnStartup();
}

/**
* Sets if Javacord should register a shutdown hook that disconnects the {@link DiscordApi} instance.
*
Expand Down
Expand Up @@ -427,6 +427,13 @@ default CompletableFuture<Integer> pruneMembers(int days) {
*/
CompletableFuture<Collection<RichInvite>> getInvites();

/**
* Checks if all members of the server are in the cache.
*
* @return Whether or not all members of the server are in the cache.
*/
boolean hasAllMembersInCache();

/**
* Gets a collection with all members of the server.
*
Expand Down
Expand Up @@ -131,6 +131,20 @@ public interface DiscordApiBuilderDelegate {
*/
boolean isWaitingForServersOnStartup();

/**
* Sets the wait for users on startup flag.
*
* @param waitForUsersOnStartup The wait for users on startup flag to set.
*/
void setWaitForUsersOnStartup(boolean waitForUsersOnStartup);

/**
* Checks if Javacord should wait for all users to get cached on startup.
*
* @return If Javacord should wait.
*/
boolean isWaitingForUsersOnStartup();

/**
* Sets if Javacord should register a shutdown hook that disconnects the {@link DiscordApi} instance.
*
Expand Down
Expand Up @@ -108,6 +108,11 @@ public class DiscordApiBuilderDelegateImpl implements DiscordApiBuilderDelegate
*/
private volatile boolean waitForServersOnStartup = true;

/**
* Whether Javacord should wait for all user to get cached on startup or not.
*/
private volatile boolean waitForUsersOnStartup = false;

/**
* Whether the shutdown hook should be registered or not.
*/
Expand Down Expand Up @@ -182,8 +187,8 @@ public CompletableFuture<DiscordApi> login() {
try (CloseableThreadContext.Instance closeableThreadContextInstance =
CloseableThreadContext.put("shard", Integer.toString(currentShard.get()))) {
new DiscordApiImpl(accountType, token, currentShard.get(), totalShards.get(), intents,
waitForServersOnStartup, registerShutdownHook, globalRatelimiter, proxySelector, proxy,
proxyAuthenticator, trustAllCertificates, future, null, preparedListeners,
waitForServersOnStartup, waitForUsersOnStartup, registerShutdownHook, globalRatelimiter,
proxySelector, proxy, proxyAuthenticator, trustAllCertificates, future, null, preparedListeners,
preparedUnspecifiedListeners);
}
return future;
Expand Down Expand Up @@ -350,6 +355,16 @@ public boolean isWaitingForServersOnStartup() {
return waitForServersOnStartup;
}

@Override
public void setWaitForUsersOnStartup(boolean waitForUsersOnStartup) {
this.waitForUsersOnStartup = waitForUsersOnStartup;
}

@Override
public boolean isWaitingForUsersOnStartup() {
return waitForUsersOnStartup;
}

@Override
public void setShutdownHookRegistrationEnabled(boolean registerShutdownHook) {
this.registerShutdownHook = registerShutdownHook;
Expand Down
38 changes: 31 additions & 7 deletions javacord-core/src/main/java/org/javacord/core/DiscordApiImpl.java
Expand Up @@ -226,6 +226,11 @@ public class DiscordApiImpl implements DiscordApi, DispatchQueueSelector {
*/
private final boolean waitForServersOnStartup;

/**
* Whether Javacord should wait for all users to get cached on startup or not.
*/
private final boolean waitForUsersOnStartup;

/**
* The latest gateway latency.
*/
Expand Down Expand Up @@ -379,7 +384,7 @@ public class DiscordApiImpl implements DiscordApi, DispatchQueueSelector {
*/
public DiscordApiImpl(String token, Ratelimiter globalRatelimiter, ProxySelector proxySelector, Proxy proxy,
Authenticator proxyAuthenticator, boolean trustAllCertificates) {
this(AccountType.BOT, token, 0, 1, Collections.emptySet(), true, globalRatelimiter,
this(AccountType.BOT, token, 0, 1, Collections.emptySet(), true, false, globalRatelimiter,
proxySelector, proxy, proxyAuthenticator, trustAllCertificates, null);
}

Expand All @@ -393,6 +398,8 @@ public DiscordApiImpl(String token, Ratelimiter globalRatelimiter, ProxySelector
* @param intents The intents for the events which should be received.
* @param waitForServersOnStartup Whether Javacord should wait for all servers
* to become available on startup or not.
* @param waitForUsersOnStartup Whether Javacord should wait for all users
* to become available on startup or not.
* @param globalRatelimiter The ratelimiter used for global ratelimits.
* @param proxySelector The proxy selector which should be used to determine the proxies that should be
* used to connect to the Discord REST API and websocket.
Expand All @@ -409,15 +416,16 @@ public DiscordApiImpl(
int totalShards,
Set<Intent> intents,
boolean waitForServersOnStartup,
boolean waitForUsersOnStartup,
Ratelimiter globalRatelimiter,
ProxySelector proxySelector,
Proxy proxy,
Authenticator proxyAuthenticator,
boolean trustAllCertificates,
CompletableFuture<DiscordApi> ready
) {
this(accountType, token, currentShard, totalShards, intents, waitForServersOnStartup, true, globalRatelimiter,
proxySelector, proxy, proxyAuthenticator, trustAllCertificates, ready, null,
this(accountType, token, currentShard, totalShards, intents, waitForServersOnStartup, waitForUsersOnStartup,
true, globalRatelimiter, proxySelector, proxy, proxyAuthenticator, trustAllCertificates, ready, null,
Collections.emptyMap(), Collections.emptyList());
}

Expand All @@ -431,6 +439,8 @@ public DiscordApiImpl(
* @param intents The intents for the events which should be received.
* @param waitForServersOnStartup Whether Javacord should wait for all servers
* to become available on startup or not.
* @param waitForUsersOnStartup Whether Javacord should wait for all users
* to become available on startup or not.
* @param globalRatelimiter The ratelimiter used for global ratelimits.
* @param proxySelector The proxy selector which should be used to determine the proxies that should be
* used to connect to the Discord REST API and websocket.
Expand All @@ -448,16 +458,17 @@ private DiscordApiImpl(
int totalShards,
Set<Intent> intents,
boolean waitForServersOnStartup,
boolean waitForUsersOnStartup,
Ratelimiter globalRatelimiter,
ProxySelector proxySelector,
Proxy proxy,
Authenticator proxyAuthenticator,
boolean trustAllCertificates,
CompletableFuture<DiscordApi> ready,
Dns dns) {
this(accountType, token, currentShard, totalShards, intents, waitForServersOnStartup, true, globalRatelimiter,
proxySelector, proxy, proxyAuthenticator, trustAllCertificates, ready, dns, Collections.emptyMap(),
Collections.emptyList());
this(accountType, token, currentShard, totalShards, intents, waitForServersOnStartup, waitForUsersOnStartup,
true, globalRatelimiter, proxySelector, proxy, proxyAuthenticator, trustAllCertificates, ready, dns,
Collections.emptyMap(), Collections.emptyList());
}

/**
Expand All @@ -466,9 +477,11 @@ private DiscordApiImpl(
* @param token The token used to connect without any account type specific prefix.
* @param currentShard The current shard the bot should connect to.
* @param totalShards The total amount of shards.
* @param intents The intents for the events which should be received.
* @param intents The intents for the events which should be received.
* @param waitForServersOnStartup Whether Javacord should wait for all servers
* to become available on startup or not.
* @param waitForUsersOnStartup Whether Javacord should wait for all users
* to become available on startup or not.
* @param registerShutdownHook Whether the shutdown hook should be registered or not.
* @param globalRatelimiter The ratelimiter used for global ratelimits.
* @param proxySelector The proxy selector which should be used to determine the proxies that should be
Expand All @@ -490,6 +503,7 @@ public DiscordApiImpl(
int totalShards,
Set<Intent> intents,
boolean waitForServersOnStartup,
boolean waitForUsersOnStartup,
boolean registerShutdownHook,
Ratelimiter globalRatelimiter,
ProxySelector proxySelector,
Expand All @@ -507,6 +521,7 @@ public DiscordApiImpl(
this.currentShard = currentShard;
this.totalShards = totalShards;
this.waitForServersOnStartup = waitForServersOnStartup;
this.waitForUsersOnStartup = waitForUsersOnStartup;
this.globalRatelimiter = globalRatelimiter;
this.proxySelector = proxySelector;
this.proxy = proxy;
Expand All @@ -521,6 +536,10 @@ public DiscordApiImpl(
throw new IllegalStateException("proxy and proxySelector must not be configured both");
}

if (!intents.contains(Intent.GUILD_MEMBERS) && isWaitingForUsersOnStartup()) {
throw new IllegalArgumentException("Cannot wait for users when GUILD_MEMBERS intent is not set!");
}

OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder()
.addInterceptor(chain -> chain.proceed(chain.request()
.newBuilder()
Expand Down Expand Up @@ -1376,6 +1395,11 @@ public boolean isWaitingForServersOnStartup() {
return waitForServersOnStartup;
}

@Override
public boolean isWaitingForUsersOnStartup() {
return waitForUsersOnStartup;
}

/**
* The proxy selector which should be used to determine the proxies that should be used to connect to the Discord
* REST API and websocket.
Expand Down
Expand Up @@ -22,7 +22,6 @@
import org.javacord.api.entity.channel.ServerTextChannel;
import org.javacord.api.entity.channel.ServerVoiceChannel;
import org.javacord.api.entity.emoji.KnownCustomEmoji;
import org.javacord.api.entity.intent.Intent;
import org.javacord.api.entity.permission.Role;
import org.javacord.api.entity.server.Ban;
import org.javacord.api.entity.server.BoostLevel;
Expand Down Expand Up @@ -274,7 +273,7 @@ public class ServerImpl implements Server, Cleanupable, InternalServerAttachable
*/
public ServerImpl(DiscordApiImpl api, JsonNode data) {
this.api = api;
ready = !api.hasUserCacheEnabled() || !api.getIntents().contains(Intent.GUILD_PRESENCES);
ready = !api.hasUserCacheEnabled() || !api.isWaitingForUsersOnStartup();

id = Long.parseLong(data.get("id").asText());
name = data.get("name").asText();
Expand Down Expand Up @@ -382,7 +381,7 @@ public ServerImpl(DiscordApiImpl api, JsonNode data) {
}

if (
(isLarge() || !api.getIntents().contains(Intent.GUILD_PRESENCES))
(isLarge() || !api.isWaitingForUsersOnStartup())
&& getMembers().size() < getMemberCount()
&& api.hasUserCacheEnabled()
) {
Expand Down Expand Up @@ -1216,6 +1215,11 @@ public CompletableFuture<Collection<RichInvite>> getInvites() {
});
}

@Override
public boolean hasAllMembersInCache() {
return getRealMembers().size() >= getMemberCount();
}

@Override
public Set<User> getMembers() {
return api.getEntityCache().get().getMemberCache()
Expand Down
Expand Up @@ -631,7 +631,7 @@ public void onTextMessage(WebSocket websocket, String text) throws Exception {
allServersLoaded = api.getUnavailableServers().isEmpty();
if (allServersLoaded) {
allUsersLoaded = !api.hasUserCacheEnabled()
|| !api.getIntents().contains(Intent.GUILD_PRESENCES)
|| !api.isWaitingForUsersOnStartup()
|| api.getAllServers().stream()
.map(ServerImpl.class::cast)
.noneMatch(server -> server.getMemberCount() != server.getRealMembers().size());
Expand Down
Expand Up @@ -320,7 +320,7 @@ class DiscordApiImplTest extends Specification {
MockProxyManager.setSocks4SystemProperties()

and:
def api = new DiscordApiImpl(AccountType.BOT, 'fakeBotToken', 0, 1, Collections.emptySet(), false, null, null, null, null, true,
def api = new DiscordApiImpl(AccountType.BOT, 'fakeBotToken', 0, 1, Collections.emptySet(), false, false, null, null, null, null, true,
null, { [InetAddress.getLoopbackAddress()] })

when:
Expand Down

0 comments on commit 8fb3481

Please sign in to comment.