From daf17d1f0a2a2886aae697dc2d7aa218417c7686 Mon Sep 17 00:00:00 2001 From: Mitmocc <33007498+Mitmocc@users.noreply.github.com> Date: Sun, 23 Oct 2022 17:42:28 +0200 Subject: [PATCH] Add support for guild scheduled events v2 (#2047) --- src/main/java/net/dv8tion/jda/api/JDA.java | 81 ++++ .../java/net/dv8tion/jda/api/Permission.java | 1 + .../net/dv8tion/jda/api/audit/ActionType.java | 16 + .../net/dv8tion/jda/api/audit/TargetType.java | 1 + .../net/dv8tion/jda/api/entities/Guild.java | 243 ++++++++++ .../jda/api/entities/ScheduledEvent.java | 440 ++++++++++++++++++ .../GenericScheduledEventGatewayEvent.java | 61 +++ .../GenericScheduledEventUserEvent.java | 123 +++++ .../ScheduledEventCreateEvent.java | 40 ++ .../ScheduledEventDeleteEvent.java | 40 ++ .../ScheduledEventUserAddEvent.java | 41 ++ .../ScheduledEventUserRemoveEvent.java | 41 ++ .../guild/scheduledevent/package-info.java | 24 + .../GenericScheduledEventUpdateEvent.java | 80 ++++ .../ScheduledEventUpdateDescriptionEvent.java | 71 +++ .../ScheduledEventUpdateEndTimeEvent.java | 72 +++ .../ScheduledEventUpdateImageEvent.java | 84 ++++ .../ScheduledEventUpdateLocationEvent.java | 84 ++++ .../update/ScheduledEventUpdateNameEvent.java | 84 ++++ .../ScheduledEventUpdateStartTimeEvent.java | 85 ++++ .../ScheduledEventUpdateStatusEvent.java | 85 ++++ .../jda/api/hooks/ListenerAdapter.java | 19 + .../api/managers/ScheduledEventManager.java | 302 ++++++++++++ .../jda/api/requests/ErrorResponse.java | 4 +- .../jda/api/requests/GatewayIntent.java | 10 + .../restaction/ScheduledEventAction.java | 160 +++++++ ...ScheduledEventMembersPaginationAction.java | 55 +++ .../jda/api/utils/cache/CacheFlag.java | 8 +- .../dv8tion/jda/api/utils/data/DataArray.java | 52 +++ .../jda/api/utils/data/DataObject.java | 52 +++ .../net/dv8tion/jda/internal/JDAImpl.java | 7 + .../jda/internal/entities/EntityBuilder.java | 69 +++ .../jda/internal/entities/GuildImpl.java | 51 ++ .../internal/entities/ScheduledEventImpl.java | 279 +++++++++++ .../internal/handle/ChannelDeleteHandler.java | 40 +- .../jda/internal/handle/EventCache.java | 2 +- .../internal/handle/GuildDeleteHandler.java | 12 +- .../handle/ScheduledEventCreateHandler.java | 54 +++ .../handle/ScheduledEventDeleteHandler.java | 59 +++ .../handle/ScheduledEventUpdateHandler.java | 136 ++++++ .../handle/ScheduledEventUserHandler.java | 66 +++ .../managers/ScheduledEventManagerImpl.java | 233 ++++++++++ .../dv8tion/jda/internal/requests/Route.java | 7 + .../internal/requests/WebSocketClient.java | 5 + .../restaction/ScheduledEventActionImpl.java | 221 +++++++++ ...duledEventMembersPaginationActionImpl.java | 96 ++++ 46 files changed, 3772 insertions(+), 24 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/ScheduledEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventGatewayEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventUserEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventCreateEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventDeleteEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserAddEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserRemoveEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/package-info.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/GenericScheduledEventUpdateEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateDescriptionEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateEndTimeEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateImageEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateLocationEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateNameEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStartTimeEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStatusEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/managers/ScheduledEventManager.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/ScheduledEventAction.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/ScheduledEventMembersPaginationAction.java create mode 100644 src/main/java/net/dv8tion/jda/internal/entities/ScheduledEventImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventCreateHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventDeleteHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUpdateHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUserHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/managers/ScheduledEventManagerImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/ScheduledEventActionImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/ScheduledEventMembersPaginationActionImpl.java diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index c6f903a343..b0b372034e 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1238,6 +1238,87 @@ default List getRolesByName(@Nonnull String name, boolean ignoreCase) { return getRoleCache().getElementsByName(name, ignoreCase); } + /** + * {@link SnowflakeCacheView} of + * all cached {@link ScheduledEvent ScheduledEvents} visible to this JDA session. + * + * @return {@link SnowflakeCacheView} + */ + @Nonnull + SnowflakeCacheView getScheduledEventCache(); + + /** + * An unmodifiable list of all {@link ScheduledEvent ScheduledEvents} of all connected + * {@link net.dv8tion.jda.api.entities.Guild Guilds}. + * + *

This copies the backing store into a list. This means every call + * creates a new list with O(n) complexity. It is recommended to store this into + * a local variable or use {@link #getScheduledEventCache()} and use its more efficient + * versions of handling these values. + * + * @return Possibly-empty immutable list of all known {@link ScheduledEvent ScheduledEvents}. + */ + @Nonnull + default List getScheduledEvents() + { + return getScheduledEventCache().asList(); + } + + /** + * This returns the {@link ScheduledEvent} which has the same id as the one provided. + *
If there is no known {@link ScheduledEvent} with an id that matches the provided + * one, then this returns {@code null}. + * + * @param id + * The id of the {@link ScheduledEvent}. + * + * @throws java.lang.NumberFormatException + * If the provided {@code id} cannot be parsed by {@link Long#parseLong(String)} + * + * @return Possibly-null {@link ScheduledEvent} with a matching id. + */ + @Nullable + default ScheduledEvent getScheduledEventById(@Nonnull String id) + { + return getScheduledEventCache().getElementById(id); + } + + /** + * This returns the {@link ScheduledEvent} which has the same id as the one provided. + *
If there is no known {@link ScheduledEvent} with an id that matches the provided + * one, then this returns {@code null}. + * + * @param id + * The id of the {@link ScheduledEvent}. + * + * @return Possibly-null {@link ScheduledEvent} with a matching id. + */ + @Nullable + default ScheduledEvent getScheduledEventById(long id) + { + return getScheduledEventCache().getElementById(id); + } + + /** + * An unmodifiable list of all {@link ScheduledEvent ScheduledEvents} that have the same name as the one provided. + *
If there are no {@link ScheduledEvent ScheduledEvents} with the provided name, then this returns an empty list. + * + * @param name + * The name of the requested {@link ScheduledEvent}. + * @param ignoreCase + * Whether to ignore case or not when comparing the provided name to each {@link ScheduledEvent#getName()}. + * + * @throws IllegalArgumentException + * If the provided name is null. + * + * @return Possibly-empty immutable list of all the {@link ScheduledEvent ScheduledEvents} that all have the + * same name as the provided name. + */ + @Nonnull + default List getScheduledEventsByName(@Nonnull String name, boolean ignoreCase) + { + return getScheduledEventCache().getElementsByName(name, ignoreCase); + } @Nullable @Override diff --git a/src/main/java/net/dv8tion/jda/api/Permission.java b/src/main/java/net/dv8tion/jda/api/Permission.java index 3a68b95a8b..d2434c8c1d 100644 --- a/src/main/java/net/dv8tion/jda/api/Permission.java +++ b/src/main/java/net/dv8tion/jda/api/Permission.java @@ -38,6 +38,7 @@ public enum Permission MANAGE_PERMISSIONS( 28, false, true, "Manage Permissions"), MANAGE_WEBHOOKS( 29, true, true, "Manage Webhooks"), MANAGE_EMOJIS_AND_STICKERS(30, true, false, "Manage Emojis and Stickers"), + MANAGE_EVENTS( 33, true, true, "Manage Events"), // Membership Permissions CREATE_INSTANT_INVITE(0, true, true, "Create Instant Invite"), diff --git a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java index bdeaa39bca..c77764b6ca 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java +++ b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java @@ -17,6 +17,7 @@ package net.dv8tion.jda.api.audit; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.ScheduledEvent; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; @@ -494,6 +495,21 @@ public enum ActionType */ STAGE_INSTANCE_DELETE(85, TargetType.STAGE_INSTANCE), + /** + * A user created a {@link ScheduledEvent ScheduledEvent} + */ + SCHEDULED_EVENT_CREATE(100, TargetType.SCHEDULED_EVENT), + + /** + * A user updated a {@link ScheduledEvent ScheduledEvent} + */ + SCHEDULED_EVENT_UPDATE(101, TargetType.SCHEDULED_EVENT), + + /** + * A user deleted/cancelled a {@link ScheduledEvent ScheduledEvent} + */ + SCHEDULED_EVENT_DELETE(102, TargetType.SCHEDULED_EVENT), + /** * An Administrator created a {@link net.dv8tion.jda.api.entities.sticker.GuildSticker GuildSticker}. * diff --git a/src/main/java/net/dv8tion/jda/api/audit/TargetType.java b/src/main/java/net/dv8tion/jda/api/audit/TargetType.java index 7865b7395c..98c5918365 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/TargetType.java +++ b/src/main/java/net/dv8tion/jda/api/audit/TargetType.java @@ -42,5 +42,6 @@ public enum TargetType STAGE_INSTANCE, STICKER, THREAD, + SCHEDULED_EVENT, UNKNOWN } diff --git a/src/main/java/net/dv8tion/jda/api/entities/Guild.java b/src/main/java/net/dv8tion/jda/api/entities/Guild.java index ddab1da330..cd41f343d2 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -72,6 +72,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.Duration; +import java.time.OffsetDateTime; import java.time.temporal.TemporalAccessor; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -1273,6 +1274,96 @@ default List getMembersWithRoles(@Nonnull Collection roles) @Nonnull MemberCacheView getMemberCache(); + /** + * Sorted {@link SnowflakeCacheView} of + * all cached {@link ScheduledEvent ScheduledEvents} of this Guild. + *
Scheduled events are sorted by their start time, and events that start at the same time + * are sorted by their snowflake ID. + * + * @return {@link SortedSnowflakeCacheView} + */ + @Nonnull + SortedSnowflakeCacheView getScheduledEventCache(); + + /** + * Gets a list of all {@link ScheduledEvent ScheduledEvents} in this Guild that have the same + * name as the one provided. + *
If there are no {@link ScheduledEvent ScheduledEvents} with the provided name, + * then this returns an empty list. + * + * @param name + * The name used to filter the returned {@link ScheduledEvent} objects. + * @param ignoreCase + * Determines if the comparison ignores case when comparing. True - case insensitive. + * + * @throws java.lang.IllegalArgumentException + * If the name is blank, empty or {@code null} + * + * @return Possibly-empty immutable list of all ScheduledEvent names that match the provided name. + */ + @Nonnull + default List getScheduledEventsByName(@Nonnull String name, boolean ignoreCase) + { + return getScheduledEventCache().getElementsByName(name, ignoreCase); + } + + /** + * Gets a {@link ScheduledEvent} from this guild that has the same id as the + * one provided. This method is similar to {@link JDA#getScheduledEventById(String)}, but it only + * checks this specific Guild for a scheduled event. + *
If there is no {@link ScheduledEvent} with an id that matches the provided + * one, then this returns {@code null}. + * + * @param id + * The id of the {@link ScheduledEvent}. + * + * @throws java.lang.NumberFormatException + * If the provided {@code id} cannot be parsed by {@link Long#parseLong(String)} + * + * @return Possibly-null {@link ScheduledEvent} with matching id. + */ + @Nullable + default ScheduledEvent getScheduledEventById(@Nonnull String id) + { + return getScheduledEventCache().getElementById(id); + } + + /** + * Gets a {@link ScheduledEvent} from this guild that has the same id as the + * one provided. This method is similar to {@link JDA#getScheduledEventById(long)}, but it only + * checks this specific Guild for a scheduled event. + *
If there is no {@link ScheduledEvent} with an id that matches the provided + * one, then this returns {@code null}. + * + * @param id + * The id of the {@link ScheduledEvent}. + * + * @return Possibly-null {@link ScheduledEvent} with matching id. + */ + @Nullable + default ScheduledEvent getScheduledEventById(long id) + { + return getScheduledEventCache().getElementById(id); + } + + /** + * Gets all {@link ScheduledEvent ScheduledEvents} in this guild. + *
Scheduled events are sorted by their start time, and events that start at the same time + * are sorted by their snowflake ID. + * + *

This copies the backing store into a list. This means every call + * creates a new list with O(n) complexity. It is recommended to store this into + * a local variable or use {@link #getScheduledEventCache()} and use its more efficient + * versions of handling these values. + * + * @return Possibly-empty immutable List of {@link ScheduledEvent ScheduledEvents}. + */ + @Nonnull + default List getScheduledEvents() + { + return getScheduledEventCache().asList(); + } + @Nonnull @Override SortedSnowflakeCacheView getStageChannelCache(); @@ -2949,6 +3040,54 @@ default Task> retrieveMembersByIds(boolean includePresence, @Nonnul @CheckReturnValue RestAction> retrieveActiveThreads(); + /** + * Retrieves a {@link ScheduledEvent} by its ID. + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

+ * + * @param id + * The ID of the {@link ScheduledEvent} + * + * @return {@link RestAction} - Type: {@link ScheduledEvent} + * + * @see #getScheduledEventById(long) + */ + @Nonnull + @CheckReturnValue + default CacheRestAction retrieveScheduledEventById(long id) + { + return retrieveScheduledEventById(Long.toUnsignedString(id)); + } + + /** + * Retrieves a {@link ScheduledEvent} by its ID. + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#SCHEDULED_EVENT ErrorResponse.UNKNOWN_SCHEDULED_EVENT} + *
    A scheduled event with the specified ID does not exist in this guild, or the currently logged in user does not + * have access to it.
  • + *
+ * + * @param id + * The ID of the {@link ScheduledEvent} + * + * @throws IllegalArgumentException + * If the specified ID is {@code null} or empty + * @throws NumberFormatException + * If the specified ID cannot be parsed by {@link Long#parseLong(String)} + * + * @return {@link RestAction} - Type: {@link ScheduledEvent} + * + * @see #getScheduledEventById(long) + */ + @Nonnull + @CheckReturnValue + CacheRestAction retrieveScheduledEventById(@Nonnull String id); + /* From GuildController */ /** @@ -4487,6 +4626,110 @@ default AuditableRestAction createSticker(@Nonnull String name, @N @CheckReturnValue AuditableRestAction deleteSticker(@Nonnull StickerSnowflake id); + /** + * Creates a new {@link ScheduledEvent}. + * Events created with this method will be of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL}. + * These events are set to take place at an external location. + * + *

Requirements
+ * + * Events are required to have a name, location and start time. + * Additionally, an end time must also be specified for events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL}. + * {@link Permission#MANAGE_EVENTS} is required on the guild level in order to create this type of event. + * + *

Example
+ *

{@code
+     * guild.createScheduledEvent("Cactus Beauty Contest", "Mike's Backyard", OffsetDateTime.now().plusHours(1), OffsetDateTime.now().plusHours(3))
+     *     .setDescription("Come and have your cacti judged! _Must be spikey to enter_")
+     *     .queue();
+     * }
+ * + * @param name + * the name for this scheduled event, 1-100 characters + * @param location + * the external location for this scheduled event, 1-100 characters + * @param startTime + * the start time for this scheduled event, can't be in the past or after the end time + * @param endTime + * the end time for this scheduled event, has to be later than the start time + * + * @throws java.lang.IllegalArgumentException + *
    + *
  • If a required parameter is {@code null} or empty
  • + *
  • If the start time is in the past
  • + *
  • If the end time is before the start time
  • + *
  • If the name is longer than 100 characters
  • + *
  • If the description is longer than 1000 characters
  • + *
  • If the location is longer than 100 characters
  • + *
+ * + * @return {@link ScheduledEventAction} + */ + @Nonnull + @CheckReturnValue + ScheduledEventAction createScheduledEvent(@Nonnull String name, @Nonnull String location, @Nonnull OffsetDateTime startTime, @Nonnull OffsetDateTime endTime); + + /** + * Creates a new {@link ScheduledEvent}. + * + *

Requirements
+ * + * Events are required to have a name, channel and start time. Depending on the + * type of channel provided, an event will be of one of two different {@link ScheduledEvent.Type Types}: + *

    + *
  1. + * {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + *
    These events are set to take place inside of a {@link StageChannel}. The + * following permissions are required in the specified stage channel in order to create an event there: + *
      + *
    • {@link Permission#MANAGE_EVENTS}
    • + *
    • {@link Permission#MANAGE_CHANNEL}
    • + *
    • {@link Permission#VOICE_MUTE_OTHERS}
    • + *
    • {@link Permission#VOICE_MOVE_OTHERS}}
    • + *
    + *
  2. + *
  3. + * {@link ScheduledEvent.Type#VOICE Type.VOICE} + *
    These events are set to take place inside of a {@link VoiceChannel}. The + * following permissions are required in the specified voice channel in order to create an event there: + *
      + *
    • {@link Permission#MANAGE_EVENTS}
    • + *
    • {@link Permission#VIEW_CHANNEL}
    • + *
    • {@link Permission#VOICE_CONNECT}
    • + *
    + *
  4. + *
+ * + *

Example
+ *

{@code
+     * guild.createScheduledEvent("Cactus Beauty Contest", guild.getGuildChannelById(channelId), OffsetDateTime.now().plusHours(1))
+     *     .setDescription("Come and have your cacti judged! _Must be spikey to enter_")
+     *     .queue();
+     * }
+ * + * @param name + * the name for this scheduled event, 1-100 characters + * @param channel + * the voice or stage channel where this scheduled event will take place + * @param startTime + * the start time for this scheduled event, can't be in the past + * + * @throws java.lang.IllegalArgumentException + *
    + *
  • If a required parameter is {@code null} or empty
  • + *
  • If the start time is in the past
  • + *
  • If the name is longer than 100 characters
  • + *
  • If the description is longer than 1000 characters
  • + *
  • If the channel is not a Stage or Voice channel
  • + *
  • If the channel is not from the same guild as the scheduled event
  • + *
+ * + * @return {@link ScheduledEventAction} + */ + @Nonnull + @CheckReturnValue + ScheduledEventAction createScheduledEvent(@Nonnull String name, @Nonnull GuildChannel channel, @Nonnull OffsetDateTime startTime); + /** * 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 diff --git a/src/main/java/net/dv8tion/jda/api/entities/ScheduledEvent.java b/src/main/java/net/dv8tion/jda/api/entities/ScheduledEvent.java new file mode 100644 index 0000000000..c760cb7b36 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/ScheduledEvent.java @@ -0,0 +1,440 @@ +/* + * 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; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +import net.dv8tion.jda.api.managers.ScheduledEventManager; +import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; +import net.dv8tion.jda.api.requests.restaction.pagination.ScheduledEventMembersPaginationAction; +import net.dv8tion.jda.api.requests.restaction.pagination.PaginationAction; +import net.dv8tion.jda.api.utils.ImageProxy; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; + +/** + * A class representing a {@link ScheduledEvent} (The events that show up under the events tab in the Official Discord Client). + * These events should not be confused with {@link net.dv8tion.jda.api.events Gateway Events}, + * which are fired by Discord whenever something interesting happens + * (ie., a {@link net.dv8tion.jda.api.events.message.MessageDeleteEvent MessageDeleteEvent} gets fired whenever a message gets deleted). + */ +public interface ScheduledEvent extends ISnowflake, Comparable +{ + /** + * The maximum allowed length for an event's name. + */ + int MAX_NAME_LENGTH = 100; + + /** + * The maximum allowed length for an event's description. + */ + int MAX_DESCRIPTION_LENGTH = 1000; + + /** + * The maximum allowed length for an event's location. + */ + int MAX_LOCATION_LENGTH = 100; + + /** + * Template for {@link #getImageUrl()} + */ + String IMAGE_URL = "https://cdn.discordapp.com/guild-events/%s/%s.%s"; + + /** + * The name of the event. + * + * @return The event's name + */ + @Nonnull + String getName(); + + /** + * The description of the event. + * + * @return The description, or {@code null} if none is specified + */ + @Nullable + String getDescription(); + + /** + * The cover image url of the event. + *

Links to a potentially heavily compressed image. You can append a size parameter to the URL if needed. Example: {@code ?size=4096} + * + * @return The image url, or {@code null} if none is specified + */ + @Nullable + String getImageUrl(); + + /** + * Returns an {@link ImageProxy} for this events cover image. + * + * @return The {@link ImageProxy} for this events cover image or null if no image is defined + * + * @see #getImageUrl() + */ + @Nullable + default ImageProxy getImage() + { + final String imageUrl = getImageUrl(); + return imageUrl == null ? null : new ImageProxy(imageUrl); + } + + /** + * The user who originally created the event. + *

May return {@code null} if user has deleted their account, the {@link User} object is not cached + * or the event was created before Discord started keeping track of event creators on October 21st, 2021. + * + * @return {@link User} object representing the event's creator or {@code null}. + * + * @see #getCreatorId() + * @see #getCreatorIdLong() + */ + @Nullable + User getCreator(); + + /** + * The ID of the user who originally created this event. + *

This method may return 0 if the event was created before Discord started keeping track of event creators on October 21st, 2021. + * + * @return The ID of the user who created this event, or 0 if no user is associated with creating this event. + * + * @see #getCreatorId() + * @see #getCreator() + */ + long getCreatorIdLong(); + + /** + * The ID of the user who originally created this event. + *
This method may return {@code null} if the event was created before Discord started keeping track of event creators on October 21st, 2021. + * + * @return The Id of the user who created this event, or {@code null} if no user is associated with creating this event. + * + * @see #getCreatorIdLong() + * @see #getCreator() + */ + @Nullable + default String getCreatorId() + { + return getCreatorIdLong() == 0 ? null : Long.toUnsignedString(getCreatorIdLong()); + } + + /** + * The {@link Status status} of the scheduled event. + * + * @return The status, or {@link Status#UNKNOWN} if the status is unknown to JDA. + * + */ + @Nonnull + Status getStatus(); + + /** + * The {@link Type type} of the scheduled event. + * + * @return The type, or {@link Type#UNKNOWN} if the type is unknown to JDA. + */ + @Nonnull + Type getType(); + + /** + * The time the event is set to start at. + * + * @return The time the event is set to start at + * + * @see #getEndTime() + */ + @Nonnull + OffsetDateTime getStartTime(); + + /** + * The time the event is set to end at. + *
The end time is only required for external events, + * which are events that are not associated with a stage or voice channel. + * + * @return The time the event is set to end at. This can't be {@code null} for events of + * {@link Type#EXTERNAL}, but can be null for other types. + * + * @see #getType() + * @see #getStartTime() + */ + @Nullable + OffsetDateTime getEndTime(); + + /** + * The guild channel the event is set to take place in. + *
Note that this method is only applicable to events which are not of {@link Type#STAGE_INSTANCE} or {@link Type#VOICE}. + * + * @return The guild channel, or {@code null} if the guild channel was deleted + * or if the event is of {@link Type#EXTERNAL} + * + * @see #getType() + * @see #getLocation() + */ + @Nullable + GuildChannelUnion getChannel(); + + /** + * The location the event is set to take place in. + * This will return the channel id for {@link Type#STAGE_INSTANCE} and {@link Type#VOICE}. + * + * @return The channel id or the external location of the event + * + * @see #getType() + * @see #getChannel() + */ + @Nonnull + String getLocation(); + + /** + * Deletes this Scheduled Event. + * + *

Possible ErrorResponses include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#SCHEDULED_EVENT UNKNOWN_SCHEDULED_EVENT} + *
    If the the event was already deleted.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS} + *
    The send request was attempted after the account lost + * {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS} in the guild.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS} + *
    If we were removed from the Guild
  • + *
+ * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If we don't have the permission to {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS MANAGE_EVENTS} + * + * @return {@link AuditableRestAction} + */ + @Nonnull + @CheckReturnValue + AuditableRestAction delete(); + + /** + * A {@link PaginationAction PaginationAction} implementation + * that allows to {@link Iterable iterate} over all {@link net.dv8tion.jda.api.entities.Member Members} interested in this Event. + * + *
This iterates in ascending order by member id. + * + *

Possible ErrorResponses include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#SCHEDULED_EVENT} + *
    If the the event was already deleted.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS} + *
    If we were removed from the Guild or can't view the events channel (Location)
  • + *
+ * + * @return {@link ScheduledEventMembersPaginationAction} + */ + @Nonnull + @CheckReturnValue + ScheduledEventMembersPaginationAction retrieveInterestedMembers(); + + /** + * The amount of users who are interested in attending the event. + *

This method only returns the cached count, and may not be consistent with the live count. Discord may additionally not + * provide an interested user count for some {@link ScheduledEvent} objects returned from the Guild's or JDA's + * cache, and this method may return -1 as a result. However, event's retrieved using {@link Guild#retrieveScheduledEventById(long)} + * will always contain an interested user count. + * + * @return The amount of users who are interested in attending the event + * + * @see Guild#retrieveScheduledEventById(long) + * @see Guild#retrieveScheduledEventById(String) + */ + int getInterestedUserCount(); + + /** + * The guild that this event was created in + * + * @return The guild + */ + @Nonnull + Guild getGuild(); + + /** + * The JDA instance associated with this event object + * + * @return The JDA instance + */ + @Nonnull + default JDA getJDA() + { + return getGuild().getJDA(); + } + + /** + * The {@link ScheduledEventManager} for this event. + *
In the ScheduledEventManager, you can modify all values and also start, end, or cancel events. + *
You can modify multiple fields in one request by chaining setters before calling {@link net.dv8tion.jda.api.requests.RestAction#queue() RestAction.queue()}. + * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the currently logged in account does not have {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS} + * + * @return The ScheduledEventManager of this event + */ + @Nonnull + ScheduledEventManager getManager(); + + /** + * Compares two {@link ScheduledEvent} objects based on their scheduled start times. + *
If two events are set to start at the same time, the comparison will be made based on their snowflake ID. + * + * @param scheduledEvent + * The provided scheduled event + * + * @throws IllegalArgumentException + * If the provided scheduled event is {@code null}, from a different {@link Guild}, or is not a valid + * scheduled event provided by JDA. + * + * @return A negative number if the original event (which is the event that the {@link #compareTo(ScheduledEvent) compareTo} + * method is called upon) starts sooner than the provided event, or positive if it will start later than + * the provided event. If both events are set to start at the same time, then the result will be negative if the original + * event's snowflake ID is less than the provided event's ID, positive if it is greater than, or 0 if they + * are the same. + * + * @see Comparable#compareTo(Object) + * @see #getStartTime() + * @see #getIdLong() + */ + @Override + int compareTo(@Nonnull ScheduledEvent scheduledEvent); + + /** + * Represents the status of a scheduled event. + * + * @see ScheduledEvent#getStatus + */ + enum Status + { + UNKNOWN(-1), + SCHEDULED(1), + ACTIVE(2), + COMPLETED(3), + CANCELED(4); + + private final int key; + + Status(int key) + { + this.key = key; + } + + /** + * The Discord id key for this Status. + * + * @return The id key for this Status + */ + public int getKey() + { + return key; + } + + /** + * Used to retrieve a Status based on a Discord id key. + * + * @param key + * The Discord id key representing the requested Status. + * + * @return The Status related to the provided key, or {@link #UNKNOWN Status.UNKNOWN} if the key is not recognized. + */ + @Nonnull + public static Status fromKey(int key) + { + for (Status status : Status.values()) + { + if (status.getKey() == key) + return status; + } + + return UNKNOWN; + } + } + + /** + * Represents what type of event an event is, or where the event will be taking place at. + */ + enum Type + { + /** + * Unknown future types that may be added by Discord which aren't represented in JDA yet. + */ + UNKNOWN(-1), + /** + * An event with it's own {@link net.dv8tion.jda.api.entities.StageInstance StageInstance} + */ + STAGE_INSTANCE(1), + /** + * An event inside a {@link net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel VoiceChannel} + */ + VOICE(2), + /** + * An event held externally. + */ + EXTERNAL(3); + + private final int key; + + Type(int key) + { + this.key = key; + } + + /** + * The Discord id key used to represent the scheduled event type. + * + * @return The id key used by discord for this scheduled event type. + */ + public int getKey() + { + return key; + } + + /** + * Whether the event is scheduled to be held in a {@link GuildChannel}. + * + * @return True, if the event is scheduled to be held in a {@link GuildChannel} + */ + public boolean isChannel() + { + return this == STAGE_INSTANCE || this == VOICE; + } + + /** + * Used to retrieve a Type based on a Discord id key. + * + * @param key + * The Discord id key representing the requested Type. + * + * @return The Type related to the provided key, or {@link #UNKNOWN Type.UNKNOWN} if the key is not recognized. + */ + @Nonnull + public static Type fromKey(int key) + { + for (Type type : Type.values()) + { + if (type.getKey() == key) + return type; + } + + return UNKNOWN; + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventGatewayEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventGatewayEvent.java new file mode 100644 index 0000000000..54c9648a65 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventGatewayEvent.java @@ -0,0 +1,61 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.events.guild.GenericGuildEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a gateway event relating to a {@link ScheduledEvent} has been fired. + * + *

It should be noted that a {@link ScheduledEvent} is not an + * actual gateway event found in the {@link net.dv8tion.jda.api.events} package, but are rather entities similar to + * {@link net.dv8tion.jda.api.entities.User User} or {@link net.dv8tion.jda.api.entities.channel.concrete.TextChannel TextChannel} objects + * representing a scheduled event. + * + *

Requirements
+ * + *

These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + *

This class may be used to check if a gateway event is related to a {@link ScheduledEvent} + * as all gateway events in the {@link net.dv8tion.jda.api.events.guild.scheduledevent} package extend this class. + */ +public abstract class GenericScheduledEventGatewayEvent extends GenericGuildEvent +{ + protected final ScheduledEvent scheduledEvent; + + public GenericScheduledEventGatewayEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent) + { + super(api, responseNumber, scheduledEvent.getGuild()); + this.scheduledEvent = scheduledEvent; + } + + /** + * The {@link ScheduledEvent} + * + * @return The Scheduled Event + */ + @Nonnull + public ScheduledEvent getScheduledEvent() + { + return scheduledEvent; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventUserEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventUserEvent.java new file mode 100644 index 0000000000..6c700ff266 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/GenericScheduledEventUserEvent.java @@ -0,0 +1,123 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.CacheRestAction; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Indicates that a {@link User User} has subscribed or unsubscribed to a {@link ScheduledEvent ScheduledEvent}. + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Can be used to detect when someone has indicated that they have subscribed or unsubscribed to an event and also retrieve their + * {@link User User} object as well as the {@link ScheduledEvent}. + */ +public abstract class GenericScheduledEventUserEvent extends GenericScheduledEventGatewayEvent +{ + private final long userId; + + public GenericScheduledEventUserEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, long userId) + { + super(api, responseNumber, scheduledEvent); + this.userId = userId; + } + /** + * The id of the user that subscribed or unsubscribed to the {@link ScheduledEvent ScheduledEvent}. + * + * @return The long user id + */ + public long getUserIdLong() + { + return userId; + } + + /** + * The id of the user that subscribed or unsubscribed to the {@link ScheduledEvent ScheduledEvent}. + * + * @return The string user id + */ + @Nonnull + public String getUserId() + { + return Long.toUnsignedString(userId); + } + + /** + * The {@link User User} who subscribed or unsubscribed to the {@link ScheduledEvent ScheduledEvent}. + *
This might be missing if the user was not cached. + * Use {@link #retrieveUser()} to load the user. + * + * @return The added user or null if this information is missing + */ + @Nullable + public User getUser() + { + return api.getUserById(userId); + } + + /** + * The {@link Member Member} instance for the user + * or {@code null} if the user is not in this guild. + *
This will also be {@code null} if the member is not available in the cache. + * Use {@link #retrieveMember()} to load the member. + * + * @return Member of the added user or null if they are no longer member of this guild + */ + @Nullable + public Member getMember() + { + return guild.getMemberById(userId); + } + + /** + * Retrieves the {@link User} that subscribed or unsubscribed to the {@link ScheduledEvent ScheduledEvent}. + *
If a user is known, this will return {@link #getUser()}. + * + * @return {@link RestAction} - Type: {@link User} + */ + @Nonnull + @CheckReturnValue + public CacheRestAction retrieveUser() + { + return getJDA().retrieveUserById(getUserIdLong()); + } + + /** + * Retrieves the {@link Member} that subscribed or unsubscribed to the {@link ScheduledEvent ScheduledEvent}. + *
If a member is known, this will return {@link #getMember()}. + * + * @return {@link RestAction} - Type: {@link Member} + */ + @Nonnull + @CheckReturnValue + public CacheRestAction retrieveMember() + { + return getGuild().retrieveMemberById(getUserIdLong()); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventCreateEvent.java new file mode 100644 index 0000000000..ea7e7ae40b --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventCreateEvent.java @@ -0,0 +1,40 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link ScheduledEvent} object has been created. + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Can be used to detect when a {@link ScheduledEvent} was created and retrieve the created scheduled event. + */ +public class ScheduledEventCreateEvent extends GenericScheduledEventGatewayEvent +{ + public ScheduledEventCreateEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent) + { + super(api, responseNumber, scheduledEvent); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventDeleteEvent.java new file mode 100644 index 0000000000..c09ef0525a --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventDeleteEvent.java @@ -0,0 +1,40 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link ScheduledEvent} object has been deleted. + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Can be used to detect when a {@link ScheduledEvent} was deleted and retrieve the deleted scheduled event. + */ +public class ScheduledEventDeleteEvent extends GenericScheduledEventGatewayEvent +{ + public ScheduledEventDeleteEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent) + { + super(api, responseNumber, scheduledEvent); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserAddEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserAddEvent.java new file mode 100644 index 0000000000..441f6a4799 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserAddEvent.java @@ -0,0 +1,41 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link net.dv8tion.jda.api.entities.User User} has subscribed to a {@link ScheduledEvent ScheduledEvent}. + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Can be used to detect when someone has subscribed to an event and also retrieve their + * {@link net.dv8tion.jda.api.entities.User User} object as well as the {@link ScheduledEvent}. + */ +public class ScheduledEventUserAddEvent extends GenericScheduledEventUserEvent +{ + public ScheduledEventUserAddEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, long userId) + { + super(api, responseNumber, scheduledEvent, userId); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserRemoveEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserRemoveEvent.java new file mode 100644 index 0000000000..dff24e7ac9 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/ScheduledEventUserRemoveEvent.java @@ -0,0 +1,41 @@ +/* + * 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.events.guild.scheduledevent; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link net.dv8tion.jda.api.entities.User User} has unsubscribed from a {@link ScheduledEvent ScheduledEvent}. + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Can be used to detect when someone unsubscribed to an event and also retrieve their + * {@link net.dv8tion.jda.api.entities.User User} object as well as the {@link ScheduledEvent}. + */ +public class ScheduledEventUserRemoveEvent extends GenericScheduledEventUserEvent +{ + public ScheduledEventUserRemoveEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, long userId) + { + super(api, responseNumber, scheduledEvent, userId); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/package-info.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/package-info.java new file mode 100644 index 0000000000..05662ff7a5 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/** + * Gateway events for {@link net.dv8tion.jda.api.entities.ScheduledEvent ScheduledEvents}. + *

It should be noted that {@link net.dv8tion.jda.api.entities.ScheduledEvent ScheduledEvents} are not + * actual gateway events found in the {@link net.dv8tion.jda.api.events} package, but are rather entities similar to + * {@link net.dv8tion.jda.api.entities.User User} or {@link net.dv8tion.jda.api.entities.channel.concrete.TextChannel TextChannel} objects + * representing a scheduled event. + */ +package net.dv8tion.jda.api.events.guild.scheduledevent; diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/GenericScheduledEventUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/GenericScheduledEventUpdateEvent.java new file mode 100644 index 0000000000..f19da0cffc --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/GenericScheduledEventUpdateEvent.java @@ -0,0 +1,80 @@ +package net.dv8tion.jda.api.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.events.UpdateEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.GenericScheduledEventGatewayEvent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A generic gateway event class representing an update of a {@link ScheduledEvent ScheduledEvent} entity. + *
All events in {@link net.dv8tion.jda.api.events.guild.scheduledevent.update} package extend this event and are fired + * when a specified field in a {@link ScheduledEvent ScheduledEvent} is updated. + * + *

It should be noted that {@link ScheduledEvent ScheduledEvents} are not + * actual gateway events found in the {@link net.dv8tion.jda.api.events} package, but are rather entities similar to + * {@link net.dv8tion.jda.api.entities.User User} or {@link net.dv8tion.jda.api.entities.channel.concrete.TextChannel TextChannel} objects + * representing a scheduled event. + * + *

Requirements
+ * + *

These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public abstract class GenericScheduledEventUpdateEvent extends GenericScheduledEventGatewayEvent implements UpdateEvent +{ + protected final T previous; + protected final T next; + protected final String identifier; + + public GenericScheduledEventUpdateEvent( + @Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, + @Nullable T previous, @Nullable T next, @Nonnull String identifier) + { + super(api, responseNumber, scheduledEvent); + this.previous = previous; + this.next = next; + this.identifier = identifier; + } + + @Nonnull + @Override + public ScheduledEvent getEntity() + { + return getScheduledEvent(); + } + + @Nonnull + @Override + public String getPropertyIdentifier() + { + return identifier; + } + + @Nullable + @Override + public T getOldValue() + { + return previous; + } + + @Nullable + @Override + public T getNewValue() + { + return next; + } + + @Override + public String toString() + { + return "ScheduledEventUpdate[" + getPropertyIdentifier() + "](" + getOldValue() + "->" + getNewValue() + ')'; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateDescriptionEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateDescriptionEvent.java new file mode 100644 index 0000000000..8ff1ebd815 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateDescriptionEvent.java @@ -0,0 +1,71 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Indicates the {@link ScheduledEvent#getDescription() description} of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} description has changed. + * + *

Identifier: {@code description} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateDescriptionEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "description"; + + public ScheduledEventUpdateDescriptionEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nullable String previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getDescription(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getDescription() description}. + * + * @return The old description, or {@code null} if no description was previously set. + */ + @Nullable + public String getOldDescription() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getDescription() description}. + * + * @return The new description, or {@code null} if the description was removed. + */ + @Nullable + public String getNewDescription() + { + return getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateEndTimeEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateEndTimeEvent.java new file mode 100644 index 0000000000..aad3e1c517 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateEndTimeEvent.java @@ -0,0 +1,72 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; + +/** + * Indicates the {@link ScheduledEvent#getEndTime() end time} of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} end time has changed. + * + *

Identifier: {@code end_time} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateEndTimeEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "end_time"; + + public ScheduledEventUpdateEndTimeEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nullable OffsetDateTime previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getEndTime(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getEndTime() end time}. + * + * @return The old end time, or {@code null} if no end time was previously set. + */ + @Nullable + public OffsetDateTime getOldEndTime() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getEndTime() end time}. + * + * @return The new start time, or {@code null} if the end time has been removed. + */ + @Nullable + public OffsetDateTime getNewEndTime() + { + return getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateImageEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateImageEvent.java new file mode 100644 index 0000000000..ae039dc7ba --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateImageEvent.java @@ -0,0 +1,84 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates the {@link ScheduledEvent#getImageUrl() image} of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} image had changed. + * + *

Identifier: {@code image} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateImageEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "image"; + + public ScheduledEventUpdateImageEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nonnull String previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getImageUrl(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getImageUrl() image}. + * + * @return The old image + */ + @Nonnull + public String getOldImageUrl() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getImageUrl() image}. + * + * @return The new image + */ + @Nonnull + public String getNewImageUrl() + { + return getNewValue(); + } + + @Nonnull + @Override + public String getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public String getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateLocationEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateLocationEvent.java new file mode 100644 index 0000000000..4d19119486 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateLocationEvent.java @@ -0,0 +1,84 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that the location of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} location has changed. + * + *

Identifier: {@code location} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateLocationEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "location"; + + public ScheduledEventUpdateLocationEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nonnull String previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getLocation(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getLocation() location}. + * + * @return The old location + */ + @Nonnull + public String getOldLocation() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getLocation() location}. + * + * @return The new location + */ + @Nonnull + public String getNewLocation() + { + return getNewValue(); + } + + @Nonnull + @Override + public String getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public String getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateNameEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateNameEvent.java new file mode 100644 index 0000000000..1e05f9f47d --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateNameEvent.java @@ -0,0 +1,84 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that the {@link ScheduledEvent#getName() name} of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} name has changed. + * + *

Identifier: {@code name} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateNameEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "name"; + + public ScheduledEventUpdateNameEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nonnull String previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getName(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getName() name}. + * + * @return The old name + */ + @Nonnull + public String getOldName() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getName() name}. + * + * @return The new name + */ + @Nonnull + public String getNewName() + { + return getNewValue(); + } + + @Nonnull + @Override + public String getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public String getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStartTimeEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStartTimeEvent.java new file mode 100644 index 0000000000..374137a2c7 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStartTimeEvent.java @@ -0,0 +1,85 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; +import java.time.OffsetDateTime; + +/** + * Indicates the {@link ScheduledEvent#getStartTime() start time} of a {@link ScheduledEvent} has changed. + * + *

Can be used to detect when the {@link ScheduledEvent} start time has changed. + * + *

Identifier: {@code start_time} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateStartTimeEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "start_time"; + + public ScheduledEventUpdateStartTimeEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nonnull OffsetDateTime previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getStartTime(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getStartTime() start time}. + * + * @return The old start time + */ + @Nonnull + public OffsetDateTime getOldStartTime() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getStartTime() start time}. + * + * @return The new start time + */ + @Nonnull + public OffsetDateTime getNewStartTime() + { + return getNewValue(); + } + + @Nonnull + @Override + public OffsetDateTime getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public OffsetDateTime getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStatusEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStatusEvent.java new file mode 100644 index 0000000000..f921c78334 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/guild/scheduledevent/update/ScheduledEventUpdateStatusEvent.java @@ -0,0 +1,85 @@ +/* + * 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.events.guild.scheduledevent.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.ScheduledEvent; + +import javax.annotation.Nonnull; + +/** + * Indicates that the {@link ScheduledEvent#getStatus() status} of a {@link ScheduledEvent} has changed. The status + * of a {@link ScheduledEvent} represents if it is currently active, canceled, or completed. + * + *

Can be used to detect when the {@link ScheduledEvent} status has changed. + * + *

Identifier: {@code status} + * + *

Requirements
+ * + *

This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + *
{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and + * {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default! + * + * Discord does not specifically tell us about the updates, but merely tells us the + * {@link ScheduledEvent ScheduledEvent} was updated and gives us the updated {@link ScheduledEvent ScheduledEvent} object. + * In order to fire a specific event like this we need to have the old {@link ScheduledEvent ScheduledEvent} cached to compare against. + */ +public class ScheduledEventUpdateStatusEvent extends GenericScheduledEventUpdateEvent +{ + public static final String IDENTIFIER = "status"; + + public ScheduledEventUpdateStatusEvent(@Nonnull JDA api, long responseNumber, @Nonnull ScheduledEvent scheduledEvent, @Nonnull ScheduledEvent.Status previous) + { + super(api, responseNumber, scheduledEvent, previous, scheduledEvent.getStatus(), IDENTIFIER); + } + + /** + * The old {@link ScheduledEvent#getStatus() status}. + * + * @return The old status + */ + @Nonnull + public ScheduledEvent.Status getOldStatus() + { + return getOldValue(); + } + + /** + * The new {@link ScheduledEvent#getStatus() status}. + * + * @return The new status + */ + @Nonnull + public ScheduledEvent.Status getNewStatus() + { + return getNewValue(); + } + + @Nonnull + @Override + public ScheduledEvent.Status getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public ScheduledEvent.Status getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index d9dd5f5ea6..5290a68b23 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -43,6 +43,11 @@ import net.dv8tion.jda.api.events.guild.override.PermissionOverrideCreateEvent; import net.dv8tion.jda.api.events.guild.override.PermissionOverrideDeleteEvent; import net.dv8tion.jda.api.events.guild.override.PermissionOverrideUpdateEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventCreateEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventDeleteEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventUserAddEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventUserRemoveEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.update.*; import net.dv8tion.jda.api.events.guild.update.*; import net.dv8tion.jda.api.events.guild.voice.*; import net.dv8tion.jda.api.events.http.HttpRequestEvent; @@ -265,6 +270,19 @@ public void onGuildUpdateMaxMembers(@Nonnull GuildUpdateMaxMembersEvent event) { public void onGuildUpdateMaxPresences(@Nonnull GuildUpdateMaxPresencesEvent event) {} public void onGuildUpdateNSFWLevel(@Nonnull GuildUpdateNSFWLevelEvent event) {} + //Scheduled Event Events + public void onScheduledEventUpdateDescription(@Nonnull ScheduledEventUpdateDescriptionEvent event) {} + public void onScheduledEventUpdateEndTime(@Nonnull ScheduledEventUpdateEndTimeEvent event) {} + public void onScheduledEventUpdateLocation(@Nonnull ScheduledEventUpdateLocationEvent event) {} + public void onScheduledEventUpdateName(@Nonnull ScheduledEventUpdateNameEvent event) {} + public void onScheduledEventUpdateStartTime(@Nonnull ScheduledEventUpdateStartTimeEvent event) {} + public void onScheduledEventUpdateStatus(@Nonnull ScheduledEventUpdateStatusEvent event) {} + + public void onScheduledEventCreate(@Nonnull ScheduledEventCreateEvent event) {} + public void onScheduledEventDelete(@Nonnull ScheduledEventDeleteEvent event) {} + public void onScheduledEventUserAdd(@Nonnull ScheduledEventUserAddEvent event) {} + public void onScheduledEventUserRemove(@Nonnull ScheduledEventUserRemoveEvent event) {} + //Guild Invite Events public void onGuildInviteCreate(@Nonnull GuildInviteCreateEvent event) {} public void onGuildInviteDelete(@Nonnull GuildInviteDeleteEvent event) {} @@ -365,6 +383,7 @@ public void onGenericEmojiUpdate(@Nonnull GenericEmojiUpdateEvent event) {} public void onGenericGuildSticker(@Nonnull GenericGuildStickerEvent event) {} public void onGenericGuildStickerUpdate(@Nonnull GenericGuildStickerUpdateEvent event) {} public void onGenericPermissionOverride(@Nonnull GenericPermissionOverrideEvent event) {} + public void onGenericScheduledEventUpdate(@Nonnull GenericScheduledEventUpdateEvent event) {} public void onGenericForumTag(@Nonnull GenericForumTagEvent event) {} public void onGenericForumTagUpdate(@Nonnull GenericForumTagUpdateEvent event) {} diff --git a/src/main/java/net/dv8tion/jda/api/managers/ScheduledEventManager.java b/src/main/java/net/dv8tion/jda/api/managers/ScheduledEventManager.java new file mode 100644 index 0000000000..a014b64d37 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/ScheduledEventManager.java @@ -0,0 +1,302 @@ +/* + * 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.managers; + +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.temporal.TemporalAccessor; + + +/** + * The Manager is providing functionality to update one or more fields of a {@link ScheduledEvent}. + *
The manager may also be used to start, cancel or end events. + * + *

Example + *

{@code
+ * manager.setLocation("at the beach")
+ *     .setStartTime(OffsetDateTime.now().plusHours(1))
+ *     .setEndTime(OffsetDateTime.now().plusHours(3))
+ *     .setName("Discussing Turtle Shells")
+ *     .queue();
+ * }
+ * + * @see ScheduledEvent#getManager() + */ +public interface ScheduledEventManager extends Manager +{ + /** Used to reset the name field */ + long NAME = 1; + /** Used to reset the description field */ + long DESCRIPTION = 1 << 1; + /** Used to reset the location field */ + long LOCATION = 1 << 2; + /** Used to reset the start time field */ + long START_TIME = 1 << 3; + /** Used to reset the end time field */ + long END_TIME = 1 << 4; + /** Used to reset the image field */ + long IMAGE = 1 << 5; + /** Used to reset the status field */ + long STATUS = 1 << 6; + + /** + * Resets the fields specified by the provided bit-flag pattern. + * You can specify a combination by using a bitwise OR concat of the flag constants. + *
Example: {@code manager.reset(ScheduledEventManager.DESCRIPTION | ScheduledEventManager.END_TIME);} + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
  • {@link #DESCRIPTION}
  • + *
  • {@link #LOCATION}
  • + *
  • {@link #START_TIME}
  • + *
  • {@link #END_TIME}
  • + *
  • {@link #IMAGE}
  • + *
  • {@link #STATUS}
  • + *
+ * + * @param fields + * Integer value containing the flags to reset. + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @Override + ScheduledEventManager reset(long fields); + + /** + * Resets the fields specified by the provided bit-flag patterns. + * You can specify a combination by using a bitwise OR concat of the flag constants. + *
Example: {@code manager.reset(ScheduledEventManager.DESCRIPTION, ScheduledEventManager.END_TIME);} + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
  • {@link #DESCRIPTION}
  • + *
  • {@link #LOCATION}
  • + *
  • {@link #START_TIME}
  • + *
  • {@link #END_TIME}
  • + *
  • {@link #IMAGE}
  • + *
  • {@link #STATUS}
  • + *
+ * + * @param fields + * Integer values containing the flags to reset. + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @Override + ScheduledEventManager reset(long... fields); + + /** + * The target {@link ScheduledEvent} for this manager + */ + @Nonnull + ScheduledEvent getScheduledEvent(); + + /** + * The {@link net.dv8tion.jda.api.entities.Guild Guild} this + * {@link ScheduledEvent ScheduledEvent} is in. + *
This is logically the same as calling {@code getScheduledEvent().getGuild()} + * + * @return The parent {@link net.dv8tion.jda.api.entities.Guild Guild} + */ + @Nonnull + default Guild getGuild() + { + return getScheduledEvent().getGuild(); + } + + /** + * Sets the name of the selected {@link ScheduledEvent ScheduledEvent} + * + * @param name + * The new name for the selected {@link ScheduledEvent ScheduledEvent} + * + * @throws java.lang.IllegalArgumentException + * If the new name is blank, empty, {@code null}, or longer than {@value ScheduledEvent#MAX_NAME_LENGTH} + * characters + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setName(@Nonnull String name); + + /** + * Sets the description of the selected {@link ScheduledEvent ScheduledEvent}. + * This field may include markdown. + * + * @param description + * The new description for the selected {@link ScheduledEvent ScheduledEvent}, + * or {@code null} to reset the description + * + * @throws java.lang.IllegalArgumentException + * If the new description is longer than {@value ScheduledEvent#MAX_DESCRIPTION_LENGTH} characters + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setDescription(@Nullable String description); + + /** + * Sets the cover image for the new {@link ScheduledEvent ScheduledEvent}. + * + * @param icon + * The cover image for the new {@link ScheduledEvent ScheduledEvent}, + * or {@code null} for no cover image. + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setImage(@Nullable Icon icon); + + /** + * Sets the location of the selected {@link ScheduledEvent} to take place in the specified {@link GuildChannel}. + *

This will change the event's type to {@link ScheduledEvent.Type#STAGE_INSTANCE} or {@link ScheduledEvent.Type#VOICE}, + * which are the only supported channel types for the location of scheduled events currently. + * + * @param channel + * The {@link GuildChannel} that the selected {@link ScheduledEvent} is set to take place in. + * + * @throws java.lang.IllegalArgumentException + *

    + *
  • If the provided {@link GuildChannel} is {@code null}
  • + *
  • If the provided {@link GuildChannel} is not from the same guild
  • + *
  • If the provided {@link GuildChannel} is not a {@link net.dv8tion.jda.api.entities.channel.concrete.StageChannel} or {@link net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel}
  • + *
+ * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the currently logged in account does not have + * {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS}, + * {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL Permission.MANAGE_CHANNEL}, + * {@link net.dv8tion.jda.api.Permission#VOICE_MUTE_OTHERS Permission.VOICE_MUTE_OTHERS}, + * or {@link net.dv8tion.jda.api.Permission#VOICE_MOVE_OTHERS Permission.VOICE_MOVE_OTHERS}, in the provided + * channel. + * + * @return ScheduledEventManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setLocation(@Nonnull GuildChannel channel); + + /** + * Sets the location of the selected {@link ScheduledEvent} to take place externally, + * or not in a specific {@link GuildChannel}. Please note that an event is required to have an end time set if + * the location is external. + *

This will change the event's type to {@link ScheduledEvent.Type#EXTERNAL} + * + * @param location + * The location that the selected {@link ScheduledEvent} is set to take place at. + * + * @throws java.lang.IllegalArgumentException + * If the provided location is blank, empty, {@code null}, or longer than + * {@value ScheduledEvent#MAX_LOCATION_LENGTH} + * @throws java.lang.IllegalStateException + * If the selected {@link ScheduledEvent} does not have an end time + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the currently logged in account does not have + * {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS} + * + * @return ScheduledEventManager for chaining convenience + * + * @see #setEndTime(TemporalAccessor) + * @see #setLocation(GuildChannel) + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setLocation(@Nonnull String location); + + /** + * Sets the time that the selected {@link ScheduledEvent} should start at. + * Events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL} will automatically + * start at this time. Events of {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + * and {@link ScheduledEvent.Type#VOICE Type.VOICE} need to be manually started. + * If the {@link ScheduledEvent} has not begun after its scheduled start time, it will be automatically cancelled after a few hours. + * + * @param startTime + * The time that the selected {@link ScheduledEvent} is set to start at. + * + * @throws java.lang.IllegalArgumentException + *

    + *
  • If the provided start time is {@code null}
  • + *
  • If the provided start time is before the end time
  • + *
  • If the provided start time is before the current time
  • + *
+ * @return ScheduledEventManager for chaining convenience + * + * @see #setEndTime(TemporalAccessor) + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setStartTime(@Nonnull TemporalAccessor startTime); + + /** + * Sets the time that the selected {@link ScheduledEvent} should end at. + * Events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL} will automatically + * end at this time, and events of {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + * and {@link ScheduledEvent.Type#VOICE Type.VOICE} will end a few minutes after the last + * person has left the channel. + * + * @param endTime + * The time that the selected {@link ScheduledEvent} is set to end at. + * + * @throws java.lang.IllegalArgumentException + *
    + *
  • If the provided end time is before the start time
  • + *
  • If the provided end time is {@code null}
  • + *
+ * + * @return ScheduledEventManager for chaining convenience + * + * @see #setStartTime(TemporalAccessor) + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setEndTime(@Nonnull TemporalAccessor endTime); + + /** + * Sets the status of the event. This method may be used to start, end or cancel an event but can only be used to + * complete one of the following transitions: + *
    + *
  1. {@link ScheduledEvent.Status#SCHEDULED Status.SCHEDULED} to {@link ScheduledEvent.Status#ACTIVE Status.ACTIVE}
  2. + *
  3. {@link ScheduledEvent.Status#SCHEDULED Status.SCHEDULED} to {@link ScheduledEvent.Status#CANCELED Status.CANCELED}
  4. + *
  5. {@link ScheduledEvent.Status#ACTIVE Status.ACTIVE} to {@link ScheduledEvent.Status#COMPLETED Status.COMPLETED}
  6. + *
+ * + * @param status + * The new status + * + * @throws java.lang.IllegalStateException + * If the transition between statuses does not follow one of the three documented above. + * @throws IllegalArgumentException + * If the provided status is {@code null} + * + * @return ScheduledEventManager for chaining convenience + * + * @see #getScheduledEvent() + * @see ScheduledEvent#getStatus() + */ + @Nonnull + @CheckReturnValue + ScheduledEventManager setStatus(@Nonnull ScheduledEvent.Status status); +} diff --git a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java index 903d9fb1d3..8bba7163fd 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java +++ b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java @@ -75,8 +75,8 @@ public enum ErrorResponse UNKNOWN_STAGE_INSTANCE( 10067, "Unknown Stage Instance"), UNKNOWN_GUILD_MEMBER_VERIFICATION_FORM( 10068, "Unknown Guild Member Verification Form"), UNKNOWN_GUILD_WELCOME_SCREEN( 10069, "Unknown Guild Welcome Screen"), - UNKNOWN_GUILD_SCHEDULED_EVENT( 10070, "Unknown Guild Scheduled Event"), - UNKNOWN_GUILD_SCHEDULED_EVENT_USER( 10071, "Unknown Guild Scheduled Event User"), + SCHEDULED_EVENT( 10070, "Unknown Scheduled Event"), + SCHEDULED_EVENT_USER( 10071, "Unknown Scheduled Event User"), BOTS_NOT_ALLOWED( 20001, "Bots cannot use this endpoint"), ONLY_BOTS_ALLOWED( 20002, "Only bots can use this endpoint"), EXPLICIT_CONTENT_CANNOT_SEND_TO_RECIPIENT(20009, "Explicit content cannot be sent to the desired recipient(s)"), diff --git a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java index 4e9a98fcb7..7d2084a3cc 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java +++ b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java @@ -25,6 +25,7 @@ import net.dv8tion.jda.api.events.guild.invite.GenericGuildInviteEvent; import net.dv8tion.jda.api.events.guild.member.GenericGuildMemberEvent; import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.update.GenericScheduledEventUpdateEvent; import net.dv8tion.jda.api.events.guild.voice.GenericGuildVoiceEvent; import net.dv8tion.jda.api.events.message.GenericMessageEvent; import net.dv8tion.jda.api.events.message.MessageBulkDeleteEvent; @@ -141,6 +142,7 @@ public enum GatewayIntent * Typing events in private channels. */ DIRECT_MESSAGE_TYPING(14), + /** * PRIVILEGED INTENT Access to message content. * @@ -158,6 +160,12 @@ public enum GatewayIntent * @see Message Content Privileged Intent FAQ */ MESSAGE_CONTENT(15), + + /** + * Scheduled Events events. + */ + SCHEDULED_EVENTS(16), + ; /** @@ -373,6 +381,8 @@ else if (GuildBanEvent.class.isAssignableFrom(event) || GuildUnbanEvent.class.is intents.add(GUILD_BANS); else if (GenericEmojiEvent.class.isAssignableFrom(event) || GenericGuildStickerEvent.class.isAssignableFrom(event)) intents.add(GUILD_EMOJIS_AND_STICKERS); + else if (GenericScheduledEventUpdateEvent.class.isAssignableFrom(event)) + intents.add(SCHEDULED_EVENTS); else if (GenericGuildInviteEvent.class.isAssignableFrom(event)) intents.add(GUILD_INVITES); else if (GenericGuildVoiceEvent.class.isAssignableFrom(event)) diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/ScheduledEventAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/ScheduledEventAction.java new file mode 100644 index 0000000000..746f0bdd2f --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/ScheduledEventAction.java @@ -0,0 +1,160 @@ +/* + * 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.requests.restaction; + +import net.dv8tion.jda.api.entities.*; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.time.temporal.TemporalAccessor; + +/** + * Extension of {@link net.dv8tion.jda.api.requests.RestAction RestAction} specifically + * designed to create a {@link ScheduledEvent ScheduledEvent}. + * This extension allows setting properties such as the name or description of an event before it is + * created. + * + *

Requirements
+ * Events that are created are required to have a name, a location, and a start time. Depending on the + * type of location provided, an event will be of one of three different {@link ScheduledEvent.Type Types}: + *

    + *
  1. + * {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + *
    These events are set to take place inside of a {@link net.dv8tion.jda.api.entities.channel.concrete.StageChannel StageChannel}. The + * following permissions are required in the specified stage channel in order to create an event there: + *
      + *
    • {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS}
    • + *
    • {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL Permission.MANAGE_CHANNEL}
    • + *
    • {@link net.dv8tion.jda.api.Permission#VOICE_MUTE_OTHERS Permission.VOICE_MUTE_OTHERS}
    • + *
    • {@link net.dv8tion.jda.api.Permission#VOICE_MOVE_OTHERS Permission.VOICE_MOVE_OTHERS}
    • + *
    + *
  2. + *
  3. + * {@link ScheduledEvent.Type#VOICE Type.VOICE} + *
    These events are set to take place inside of a {@link net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel}. The + * following permissions are required in the specified voice channel in order to create an event there: + *
      + *
    • {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS}
    • + *
    • {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
    • + *
    • {@link net.dv8tion.jda.api.Permission#VOICE_CONNECT Permission.VOICE_CONNECT}
    • + *
    + *
  4. + *
  5. + * {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL} + *
    These events are set to take place at an external location. {@link net.dv8tion.jda.api.Permission#MANAGE_EVENTS Permission.MANAGE_EVENTS} + * is required on the guild level in order to create this type of event. Additionally, an end time must + * also be specified. + *
  6. + *
+ * + * @see net.dv8tion.jda.api.entities.Guild + * @see Guild#createScheduledEvent(String, String, OffsetDateTime, OffsetDateTime) + * @see Guild#createScheduledEvent(String, net.dv8tion.jda.api.entities.channel.middleman.GuildChannel, OffsetDateTime) + */ +public interface ScheduledEventAction extends FluentAuditableRestAction +{ + /** + * The guild to create the {@link ScheduledEvent} in + * + * @return The guild + */ + @Nonnull + Guild getGuild(); + + /** + * Sets the name for the new {@link ScheduledEvent ScheduledEvent}. + * + * @param name + * The name for the new {@link ScheduledEvent ScheduledEvent} + * + * @throws java.lang.IllegalArgumentException + * If the new name is blank, empty, {@code null}, or contains more than {@value ScheduledEvent#MAX_NAME_LENGTH} + * characters + * + * @return The current ScheduledEventAction, for chaining convenience + */ + @Nonnull + ScheduledEventAction setName(@Nonnull String name); + + /** + * Sets the description for the new {@link ScheduledEvent ScheduledEvent}. + * This field may include markdown. + * + * @param description + * The description for the new {@link ScheduledEvent ScheduledEvent}, + * or {@code null} for no description + * + * @throws java.lang.IllegalArgumentException + * If the new description is longer than {@value ScheduledEvent#MAX_DESCRIPTION_LENGTH} characters + * + * @return The current ScheduledEventAction, for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventAction setDescription(@Nullable String description); + + /** + *

Sets the time that the new {@link ScheduledEvent} will start at. + * Events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL} will automatically + * start at this time, but events of {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + * and {@link ScheduledEvent.Type#VOICE Type.VOICE} will need to be manually started, + * and will automatically be cancelled a few hours after the start time if not. + * + * @param startTime + * The time that the new {@link ScheduledEvent} should start at + * + * @throws java.lang.IllegalArgumentException + * If the provided start time is {@code null}, or takes place after the end time + * + * @return The current ScheduledEventAction, for chaining convenience + */ + @Nonnull + ScheduledEventAction setStartTime(@Nonnull TemporalAccessor startTime); + + /** + * Sets the time that the new {@link ScheduledEvent} will end at. + * Events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL} will automatically + * end at this time, and events of {@link ScheduledEvent.Type#STAGE_INSTANCE Type.STAGE_INSTANCE} + * and {@link ScheduledEvent.Type#VOICE Type.VOICE} will end a few minutes after the last + * user has left the channel. + *

Note: Setting an end time is only possible for events of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL}. + * + * @param endTime + * The time that the new {@link ScheduledEvent} is set to end at + * + * @throws java.lang.IllegalArgumentException + * If the provided end time is chronologically set before the start time + * + * @return The current ScheduledEventAction, for chaining convenience + */ + @Nonnull + ScheduledEventAction setEndTime(@Nullable TemporalAccessor endTime); + + /** + * Sets the cover image for the new {@link ScheduledEvent ScheduledEvent}. + * + * @param icon + * The cover image for the new {@link ScheduledEvent ScheduledEvent}, + * or {@code null} for no cover image + * + * @return The current ScheduledEventAction, for chaining convenience + */ + @Nonnull + @CheckReturnValue + ScheduledEventAction setImage(@Nullable Icon icon); +} diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/ScheduledEventMembersPaginationAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/ScheduledEventMembersPaginationAction.java new file mode 100644 index 0000000000..75181afbf6 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/ScheduledEventMembersPaginationAction.java @@ -0,0 +1,55 @@ +/* + * 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.requests.restaction.pagination; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; + +import javax.annotation.Nonnull; + +/** + * {@link PaginationAction PaginationAction} that paginates the scheduled event users endpoint. + *
Note that this implementation is not considered thread-safe as modifications to the cache are not done + * with a lock. Calling methods on this class from multiple threads is not recommended. + * + *

Limits:
+ * Minimum - 1 + *
Maximum - 100 + * + *

Example
+ *

{@code
+ * // Get every member interested in this event and add the members names to a list
+ * public static void getInterestedMembers(ScheduledEvent event) {
+ *      // get paginator
+ *      ScheduledEventMembersPaginationAction members = event.retrieveInterestedMembers();
+ *      // add the name of every interested member to a list
+ *      ArrayList memberNames = new ArrayList();
+ *      members.forEachAsync(member -> memberNames.add(member.getEffectiveName()));
+ * }
+ * }
+ */ +public interface ScheduledEventMembersPaginationAction extends PaginationAction +{ + /** + * The current target {@link Guild Guild} for + * this ScheduledEventMembersPaginationAction. + * + * @return The never-null target Guild + */ + @Nonnull + Guild getGuild(); +} diff --git a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java index d5934739ec..daaa6f3c80 100644 --- a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java +++ b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java @@ -84,7 +84,13 @@ public enum CacheFlag * * @since 4.3.0 */ - ONLINE_STATUS(GatewayIntent.GUILD_PRESENCES) + ONLINE_STATUS(GatewayIntent.GUILD_PRESENCES), + /** + * Enables cache for {@link Guild#getScheduledEventCache()} + * + *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#SCHEDULED_EVENTS SCHEDULED_EVENTS} intent to be enabled. + */ + SCHEDULED_EVENTS(GatewayIntent.SCHEDULED_EVENTS), ; private static final EnumSet privileged = EnumSet.of(ACTIVITY, CLIENT_STATUS, ONLINE_STATUS); diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java b/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java index 36173191f4..a019370b55 100644 --- a/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java +++ b/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java @@ -35,6 +35,8 @@ import javax.annotation.Nullable; import java.io.*; import java.nio.ByteBuffer; +import java.time.OffsetDateTime; +import java.time.format.DateTimeParseException; import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; @@ -522,6 +524,56 @@ public long getUnsignedLong(int index) return value; } + /** + * Resolves the value at the specified index to an {@link OffsetDateTime}. + *
Note: This method should be used on ISO8601 timestamps + * + * @param index + * The index to resolve + * + * @throws net.dv8tion.jda.api.exceptions.ParsingException + * If the value is missing, null, or not a valid ISO8601 timestamp + * + * @return Possibly-null {@link OffsetDateTime} object representing the timestamp + */ + @Nonnull + public OffsetDateTime getOffsetDateTime(int index) + { + OffsetDateTime value = getOffsetDateTime(index, null); + if(value == null) + throw valueError(index, "OffsetDateTime"); + return value; + } + /** + * Resolves the value at the specified index to an {@link OffsetDateTime}. + *
Note: This method should only be used on ISO8601 timestamps + * + * @param index + * The index to resolve + * @param defaultValue + * Alternative value to use when no value or null value is associated with the key + * + * @throws net.dv8tion.jda.api.exceptions.ParsingException + * If the value is not a valid ISO8601 timestamp + * + * @return Possibly-null {@link OffsetDateTime} object representing the timestamp + */ + @Contract("_, !null -> !null") + public OffsetDateTime getOffsetDateTime(int index, @Nullable OffsetDateTime defaultValue) + { + OffsetDateTime value; + try + { + value = get(OffsetDateTime.class, index, OffsetDateTime::parse, null); + } + catch (DateTimeParseException e) + { + String reason = "Cannot parse value for %s into an OffsetDateTime object. Try double checking that %s is a valid ISO8601 timestamp"; + throw new ParsingException(String.format(reason, e.getParsedString())); + } + return value == null ? defaultValue : value; + } + /** * Resolves the value at the specified index to an unsigned long. * diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java b/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java index c70a0d8c33..5e5b802aa9 100644 --- a/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java +++ b/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java @@ -36,6 +36,8 @@ import javax.annotation.Nullable; import java.io.*; import java.nio.ByteBuffer; +import java.time.OffsetDateTime; +import java.time.format.DateTimeParseException; import java.util.*; import java.util.function.Function; import java.util.function.UnaryOperator; @@ -646,6 +648,56 @@ public double getDouble(@Nonnull String key, double defaultValue) return value == null ? defaultValue : value; } + /** + * Resolves an {@link OffsetDateTime} to a key. + *
Note: This method should be used on ISO8601 timestamps + * + * @param key + * The key to check for a value + * + * @throws net.dv8tion.jda.api.exceptions.ParsingException + * If the value is missing, null, or not a valid ISO8601 timestamp + * + * @return Possibly-null {@link OffsetDateTime} object representing the timestamp + */ + @Nonnull + public OffsetDateTime getOffsetDateTime(@Nonnull String key) + { + OffsetDateTime value = getOffsetDateTime(key, null); + if(value == null) + throw valueError(key, "OffsetDateTime"); + return value; + } + /** + * Resolves an {@link OffsetDateTime} to a key. + *
Note: This method should only be used on ISO8601 timestamps + * + * @param key + * The key to check for a value + * @param defaultValue + * Alternative value to use when no value or null value is associated with the key + * + * @throws net.dv8tion.jda.api.exceptions.ParsingException + * If the value is not a valid ISO8601 timestamp + * + * @return Possibly-null {@link OffsetDateTime} object representing the timestamp + */ + @Contract("_, !null -> !null") + public OffsetDateTime getOffsetDateTime(@Nonnull String key, @Nullable OffsetDateTime defaultValue) + { + OffsetDateTime value; + try + { + value = get(OffsetDateTime.class, key, OffsetDateTime::parse, null); + } + catch (DateTimeParseException e) + { + String reason = "Cannot parse value for %s into an OffsetDateTime object. Try double checking that %s is a valid ISO8601 timestmap"; + throw new ParsingException(String.format(reason, key, e.getParsedString())); + } + return value == null ? defaultValue : value; + } + /** * Removes the value associated with the specified key. * If no value is associated with the key, this does nothing. diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 0e19aa3a2e..b94c77cbf0 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -659,6 +659,13 @@ public RestAction> retrieveNitroStickerPacks() }); } + @Nonnull + @Override + public SnowflakeCacheView getScheduledEventCache() + { + return CacheView.allSnowflakes(() -> guildCache.stream().map(Guild::getScheduledEventCache)); + } + @Nonnull @Override public SnowflakeCacheView getCategoryCache() diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index a5098e4fa9..f5065c2325 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -197,6 +197,23 @@ private void createGuildEmojiPass(GuildImpl guildObj, DataArray array) } } + private void createScheduledEventPass(GuildImpl guildObj, DataArray array) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + return; + SnowflakeCacheViewImpl eventView = guildObj.getScheduledEventsView(); + for (int i = 0; i < array.length(); i++) + { + DataObject object = array.getObject(i); + if (object.isNull("id")) + { + LOG.error("Received GUILD_CREATE with a scheduled event with a null ID. JSON: {}", object); + continue; + } + createScheduledEvent(guildObj, object); + } + } + private void createGuildStickerPass(GuildImpl guildObj, DataArray array) { if (!getJDA().isCacheFlagSet(CacheFlag.STICKER)) @@ -240,6 +257,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< final DataArray roleArray = guildJson.getArray("roles"); final DataArray channelArray = guildJson.getArray("channels"); final DataArray threadArray = guildJson.getArray("threads"); + final DataArray scheduledEventsArray = guildJson.getArray("guild_scheduled_events"); final DataArray emojisArray = guildJson.getArray("emojis"); final DataArray stickersArray = guildJson.getArray("stickers"); final DataArray voiceStateArray = guildJson.getArray("voice_states"); @@ -362,6 +380,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< } } + createScheduledEventPass(guildObj, scheduledEventsArray); createGuildEmojiPass(guildObj, emojisArray); createGuildStickerPass(guildObj, stickersArray); guildJson.optArray("stage_instances") @@ -961,6 +980,56 @@ public RichCustomEmojiImpl createEmoji(GuildImpl guildObj, DataObject json) .setAvailable(json.getBoolean("available", true)); } + public ScheduledEvent createScheduledEvent(GuildImpl guild, DataObject json) + { + final long id = json.getLong("id"); + ScheduledEventImpl scheduledEvent = (ScheduledEventImpl) guild.getScheduledEventsView().get(id); + if (scheduledEvent == null) + { + SnowflakeCacheViewImpl scheduledEventView = guild.getScheduledEventsView(); + try (UnlockHook hook = scheduledEventView.writeLock()) + { + scheduledEvent = new ScheduledEventImpl(id, guild); + if (getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + { + scheduledEventView.getMap().put(id, scheduledEvent); + } + } + } + + scheduledEvent.setName(json.getString("name")) + .setDescription(json.getString("description", null)) + .setStatus(ScheduledEvent.Status.fromKey(json.getInt("status", -1))) + .setInterestedUserCount(json.getInt("user_count", -1)) + .setStartTime(json.getOffsetDateTime("scheduled_start_time")) + .setEndTime(json.getOffsetDateTime("scheduled_end_time", null)) + .setImage(json.getString("image", null)); + + final long creatorId = json.getLong("creator_id", 0); + scheduledEvent.setCreatorId(creatorId); + if (creatorId != 0) + { + if (json.hasKey("creator")) + scheduledEvent.setCreator(createUser(json.getObject("creator"))); + else + scheduledEvent.setCreator(getJDA().getUserById(creatorId)); + } + final ScheduledEvent.Type type = ScheduledEvent.Type.fromKey(json.getInt("entity_type")); + scheduledEvent.setType(type); + switch (type) + { + case STAGE_INSTANCE: + case VOICE: + scheduledEvent.setLocation(json.getString("channel_id")); + break; + case EXTERNAL: + String externalLocation = json.getObject("entity_metadata").getString("location"); + scheduledEvent.setLocation(externalLocation); + } + return scheduledEvent; + } + + public Category createCategory(DataObject json, long guildId) { return createCategory(null, json, guildId); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java index d983003935..5eaedba630 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -103,6 +103,7 @@ public class GuildImpl implements Guild private final JDAImpl api; private final SortedSnowflakeCacheViewImpl categoryCache = new SortedSnowflakeCacheViewImpl<>(Category.class, Channel::getName, Comparator.naturalOrder()); + private final SortedSnowflakeCacheViewImpl scheduledEventCache = new SortedSnowflakeCacheViewImpl<>(ScheduledEvent.class, ScheduledEvent::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl voiceChannelCache = new SortedSnowflakeCacheViewImpl<>(VoiceChannel.class, Channel::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl textChannelCache = new SortedSnowflakeCacheViewImpl<>(TextChannel.class, Channel::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl newsChannelCache = new SortedSnowflakeCacheViewImpl<>(NewsChannel.class, Channel::getName, Comparator.naturalOrder()); @@ -540,6 +541,44 @@ public TextChannel getRulesChannel() return rulesChannel; } + @Nonnull + @Override + public CacheRestAction retrieveScheduledEventById(@Nonnull String id) + { + Checks.isSnowflake(id); + return new DeferredRestAction<>(getJDA(), ScheduledEvent.class, + () -> getScheduledEventById(id), + () -> + { + Route.CompiledRoute route = Route.Guilds.GET_SCHEDULED_EVENT.compile(getId(), id); + return new RestActionImpl<>(getJDA(), route, (response, request) -> api.getEntityBuilder().createScheduledEvent(this, response.getObject())); + }); + } + + @Nonnull + @Override + public CacheRestAction retrieveScheduledEventById(long id) + { + return retrieveScheduledEventById(Long.toUnsignedString(id)); + } + + @Nonnull + @Override + public ScheduledEventAction createScheduledEvent(@Nonnull String name, @Nonnull String location, @Nonnull OffsetDateTime startTime, @Nonnull OffsetDateTime endTime) + { + checkPermission(Permission.MANAGE_EVENTS); + return new ScheduledEventActionImpl(name, location, startTime, endTime, this); + } + + @Nonnull + @Override + public ScheduledEventAction createScheduledEvent(@Nonnull String name, @Nonnull GuildChannel channel, @Nonnull OffsetDateTime startTime) + { + checkPermission(Permission.MANAGE_EVENTS); + return new ScheduledEventActionImpl(name, channel, startTime, this); + } + + @Override public TextChannel getCommunityUpdatesChannel() { @@ -626,6 +665,13 @@ public MemberCacheView getMemberCache() return memberCache; } + @Nonnull + @Override + public SortedSnowflakeCacheView getScheduledEventCache() + { + return scheduledEventCache; + } + @Nonnull @Override public SortedSnowflakeCacheView getCategoryCache() @@ -2119,6 +2165,11 @@ public GuildImpl setBoostProgressBarEnabled(boolean enabled) // -- Map getters -- + public SortedSnowflakeCacheViewImpl getScheduledEventsView() + { + return scheduledEventCache; + } + public SortedSnowflakeCacheViewImpl getCategoriesView() { return categoryCache; diff --git a/src/main/java/net/dv8tion/jda/internal/entities/ScheduledEventImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/ScheduledEventImpl.java new file mode 100644 index 0000000000..16c635d154 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/ScheduledEventImpl.java @@ -0,0 +1,279 @@ +/* + * 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.internal.entities; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; +import net.dv8tion.jda.api.managers.ScheduledEventManager; +import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; +import net.dv8tion.jda.api.requests.restaction.pagination.ScheduledEventMembersPaginationAction; +import net.dv8tion.jda.internal.managers.ScheduledEventManagerImpl; +import net.dv8tion.jda.internal.requests.Route; +import net.dv8tion.jda.internal.requests.restaction.AuditableRestActionImpl; +import net.dv8tion.jda.internal.requests.restaction.pagination.ScheduledEventMembersPaginationActionImpl; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; + +public class ScheduledEventImpl implements ScheduledEvent +{ + private final long id; + private final Guild guild; + + private String name, description; + private OffsetDateTime startTime, endTime; + private String image; + private Status status; + private Type type; + private User creator; + private long creatorId; + private int interestedUserCount; + private String location; + + public ScheduledEventImpl(long id, Guild guild) + { + this.id = id; + this.guild = guild; + } + + @Nonnull + @Override + public String getName() + { + return name; + } + + @Nullable + @Override + public String getDescription() + { + return description; + } + + @Nullable + @Override + public String getImageUrl() + { + return image == null ? null : String.format(IMAGE_URL, getId(), image, image.startsWith("a_") ? "gif" : "png"); + } + + @Nullable + @Override + public User getCreator() + { + return creator; + } + + @Override + public long getCreatorIdLong() + { + return creatorId; + } + + @Nonnull + @Override + public Status getStatus() + { + return status; + } + + @Nonnull + @Override + public Type getType() + { + return type; + } + + @Nonnull + @Override + public OffsetDateTime getStartTime() + { + return startTime; + } + + @Nullable + @Override + public OffsetDateTime getEndTime() + { + return endTime; + } + + @Nullable + @Override + public GuildChannelUnion getChannel() + { + if (type.isChannel()) + return (GuildChannelUnion) guild.getGuildChannelById(location); + return null; + } + + @Nonnull + @Override + public String getLocation() + { + return location; + } + + @Override + public int getInterestedUserCount() + { + return interestedUserCount; + } + + @Nonnull + @Override + public Guild getGuild() + { + return guild; + } + + @Override + public long getIdLong() + { + return id; + } + + @Nonnull + @Override + public ScheduledEventManager getManager() + { + return new ScheduledEventManagerImpl(this); + } + + @Nonnull + @Override + public AuditableRestAction delete() + { + Guild guild = getGuild(); + if (!guild.getSelfMember().hasPermission(Permission.MANAGE_EVENTS)) + throw new InsufficientPermissionException(guild, Permission.MANAGE_EVENTS); + + Route.CompiledRoute route = Route.Guilds.DELETE_SCHEDULED_EVENT.compile(guild.getId(), getId()); + return new AuditableRestActionImpl<>(getJDA(), route); + } + + @Nonnull + @Override + public ScheduledEventMembersPaginationAction retrieveInterestedMembers() + { + return new ScheduledEventMembersPaginationActionImpl(this); + } + + public ScheduledEventImpl setName(String name) + { + this.name = name; + return this; + } + + public ScheduledEventImpl setType(Type type) + { + this.type = type; + return this; + } + + public ScheduledEventImpl setLocation(String location) + { + this.location = location; + return this; + } + + public ScheduledEventImpl setDescription(String description) + { + this.description = description; + return this; + } + + public ScheduledEventImpl setImage(String image) + { + this.image = image; + return this; + } + + public ScheduledEventImpl setCreatorId(long creatorId) + { + this.creatorId = creatorId; + return this; + } + + public ScheduledEventImpl setCreator(User creator) + { + this.creator = creator; + return this; + } + + public ScheduledEventImpl setStatus(Status status) + { + this.status = status; + return this; + } + + public ScheduledEventImpl setStartTime(OffsetDateTime startTime) + { + this.startTime = startTime; + return this; + } + + public ScheduledEventImpl setEndTime(OffsetDateTime endTime) + { + this.endTime = endTime; + return this; + } + + public ScheduledEventImpl setInterestedUserCount(int interestedUserCount) + { + this.interestedUserCount = interestedUserCount; + return this; + } + + @Override + public int compareTo(@Nonnull ScheduledEvent scheduledEvent) + { + Checks.notNull(scheduledEvent, "Scheduled Event"); + Checks.check(this.getGuild().equals(scheduledEvent.getGuild()), "Cannot compare two Scheduled Events belonging to seperate guilds!"); + + int startTimeComparison = OffsetDateTime.timeLineOrder().compare(this.getStartTime(), scheduledEvent.getStartTime()); + if (startTimeComparison == 0) + return Long.compare(this.getIdLong(), scheduledEvent.getIdLong()); + else + return startTimeComparison; + } + + @Override + public boolean equals(Object o) + { + if (o == this) + return true; + if (!(o instanceof ScheduledEventImpl)) + return false; + return this.id == ((ScheduledEventImpl) o).id; + } + + @Override + public int hashCode() + { + return Long.hashCode(id); + } + + @Override + public String toString() + { + return "ScheduledEvent:" + getName() + '(' + id + ')'; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java index c0a36c6b5e..2ef2ccdc6f 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java @@ -16,6 +16,7 @@ package net.dv8tion.jda.internal.handle; +import net.dv8tion.jda.api.entities.ScheduledEvent; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent; @@ -91,19 +92,19 @@ protected Long handleInternally(DataObject content) return null; } - // This is done in the AudioWebSocket already -// //We use this instead of getAudioManager(Guild) so we don't create a new instance. Efficiency! -// AudioManagerImpl manager = (AudioManagerImpl) getJDA().getAudioManagersView().get(guild.getIdLong()); -// if (manager != null && manager.isConnected() -// && manager.getConnectedChannel().getIdLong() == channel.getIdLong()) -// { -// manager.closeAudioConnection(ConnectionStatus.DISCONNECTED_CHANNEL_DELETED); -// } + // This is done in the AudioWebSocket already + // We use this instead of getAudioManager(Guild) so we don't create a new instance. Efficiency! + // AudioManagerImpl manager = (AudioManagerImpl) getJDA().getAudioManagersView().get(guild.getIdLong()); + // if (manager != null && manager.isConnected() + // && manager.getConnectedChannel().getIdLong() == channel.getIdLong()) + // { + // manager.closeAudioConnection(ConnectionStatus.DISCONNECTED_CHANNEL_DELETED); + // } guild.getVoiceChannelsView().remove(channel.getIdLong()); getJDA().handleEvent( - new ChannelDeleteEvent( - getJDA(), responseNumber, - channel)); + new ChannelDeleteEvent( + getJDA(), responseNumber, + channel)); break; } case STAGE: @@ -117,9 +118,9 @@ protected Long handleInternally(DataObject content) guild.getStageChannelsView().remove(channel.getIdLong()); getJDA().handleEvent( - new ChannelDeleteEvent( - getJDA(), responseNumber, - channel)); + new ChannelDeleteEvent( + getJDA(), responseNumber, + channel)); break; } @@ -175,6 +176,17 @@ protected Long handleInternally(DataObject content) default: WebSocketClient.LOG.debug("CHANNEL_DELETE provided an unknown channel type. JSON: {}", content); } + + if (guild != null) + { + // Deleting any scheduled events associated to the deleted channel as they are deleted when the channel gets deleted. + // There is no delete event for the deletion of scheduled events in this case, so we do this to keep the cache in sync. + String channelId1 = Long.toUnsignedString(channelId); + guild.getScheduledEventsView().stream() + .filter(scheduledEvent -> scheduledEvent.getType().isChannel() && scheduledEvent.getLocation().equals(channelId1)) + .forEach(scheduledEvent -> guild.getScheduledEventsView().remove(scheduledEvent.getIdLong())); + } + getJDA().getEventCache().clear(EventCache.Type.CHANNEL, channelId); return null; } diff --git a/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java b/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java index 90130d599c..45e8cd5847 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java @@ -129,7 +129,7 @@ public synchronized void clear(Type type, long id) public enum Type { - USER, MEMBER, GUILD, CHANNEL, ROLE, RELATIONSHIP, CALL + USER, MEMBER, GUILD, CHANNEL, ROLE, RELATIONSHIP, CALL, SCHEDULED_EVENT } private class CacheNode diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildDeleteHandler.java index 9a2a4a5381..49d4e72f4f 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/GuildDeleteHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildDeleteHandler.java @@ -60,16 +60,16 @@ protected Long handleInternally(DataObject content) { setupController.onUnavailable(id); getJDA().handleEvent( - new GuildUnavailableEvent( - getJDA(), responseNumber, - guild)); + new GuildUnavailableEvent( + getJDA(), responseNumber, + guild)); } else { getJDA().handleEvent( - new GuildLeaveEvent( - getJDA(), responseNumber, - guild)); + new GuildLeaveEvent( + getJDA(), responseNumber, + guild)); } getJDA().getEventCache().clear(EventCache.Type.GUILD, id); return null; diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventCreateHandler.java new file mode 100644 index 0000000000..cf89b764d3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventCreateHandler.java @@ -0,0 +1,54 @@ +/* + * 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.internal.handle; + +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventCreateEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; + +public class ScheduledEventCreateHandler extends SocketHandler +{ + public ScheduledEventCreateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + return null; + long guildId = content.getUnsignedLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + EventCache.LOG.debug("Caching SCHEDULED_EVENT_CREATE for uncached guild with id {}", guildId); + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + ScheduledEvent event = getJDA().getEntityBuilder().createScheduledEvent(guild, content); + getJDA().handleEvent(new ScheduledEventCreateEvent(getJDA(), responseNumber, event)); + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventDeleteHandler.java new file mode 100644 index 0000000000..7c8e3e3c40 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventDeleteHandler.java @@ -0,0 +1,59 @@ +/* + * 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.internal.handle; + +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventDeleteEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; + +public class ScheduledEventDeleteHandler extends SocketHandler +{ + public ScheduledEventDeleteHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + return null; + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + EventCache.LOG.debug("SCHEDULED_EVENT_DELETE was received for a Guild that is not yet cached: {}", content); + return null; + } + + final long eventId = content.getLong("id"); + ScheduledEvent removedEvent = guild.getScheduledEventsView().remove(eventId); + if (removedEvent != null) + { + getJDA().handleEvent( + new ScheduledEventDeleteEvent( + getJDA(), responseNumber, + removedEvent)); + } + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUpdateHandler.java new file mode 100644 index 0000000000..651ff818a3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUpdateHandler.java @@ -0,0 +1,136 @@ +/* + * 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.internal.handle; + +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.concrete.StageChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.events.guild.scheduledevent.update.*; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.entities.ScheduledEventImpl; + +import java.time.OffsetDateTime; +import java.util.*; + +public class ScheduledEventUpdateHandler extends SocketHandler +{ + public ScheduledEventUpdateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + return null; + long guildId = content.getUnsignedLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + EventCache.LOG.debug("Caching SCHEDULED_EVENT_UPDATE for uncached guild with id {}", guildId); + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + ScheduledEventImpl event = (ScheduledEventImpl) guild.getScheduledEventById(content.getUnsignedLong("id")); + if (event == null) + { + api.getEntityBuilder().createScheduledEvent(guild, content); + return null; + } + + final String name = content.getString("name"); + final String description = content.getString("description", null); + final OffsetDateTime startTime = content.getOffsetDateTime("scheduled_start_time"); + final OffsetDateTime endTime = content.getOffsetDateTime("scheduled_end_time", null); + final ScheduledEvent.Status status = ScheduledEvent.Status.fromKey(content.getInt("status", -1)); + final String imageUrl = content.getString("image", null); + String location = content.getString("channel_id", null); + GuildChannel channel = null; + String oldLocation = event.getLocation(); + if (location == null) + location = content.getObject("entity_metadata").getString("location", null); + else + channel = guild.getGuildChannelById(location); + + if (!Objects.equals(name, event.getName())) + { + String oldName = event.getName(); + event.setName(name); + getJDA().handleEvent( + new ScheduledEventUpdateNameEvent(getJDA(), responseNumber, + event, oldName)); + } + if (!Objects.equals(description, event.getDescription())) + { + String oldDescription = event.getDescription(); + event.setDescription(description); + getJDA().handleEvent(new ScheduledEventUpdateDescriptionEvent(getJDA(), responseNumber, event, oldDescription)); + } + if (!Objects.equals(startTime, event.getStartTime())) + { + OffsetDateTime oldStartTime = event.getStartTime(); + event.setStartTime(startTime); + getJDA().handleEvent(new ScheduledEventUpdateStartTimeEvent(getJDA(), responseNumber, event, oldStartTime)); + } + if (!Objects.equals(endTime, event.getEndTime())) + { + OffsetDateTime oldEndTime = event.getEndTime(); + event.setEndTime(endTime); + getJDA().handleEvent(new ScheduledEventUpdateEndTimeEvent(getJDA(), responseNumber, event, oldEndTime)); + } + if (!Objects.equals(status, event.getStatus())) + { + ScheduledEvent.Status oldStatus = event.getStatus(); + event.setStatus(status); + getJDA().handleEvent(new ScheduledEventUpdateStatusEvent(getJDA(), responseNumber, event, oldStatus)); + } + if (channel == null && location != null && !location.equals(event.getLocation())) + { + event.setLocation(location); + event.setType(ScheduledEvent.Type.EXTERNAL); + getJDA().handleEvent(new ScheduledEventUpdateLocationEvent(getJDA(), responseNumber, event, oldLocation)); + } + if (channel instanceof StageChannel && !location.equals(event.getLocation())) + { + event.setLocation(channel.getId()); + event.setType(ScheduledEvent.Type.STAGE_INSTANCE); + getJDA().handleEvent(new ScheduledEventUpdateLocationEvent(getJDA(), responseNumber, event, oldLocation)); + } + if (channel instanceof VoiceChannel && !location.equals(event.getLocation())) + { + event.setLocation(channel.getId()); + event.setType(ScheduledEvent.Type.VOICE); + getJDA().handleEvent(new ScheduledEventUpdateLocationEvent(getJDA(), responseNumber, event, oldLocation)); + } + if (!Objects.equals(imageUrl, event.getImageUrl())) + { + String oldImageUrl = event.getImageUrl(); + event.setImage(imageUrl); + getJDA().handleEvent(new ScheduledEventUpdateImageEvent(getJDA(), responseNumber, event, oldImageUrl)); + } + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUserHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUserHandler.java new file mode 100644 index 0000000000..91e9184617 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/ScheduledEventUserHandler.java @@ -0,0 +1,66 @@ +/* + * 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.internal.handle; + +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventUserAddEvent; +import net.dv8tion.jda.api.events.guild.scheduledevent.ScheduledEventUserRemoveEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; + +public class ScheduledEventUserHandler extends SocketHandler +{ + private final boolean add; + + public ScheduledEventUserHandler(JDAImpl api, boolean add) + { + super(api); + this.add = add; + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SCHEDULED_EVENTS)) + return null; + long guildId = content.getUnsignedLong("guild_id", 0L); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + EventCache.LOG.debug("Caching SCHEDULED_EVENT_USER_ADD for uncached guild with id {}", guildId); + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + ScheduledEvent event = guild.getScheduledEventById(content.getUnsignedLong("guild_scheduled_event_id")); + long userId = content.getUnsignedLong("user_id"); + if (event == null) + return null; + + if (add) + getJDA().handleEvent(new ScheduledEventUserAddEvent(getJDA(), responseNumber, event, userId)); + else + getJDA().handleEvent(new ScheduledEventUserRemoveEvent(getJDA(), responseNumber, event, userId)); + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/managers/ScheduledEventManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/ScheduledEventManagerImpl.java new file mode 100644 index 0000000000..b25a2eb2aa --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/managers/ScheduledEventManagerImpl.java @@ -0,0 +1,233 @@ +/* + * 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.internal.managers; + +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.entities.Icon; +import net.dv8tion.jda.api.entities.channel.concrete.StageChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.managers.ScheduledEventManager; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.requests.Route; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; +import okhttp3.RequestBody; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + +public class ScheduledEventManagerImpl extends ManagerBase implements ScheduledEventManager +{ + protected ScheduledEvent event; + protected String name, description; + protected long channelId; + protected String location; + protected Icon image; + protected OffsetDateTime startTime, endTime; + protected ScheduledEvent.Type entityType; + protected ScheduledEvent.Status status; + + public ScheduledEventManagerImpl(ScheduledEvent event) + { + super(event.getJDA(), Route.Guilds.MODIFY_SCHEDULED_EVENT.compile(event.getGuild().getId(), event.getId())); + this.event = event; + if (isPermissionChecksEnabled()) + checkPermissions(); + } + + @Nonnull + @Override + public ScheduledEvent getScheduledEvent() + { + ScheduledEvent realEvent = event.getGuild().getScheduledEventById(event.getIdLong()); + if (realEvent != null) + event = realEvent; + return event; + } + + @Nonnull + @Override + @CheckReturnValue + public ScheduledEventManagerImpl setName(@Nonnull String name) + { + Checks.notBlank(name, "Name"); + Checks.notLonger(name, ScheduledEvent.MAX_NAME_LENGTH, "Name"); + this.name = name; + set |= NAME; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setDescription(@Nullable String description) + { + Checks.notLonger(description, ScheduledEvent.MAX_DESCRIPTION_LENGTH, "Description"); + this.description = description; + set |= DESCRIPTION; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setImage(@Nullable Icon icon) + { + this.image = icon; + set |= IMAGE; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setLocation(@Nonnull GuildChannel channel) + { + Checks.notNull(channel, "Channel"); + if (!channel.getGuild().equals(event.getGuild())) + { + throw new IllegalArgumentException("Invalid parameter: Channel has to be from the same guild as the scheduled event!"); + } + else if (channel instanceof StageChannel) + { + this.channelId = channel.getIdLong(); + this.entityType = ScheduledEvent.Type.STAGE_INSTANCE; + } + else if (channel instanceof VoiceChannel) + { + this.channelId = channel.getIdLong(); + this.entityType = ScheduledEvent.Type.VOICE; + } + else + { + throw new IllegalArgumentException("Invalid parameter: Can only set location to Voice and Stage Channels!"); + } + + set |= LOCATION; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setLocation(@Nonnull String location) + { + Checks.notBlank(location, "Location"); + Checks.notLonger(location, ScheduledEvent.MAX_LOCATION_LENGTH, "Location"); + this.location = location; + this.entityType = ScheduledEvent.Type.EXTERNAL; + set |= LOCATION; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setStartTime(@Nonnull TemporalAccessor startTime) + { + Checks.notNull(startTime, "Start Time"); + OffsetDateTime offsetStartTime = Helpers.toOffsetDateTime(startTime); + Checks.check(offsetStartTime.isAfter(OffsetDateTime.now()), "Cannot schedule event in the past!"); + Checks.check(offsetStartTime.isBefore(OffsetDateTime.now().plusYears(5)), "Scheduled start and end times must be within five years."); + this.startTime = offsetStartTime; + set |= START_TIME; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setEndTime(@Nonnull TemporalAccessor endTime) + { + Checks.notNull(endTime, "End Time"); + OffsetDateTime offsetEndTime = Helpers.toOffsetDateTime(endTime); + Checks.check(offsetEndTime.isBefore(OffsetDateTime.now().plusYears(5)), "Scheduled start and end times must be within five years."); + this.endTime = offsetEndTime; + set |= END_TIME; + return this; + } + + @Nonnull + @Override + public ScheduledEventManager setStatus(@Nonnull ScheduledEvent.Status status) + { + Checks.notNull(status, "Status"); + Checks.check(status != ScheduledEvent.Status.UNKNOWN, "Cannot set the event status to an unknown status!"); + Checks.check(status != ScheduledEvent.Status.SCHEDULED && getScheduledEvent().getStatus() != ScheduledEvent.Status.ACTIVE, "Cannot perform status update!"); + this.status = status; + set |= STATUS; + return this; + } + + @Override + protected RequestBody finalizeData() + { + preChecks(); + DataObject object = DataObject.empty(); + if (shouldUpdate(NAME)) + object.put("name", name); + if (shouldUpdate(DESCRIPTION)) + object.put("description", description); + if (shouldUpdate(LOCATION)) + { + object.put("entity_type", entityType.getKey()); + switch (entityType) + { + case STAGE_INSTANCE: + case VOICE: + object.put("channel_id", channelId); + break; + case EXTERNAL: + object.put("entity_metadata", DataObject.empty().put("location", location)); + object.put("channel_id", null); + break; + default: + throw new IllegalStateException("ScheduledEventType " + entityType + " is not supported!"); + } + } + if (shouldUpdate(START_TIME)) + object.put("scheduled_start_time", startTime.format(DateTimeFormatter.ISO_DATE_TIME)); + if (shouldUpdate(END_TIME)) + object.put("scheduled_end_time", endTime.format(DateTimeFormatter.ISO_DATE_TIME)); + if (shouldUpdate(IMAGE)) + object.put("image", image != null ? image.getEncoding() : null); + if (shouldUpdate(STATUS)) + object.put("status", status.getKey()); + + return getRequestBody(object); + } + + private void preChecks() + { + if (shouldUpdate(LOCATION)) + { + if (entityType == ScheduledEvent.Type.EXTERNAL) + Checks.check((endTime).isAfter(startTime), "Cannot schedule event to end before starting!"); + Checks.check(getScheduledEvent().getStatus() == ScheduledEvent.Status.SCHEDULED, "Cannot update location of non-scheduled event."); + if (entityType == ScheduledEvent.Type.EXTERNAL && endTime == null && getScheduledEvent().getEndTime() == null) + throw new IllegalStateException("Missing required parameter: End Time"); + } + + if (shouldUpdate(START_TIME)) + { + Checks.check(getScheduledEvent().getStatus() == ScheduledEvent.Status.SCHEDULED, "Cannot update start time of non-scheduled event!"); + Checks.check((endTime == null && getScheduledEvent().getEndTime() == null) || (endTime == null ? getScheduledEvent().getEndTime() : endTime).isAfter(startTime), "Cannot schedule event to end before starting!"); + } + + if (shouldUpdate(END_TIME)) + Checks.check((startTime == null ? getScheduledEvent().getStartTime() : startTime).isBefore(endTime), "Cannot schedule event to end before starting!"); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/Route.java b/src/main/java/net/dv8tion/jda/internal/requests/Route.java index 7b35c5bfa1..e3db717a6e 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/Route.java @@ -159,6 +159,13 @@ public static class Guilds public static final Route LIST_ACTIVE_THREADS = new Route(GET, "guilds/{guild_id}/threads/active"); + public static final Route GET_SCHEDULED_EVENT = new Route(GET, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}"); + public static final Route GET_SCHEDULED_EVENTS = new Route(GET, "guilds/{guild_id}/scheduled-events"); + public static final Route CREATE_SCHEDULED_EVENT = new Route(POST, "guilds/{guild_id}/scheduled-events"); + public static final Route MODIFY_SCHEDULED_EVENT = new Route(PATCH, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}"); + public static final Route DELETE_SCHEDULED_EVENT = new Route(DELETE, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}"); + public static final Route GET_SCHEDULED_EVENT_USERS = new Route(GET, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}/users"); + //Client Only public static final Route CREATE_GUILD = new Route(POST, "guilds"); public static final Route DELETE_GUILD = new Route(POST, "guilds/{guild_id}/delete"); diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java index 74bbc293c2..3fbdff18da 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java @@ -1339,6 +1339,11 @@ protected void setupHandlers() handlers.put("GUILD_CREATE", new GuildCreateHandler(api)); handlers.put("GUILD_DELETE", new GuildDeleteHandler(api)); handlers.put("GUILD_EMOJIS_UPDATE", new GuildEmojisUpdateHandler(api)); + handlers.put("GUILD_SCHEDULED_EVENT_CREATE", new ScheduledEventCreateHandler(api)); + handlers.put("GUILD_SCHEDULED_EVENT_UPDATE", new ScheduledEventUpdateHandler(api)); + handlers.put("GUILD_SCHEDULED_EVENT_DELETE", new ScheduledEventDeleteHandler(api)); + handlers.put("GUILD_SCHEDULED_EVENT_USER_ADD", new ScheduledEventUserHandler(api, true)); + handlers.put("GUILD_SCHEDULED_EVENT_USER_REMOVE", new ScheduledEventUserHandler(api, false)); handlers.put("GUILD_MEMBER_ADD", new GuildMemberAddHandler(api)); handlers.put("GUILD_MEMBER_REMOVE", new GuildMemberRemoveHandler(api)); handlers.put("GUILD_MEMBER_UPDATE", new GuildMemberUpdateHandler(api)); diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/ScheduledEventActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ScheduledEventActionImpl.java new file mode 100644 index 0000000000..ed14cd24c0 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ScheduledEventActionImpl.java @@ -0,0 +1,221 @@ +/* + * 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.internal.requests.restaction; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.entities.Icon; +import net.dv8tion.jda.api.entities.channel.concrete.StageChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.requests.Request; +import net.dv8tion.jda.api.requests.Response; +import net.dv8tion.jda.api.requests.restaction.ScheduledEventAction; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.requests.Route; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; +import okhttp3.RequestBody; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +public class ScheduledEventActionImpl extends AuditableRestActionImpl implements ScheduledEventAction +{ + protected final Guild guild; + protected String name, description; + protected Icon image; + protected long channelId; + protected String location; + protected OffsetDateTime startTime, endTime; + protected final ScheduledEvent.Type entityType; + + public ScheduledEventActionImpl(String name, String location, TemporalAccessor startTime, TemporalAccessor endTime, Guild guild) + { + super(guild.getJDA(), Route.Guilds.CREATE_SCHEDULED_EVENT.compile(guild.getId())); + this.guild = guild; + setName(name); + setStartTime(startTime); + setEndTime(endTime); + Checks.notNull(location, "Location"); + Checks.notBlank(location, "Location"); + Checks.notEmpty(location, "Location"); + Checks.notLonger(location, ScheduledEvent.MAX_LOCATION_LENGTH, "Location"); + this.location = location; + this.entityType = ScheduledEvent.Type.EXTERNAL; + } + + public ScheduledEventActionImpl(String name, GuildChannel channel, TemporalAccessor startTime, Guild guild) + { + super(guild.getJDA(), Route.Guilds.CREATE_SCHEDULED_EVENT.compile(guild.getId())); + this.guild = guild; + setName(name); + setStartTime(startTime); + Checks.notNull(channel, "Channel"); + if (!channel.getGuild().equals(guild)) + { + throw new IllegalArgumentException("Invalid parameter: Channel has to be from the same guild as the scheduled event!"); + } + else if (channel instanceof StageChannel) + { + this.channelId = channel.getIdLong(); + this.entityType = ScheduledEvent.Type.STAGE_INSTANCE; + } + else if (channel instanceof VoiceChannel) + { + this.channelId = channel.getIdLong(); + this.entityType = ScheduledEvent.Type.VOICE; + } + else + { + throw new IllegalArgumentException("Invalid parameter: Can only set location to Voice and Stage Channels!"); + } + } + + @Nonnull + @Override + public ScheduledEventActionImpl setCheck(BooleanSupplier checks) + { + return (ScheduledEventActionImpl) super.setCheck(checks); + } + + @Nonnull + @Override + public ScheduledEventActionImpl timeout(long timeout, @Nonnull TimeUnit unit) + { + return (ScheduledEventActionImpl) super.timeout(timeout, unit); + } + + @Nonnull + @Override + public ScheduledEventActionImpl deadline(long timestamp) + { + return (ScheduledEventActionImpl) super.deadline(timestamp); + } + + @Nonnull + @Override + public ScheduledEventActionImpl reason(@Nullable String reason) + { + return (ScheduledEventActionImpl) super.reason(reason); + } + + @Nonnull + @Override + public Guild getGuild() + { + return guild; + } + + @Nonnull + @Override + public ScheduledEventActionImpl setName(@Nullable String name) + { + Checks.notBlank(name, "Name"); + Checks.notLonger(name, ScheduledEvent.MAX_NAME_LENGTH, "Name"); + this.name = name; + return this; + } + + @Nonnull + @Override + @CheckReturnValue + public ScheduledEventActionImpl setDescription(@Nullable String description) + { + if (description != null) + Checks.notLonger(description, ScheduledEvent.MAX_DESCRIPTION_LENGTH, "Description"); + this.description = description; + return this; + } + + @Nonnull + @Override + public ScheduledEventAction setStartTime(@Nonnull TemporalAccessor startTime) + { + Checks.notNull(startTime, "Start Time"); + OffsetDateTime offsetStartTime = Helpers.toOffsetDateTime(startTime); + Checks.check(offsetStartTime.isAfter(OffsetDateTime.now()), "Cannot schedule event in the past!"); + Checks.check(offsetStartTime.isBefore(OffsetDateTime.now().plusYears(5)), "Scheduled start and end times must be within five years."); + this.startTime = offsetStartTime; + return this; + } + + @Nonnull + @Override + public ScheduledEventAction setEndTime(@Nullable TemporalAccessor endTime) + { + Checks.notNull(endTime, "End Time"); + OffsetDateTime offsetEndTime = Helpers.toOffsetDateTime(endTime); + Checks.check(offsetEndTime.isAfter(startTime), "Cannot schedule event to end before its starting!"); + Checks.check(offsetEndTime.isBefore(OffsetDateTime.now().plusYears(5)), "Scheduled start and end times must be within five years."); + this.endTime = offsetEndTime; + return this; + } + + @Nonnull + @Override + public ScheduledEventAction setImage(@Nullable Icon icon) + { + this.image = icon; + return this; + } + + @Override + protected RequestBody finalizeData() + { + DataObject object = DataObject.empty(); + object.put("entity_type", entityType.getKey()); + object.put("privacy_level", 2); + object.put("name", name); + object.put("scheduled_start_time", startTime.format(DateTimeFormatter.ISO_DATE_TIME)); + + switch (entityType) + { + case STAGE_INSTANCE: + case VOICE: + object.put("channel_id", channelId); + break; + case EXTERNAL: + object.put("entity_metadata", DataObject.empty().put("location", location)); + break; + default: + throw new IllegalStateException("ScheduledEventType " + entityType + " is not supported!"); + } + + if (description != null) + object.put("description", description); + if (image != null) + object.put("image", image.getEncoding()); + if (endTime != null) + object.put("scheduled_end_time", endTime.format(DateTimeFormatter.ISO_DATE_TIME)); + + return getRequestBody(object); + } + + @Override + protected void handleSuccess(Response response, Request request) + { + request.onSuccess(api.getEntityBuilder().createScheduledEvent((GuildImpl) guild, response.getObject())); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/ScheduledEventMembersPaginationActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/ScheduledEventMembersPaginationActionImpl.java new file mode 100644 index 0000000000..003f12d907 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/ScheduledEventMembersPaginationActionImpl.java @@ -0,0 +1,96 @@ +/* + * 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.internal.requests.restaction.pagination; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.exceptions.ParsingException; +import net.dv8tion.jda.api.requests.Request; +import net.dv8tion.jda.api.requests.Response; +import net.dv8tion.jda.api.requests.restaction.pagination.ScheduledEventMembersPaginationAction; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.entities.EntityBuilder; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.requests.Route; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ScheduledEventMembersPaginationActionImpl extends PaginationActionImpl implements ScheduledEventMembersPaginationAction +{ + protected final Guild guild; + + public ScheduledEventMembersPaginationActionImpl(ScheduledEvent event) + { + super(event.getGuild().getJDA(), Route.Guilds.GET_SCHEDULED_EVENT_USERS.compile(event.getGuild().getId(), event.getId()).withQueryParams("with_member", "true"), 1, 100, 100); + this.guild = event.getGuild(); + } + + @Nonnull + @Override + public Guild getGuild() + { + return guild; + } + + @Override + protected void handleSuccess(Response response, Request> request) + { + DataArray array = response.getArray(); + List members = new ArrayList<>(array.length()); + EntityBuilder builder = api.getEntityBuilder(); + for (int i = 0; i < array.length(); i++) + { + try + { + DataObject object = array.getObject(i); + if (object.isNull("member")) + continue; + DataObject userObject = object.getObject("user"); + DataObject memberObject = object.getObject("member"); + Member member = builder.createMember((GuildImpl) guild, memberObject.put("user", userObject)); + members.add(member); + } + catch (ParsingException | NullPointerException e) + { + LOG.warn("Encountered an exception in ScheduledEventPagination", e); + } + } + + if (order == PaginationOrder.BACKWARD) + Collections.reverse(members); + if (useCache) + cached.addAll(members); + + if (!members.isEmpty()) + { + last = members.get(members.size() - 1); + lastKey = last.getIdLong(); + } + request.onSuccess(members); + } + + @Override + protected long getKey(Member it) + { + return it.getIdLong(); + } +}