From a17b91c0555407c62fc1f3ba3dc4f2a7e5ae74a3 Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Mon, 15 Nov 2021 15:06:32 -0600 Subject: [PATCH 01/21] Start work for guild scheduled events --- .../discord4j/core/object/audit/ActionType.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/discord4j/core/object/audit/ActionType.java b/core/src/main/java/discord4j/core/object/audit/ActionType.java index 508db881d..bbfd4abcc 100644 --- a/core/src/main/java/discord4j/core/object/audit/ActionType.java +++ b/core/src/main/java/discord4j/core/object/audit/ActionType.java @@ -59,19 +59,17 @@ public enum ActionType { INTEGRATION_CREATE(80), INTEGRATION_UPDATE(81), INTEGRATION_DELETE(82), - + GUILD_SCHEDULED_EVENT_CREATE(100), + GUILD_SCHEDULED_EVENT_UPDATE(101), + GUILD_SCHEDULED_EVENT_DELETE(102), AUTO_MODERATION_RULE_CREATE(140), - AUTO_MODERATION_RULE_UPDATE(141), - AUTO_MODERATION_RULE_DELETE(142), - AUTO_MODERATION_BLOCK_MESSAGE(143), - AUTO_MODERATION_FLAG_TO_CHANNEL(144), - AUTO_MODERATION_USER_COMMUNICATION_DISABLED(145); + /** * Gets the type of action. It is guaranteed that invoking {@link #getValue()} from the returned enum will equal * ({@link #equals(Object)}) the supplied {@code value}. @@ -116,6 +114,9 @@ public static ActionType of(final int value) { case 80: return INTEGRATION_CREATE; case 81: return INTEGRATION_UPDATE; case 82: return INTEGRATION_DELETE; + case 100: return GUILD_SCHEDULED_EVENT_CREATE; + case 101: return GUILD_SCHEDULED_EVENT_UPDATE; + case 102: return GUILD_SCHEDULED_EVENT_DELETE; case 140: return AUTO_MODERATION_RULE_CREATE; case 141: return AUTO_MODERATION_RULE_UPDATE; case 142: return AUTO_MODERATION_RULE_DELETE; From 1edc0d6bf872eeae88392bc615aa4c42bc0286b8 Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Mon, 15 Nov 2021 22:03:29 -0600 Subject: [PATCH 02/21] Continued work on implementing scheduled events --- .../core/object/audit/ChangeKey.java | 9 +- .../rest/entity/RestGuildScheduledEvent.java | 98 +++++++++++++++++++ .../java/discord4j/rest/route/Routes.java | 53 ++++++++++ .../rest/service/ScheduledEventService.java | 29 ++++++ 4 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java create mode 100644 rest/src/main/java/discord4j/rest/service/ScheduledEventService.java diff --git a/core/src/main/java/discord4j/core/object/audit/ChangeKey.java b/core/src/main/java/discord4j/core/object/audit/ChangeKey.java index 6af262eb7..ea604da1d 100644 --- a/core/src/main/java/discord4j/core/object/audit/ChangeKey.java +++ b/core/src/main/java/discord4j/core/object/audit/ChangeKey.java @@ -116,7 +116,7 @@ public final class ChangeKey { public static final ChangeKey DENY = changeKey("deny", PERMISSION_SET_PARSER); /** Invite code changed */ public static final ChangeKey INVITE_CODE = changeKey("code", STRING_PARSER); - /** Channel for invite code changed */ + /** Channel for invite code or guild scheduled event changed */ public static final ChangeKey INVITE_CHANNEL_ID = changeKey("channel_id", SNOWFLAKE_PARSER); /** Person who created invite code changed */ public static final ChangeKey INVITER_ID = changeKey("inviter_id", SNOWFLAKE_PARSER); @@ -167,6 +167,13 @@ public final class ChangeKey { public static final ChangeKey THREAD_AUTO_ARCHIVE_DURATION = changeKey("auto_archive_duration", INTEGER_PARSER); /** Thread is now locked/unlocked */ public static final ChangeKey THREAD_LOCKED = changeKey("locked", BOOLEAN_PARSER); + /** entity type of guild scheduled event changed */ + public static final ChangeKey ENTITY_TYPE = changeKey("entity_type", INTEGER_PARSER); + /** channel ID for guild scheduled event changed */ + public static final ChangeKey LOCATION = changeKey("location", STRING_PARSER); + /** status of guild scheduled event changed */ + public static final ChangeKey STATUS = changeKey("status", INTEGER_PARSER); + private static ChangeKey changeKey(String name, BiFunction parser) { return new ChangeKey<>(name, parser); diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java new file mode 100644 index 000000000..fe5a2860e --- /dev/null +++ b/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java @@ -0,0 +1,98 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.rest.entity; + +import discord4j.common.util.Snowflake; +import discord4j.discordjson.json.GuildScheduledEventData; +import discord4j.rest.RestClient; +import reactor.core.publisher.Mono; + +/** + * Represents a guild scheduled event within Discord. + */ +public class RestGuildScheduledEvent { + + private final RestClient restClient; + + private final long guildId; + + private final long id; + + private RestGuildScheduledEvent(RestClient restClient, long guildId, long id) { + this.restClient = restClient; + this.guildId = guildId; + this.id = id; + } + + /** + * Create a {@link RestGuildScheduledEvent} for the given parameters. This method does not perform any API request. + * + * @param restClient The client to make API requests. + * @param guildId The ID of the guild this entity belongs to. + * @param id the ID of this entity. + * @return A {@code RestGuildScheduledEvent} represented by the given parameters. + */ + public static RestGuildScheduledEvent create(RestClient restClient, Snowflake guildId, Snowflake id) { + return new RestGuildScheduledEvent(restClient, guildId.asLong(), id.asLong()); + } + + static RestGuildScheduledEvent create(RestClient restClient, long guildId, long id) { + return new RestGuildScheduledEvent(restClient, guildId, id); + } + + /** + * Returns the ID of the guild this scheduled event belongs to. + * + * @return The ID of the guild this scheduled event belongs to. + */ + public Snowflake getGuildId() { + return Snowflake.of(guildId); + } + + /** + * Returns the ID of this scheduled event. + * + * @return The ID of this scheduled event. + */ + public Snowflake getId() { + return Snowflake.of(id); + } + + /** + * Return the guild tied to thi role as a REST operations handle. + * + * @return The parent guild for this scheduled event. + */ + public RestGuild guild() { + return RestGuild.create(restClient, guildId); + } + + //TODO: Edit + + //TODO: Delete + + //TODO: get users + + /** + * Retrieve this scheduled event's data upon subscription. + * + * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData} belonging to + * this scheduled event. If an error is received, it is emitted through the {@code Mono}. + */ + //TODO: get data +} diff --git a/rest/src/main/java/discord4j/rest/route/Routes.java b/rest/src/main/java/discord4j/rest/route/Routes.java index 994da914e..54b84a7ee 100644 --- a/rest/src/main/java/discord4j/rest/route/Routes.java +++ b/rest/src/main/java/discord4j/rest/route/Routes.java @@ -1193,4 +1193,57 @@ public abstract class Routes { /////////////////////////////////////////// public static final Route INTERACTION_RESPONSE_CREATE = Route.post("/interactions/{interaction.id}/{interaction.token}/callback"); + + ///////////////////////////////////////////////////// + ////////// Guild Scheduled Event Resource /////////// + ///////////////////////////////////////////////////// + + /** + * Returns a list of all scheduled events for a guild. + * + * @see + * https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild + */ + public static final Route GUILD_SCHEDULED_EVENTS_GET = Route.get("/guilds/{guild.id}/scheduled-events"); + + /** + * Creates a guild scheduled event for the given guild. Returns a scheduled event object on success. + * + * @see + * ttps://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event + */ + public static final Route GUILD_SCHEDULED_EVENT_CREATE = Route.post("/guilds/{guild.id}/scheduled-events"); + + /** + * Returns a scheduled event for the given guild. + * + * @see + * https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event + */ + public static final Route GUILD_SCHEDULED_EVENT_GET = Route.get("/guilds/{guild.id}/scheduled-events/{event.id}"); + + /** + * Modifies a scheduled event for the given guild. Returns the modified scheduled event object on success. + * + * @see + * https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event + */ + public static final Route GUILD_SCHEDULED_EVENT_MODIFY = Route.patch("/guilds/{guild.id}/scheduled-events/{event.id}"); + + /** + * Deletes a scheduled event for the given guild. Returns a 204 empty response on success. + * + * @see + * https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event + */ + public static final Route GUILD_SCHEDULED_EVENT_DELETE = Route.delete("/guilds/{guild.id}/scheduled-events/{event.id}"); + + /** + * Returns a list of users RSVP'd to the scheduled event for the given guild. Returns a list of user objects on + * success with an optional `guild_member` property for each user if `with_member` query param is passed. + * + * @see + * https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users + */ + public static final Route GUILD_SCHEDULED_EVENT_USERS_GET = Route.get("/guilds/{guild.id}/scheduled-events/{event.id}/users"); } diff --git a/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java b/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java new file mode 100644 index 000000000..5949af47b --- /dev/null +++ b/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java @@ -0,0 +1,29 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.rest.service; + +import discord4j.rest.request.Router; + +public class ScheduledEventService extends RestService { + + public ScheduledEventService(Router router) { + super(router); + } + + //TODO: stuff +} From 7d08a3edfa5cb1000ca4867492b2215a5d2eef6c Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Tue, 16 Nov 2021 14:15:54 -0600 Subject: [PATCH 03/21] Add events requests to guild service, need to finish up rest stuff --- .../java/discord4j/rest/entity/RestGuild.java | 43 ++++++++++++++++++ .../discord4j/rest/service/GuildService.java | 44 +++++++++++++++++++ .../rest/service/ScheduledEventService.java | 29 ------------ 3 files changed, 87 insertions(+), 29 deletions(-) delete mode 100644 rest/src/main/java/discord4j/rest/service/ScheduledEventService.java diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuild.java b/rest/src/main/java/discord4j/rest/entity/RestGuild.java index 176255ad0..14293fd7c 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestGuild.java +++ b/rest/src/main/java/discord4j/rest/entity/RestGuild.java @@ -121,6 +121,17 @@ public RestRole role(Snowflake roleId) { return RestRole.create(restClient, id, roleId.asLong()); } + /** + * Returns a {@link RestGuildScheduledEvent} representation under this guild. + * This method does not perform any API request. + * + * @param eventId The entity ID + * @return a {@code RestGuildScheduledEvent} with the given ID, under this guild + */ + public RestGuildScheduledEvent scheduledEvent(Snowflake eventId) { + return RestGuildScheduledEvent.create(restClient, id, eventId.asLong()); + } + /** * Modify a guild's settings. Requires the {@link Permission#MANAGE_GUILD} permission. Returns the updated guild * object on success. @@ -329,6 +340,38 @@ public Flux getTemplates() { return restClient.getTemplateService().getTemplates(id); } + /** + * Requests to retrieve the scheduled event under this guild. + * + * @param eventId The ID of the event + * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData}. If an + * error is received, it is emitted through the {@code Mono}. + */ + public Mono getScheduledEvent(Snowflake eventId) { + return restClient.getGuildService().getScheduledEvent(id, eventId.asLong()); + } + + /** + * Requests to retrieve the scheduled events under this guild. + * + * @return A {@link Flux} where, upon successful completion, emits 0...N {@link GuildScheduledEventData}. If an + * error is received, it is emitted through the {@code Flux}. + */ + public Flux getScheduledEvents(@Nullable Boolean withUserCounts) { + Map queryParams = new HashMap<>(); + Optional.ofNullable(withUserCounts).ifPresent(value -> queryParams.put("with_user_count", value)); + return restClient.getGuildService().getScheduledEvents(id, queryParams); + } + + //TODO: get RSVP'd users for an event + + //TODO: create event + + //TODO: modify event + + //TODO: delete event + + @Override public boolean equals(final Object o) { if (this == o) return true; diff --git a/rest/src/main/java/discord4j/rest/service/GuildService.java b/rest/src/main/java/discord4j/rest/service/GuildService.java index 479280bd7..70adda0e8 100644 --- a/rest/src/main/java/discord4j/rest/service/GuildService.java +++ b/rest/src/main/java/discord4j/rest/service/GuildService.java @@ -332,4 +332,48 @@ public Mono modifyOthersVoiceState(long guildId, long userId, UpdateUserVo .bodyToMono(Void.class); } + public Mono getScheduledEvent(long guildId, long eventId) { + return Routes.GUILD_SCHEDULED_EVENT_GET.newRequest(guildId, eventId) + .exchange(getRouter()) + .bodyToMono(GuildScheduledEventData.class); + } + + public Flux getScheduledEvents(long guildId, Map queryParams) { + return Routes.GUILD_SCHEDULED_EVENTS_GET.newRequest(guildId) + .query(queryParams) + .exchange(getRouter()) + .bodyToMono(GuildScheduledEventData[].class) + .flatMapMany(Flux::fromArray); + } + + public Mono createScheduledEvent(long guildId, GuildScheduledEventCreateRequest request) { + return Routes.GUILD_SCHEDULED_EVENT_CREATE.newRequest(guildId) + .body(request) + .exchange(getRouter()) + .bodyToMono(GuildScheduledEventData.class); + } + + public Mono modifyScheduledEvent(long guildId, long eventId, GuildScheduledEventModifyRequest request, @Nullable String reason) { + return Routes.GUILD_SCHEDULED_EVENT_MODIFY.newRequest(guildId, eventId) + .body(request) + .optionalHeader("X-Audit-Log-Reason", reason) + .exchange(getRouter()) + .bodyToMono(GuildScheduledEventData.class); + } + + public Mono deleteScheduledEvent(long guildId, long eventId, @Nullable String reason) { + return Routes.GUILD_SCHEDULED_EVENT_DELETE.newRequest(guildId, eventId) + .optionalHeader("X-Audit-Log-Reason", reason) + .exchange(getRouter()) + .bodyToMono(Void.class); + } + + //TODO: Discord might be changing the data structure of the response, awaiting confirmation. + public Flux getScheduledEventUsers(long guildId, long eventId, Map queryParams) { + return Routes.GUILD_SCHEDULED_EVENT_USERS_GET.newRequest(guildId, eventId) + .query(queryParams) + .exchange(getRouter()) + .bodyToMono(UserData[].class) + .flatMapMany(Flux::fromArray); + } } diff --git a/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java b/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java deleted file mode 100644 index 5949af47b..000000000 --- a/rest/src/main/java/discord4j/rest/service/ScheduledEventService.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - -package discord4j.rest.service; - -import discord4j.rest.request.Router; - -public class ScheduledEventService extends RestService { - - public ScheduledEventService(Router router) { - super(router); - } - - //TODO: stuff -} From 8c31acb145b859bd98e9d73afd67d036abbdabbc Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Tue, 16 Nov 2021 20:07:15 -0600 Subject: [PATCH 04/21] Finish rest implementation --- .../main/java/discord4j/rest/RestClient.java | 15 ++ .../java/discord4j/rest/entity/RestGuild.java | 56 ++++-- .../rest/entity/RestGuildScheduledEvent.java | 98 ---------- .../rest/entity/RestScheduledEvent.java | 179 ++++++++++++++++++ .../discord4j/rest/service/GuildService.java | 5 +- 5 files changed, 241 insertions(+), 112 deletions(-) delete mode 100644 rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java create mode 100644 rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java diff --git a/rest/src/main/java/discord4j/rest/RestClient.java b/rest/src/main/java/discord4j/rest/RestClient.java index 1fdcb40ec..ce25b028f 100644 --- a/rest/src/main/java/discord4j/rest/RestClient.java +++ b/rest/src/main/java/discord4j/rest/RestClient.java @@ -274,6 +274,21 @@ public RestRole restRole(Snowflake guildId, RoleData data) { return RestRole.create(this, guildId, Snowflake.of(data.id())); } + /** + * Requests to retrieve the scheduled event represented by the supplied ID. + * + * @param guildId The ID of the guild + * @param eventId The ID of the event + * @return A {@link RestScheduledEvent} as represented by the supplied IDs. + */ + public RestScheduledEvent getScheduledEventById(final Snowflake guildId, final Snowflake eventId) { + return RestScheduledEvent.create(this, guildId, eventId); + } + + public RestScheduledEvent restScheduledEvent(Snowflake guildId, GuildScheduledEventData data) { + return RestScheduledEvent.create(this, guildId, Snowflake.of(data.id())); + } + /** * Requests to retrieve the user represented by the supplied ID. * diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuild.java b/rest/src/main/java/discord4j/rest/entity/RestGuild.java index 14293fd7c..7ceedaa48 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestGuild.java +++ b/rest/src/main/java/discord4j/rest/entity/RestGuild.java @@ -122,14 +122,14 @@ public RestRole role(Snowflake roleId) { } /** - * Returns a {@link RestGuildScheduledEvent} representation under this guild. + * Returns a {@link RestScheduledEvent} representation under this guild. * This method does not perform any API request. * * @param eventId The entity ID * @return a {@code RestGuildScheduledEvent} with the given ID, under this guild */ - public RestGuildScheduledEvent scheduledEvent(Snowflake eventId) { - return RestGuildScheduledEvent.create(restClient, id, eventId.asLong()); + public RestScheduledEvent scheduledEvent(Snowflake eventId) { + return RestScheduledEvent.create(restClient, id, eventId.asLong()); } /** @@ -354,22 +354,56 @@ public Mono getScheduledEvent(Snowflake eventId) { /** * Requests to retrieve the scheduled events under this guild. * - * @return A {@link Flux} where, upon successful completion, emits 0...N {@link GuildScheduledEventData}. If an - * error is received, it is emitted through the {@code Flux}. + * @param withUserCount Whether to include number of users subscribed to each event + * @return A {@link Flux} that continually emits all the {@link GuildScheduledEventData} associated with this guild. + * If an error is received, it is emitted through the {@code Flux}. */ - public Flux getScheduledEvents(@Nullable Boolean withUserCounts) { + public Flux getScheduledEvents(@Nullable Boolean withUserCount) { Map queryParams = new HashMap<>(); - Optional.ofNullable(withUserCounts).ifPresent(value -> queryParams.put("with_user_count", value)); + Optional.ofNullable(withUserCount).ifPresent(value -> queryParams.put("with_user_count", value)); return restClient.getGuildService().getScheduledEvents(id, queryParams); } - //TODO: get RSVP'd users for an event + /** + * Create a new scheduled event for the guild. Requires the {@link Permission#MANAGE_EVENTS} permission. Returns + * the new event object on success. + * + * @param request the request body + * @return A {@link Mono} where, upon subscription, emits the created {@link GuildScheduledEventData} on success. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono createScheduledEvent(GuildScheduledEventCreateRequest request) { + return restClient.getGuildService().createScheduledEvent(id, request); + } - //TODO: create event + /** + * Requests to modify a scheduled event. Requires the {@link Permission#MANAGE_EVENTS} permission. + * Returns the modified event object on success. + * + * @param eventId The ID of the event + * @param request the request body + * @param reason an optional reason for the audit log + * @return A {@link Mono} where, upon subscription, emits the modified {@link GuildScheduledEventData} on success. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono modifyScheduledEvent(Snowflake eventId, + GuildScheduledEventModifyRequest request, + @Nullable String reason) { + return restClient.getGuildService().modifyScheduledEvent(id, eventId.asLong(), request, reason); + } - //TODO: modify event + /** + * Requests to delete a scheduled event. Requires the {@link Permission#MANAGE_EVENTS} permission. + * + * @param eventId The ID of the event + * @param reason an optional reason for the audit log + * @return A {@link Mono} where, upon successful completion, emits nothing; indicating the event has been deleted. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono deleteScheduledEvent(Snowflake eventId, @Nullable String reason) { + return restClient.getGuildService().deleteScheduledEvent(id, eventId.asLong(), reason); + } - //TODO: delete event @Override diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java deleted file mode 100644 index fe5a2860e..000000000 --- a/rest/src/main/java/discord4j/rest/entity/RestGuildScheduledEvent.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - -package discord4j.rest.entity; - -import discord4j.common.util.Snowflake; -import discord4j.discordjson.json.GuildScheduledEventData; -import discord4j.rest.RestClient; -import reactor.core.publisher.Mono; - -/** - * Represents a guild scheduled event within Discord. - */ -public class RestGuildScheduledEvent { - - private final RestClient restClient; - - private final long guildId; - - private final long id; - - private RestGuildScheduledEvent(RestClient restClient, long guildId, long id) { - this.restClient = restClient; - this.guildId = guildId; - this.id = id; - } - - /** - * Create a {@link RestGuildScheduledEvent} for the given parameters. This method does not perform any API request. - * - * @param restClient The client to make API requests. - * @param guildId The ID of the guild this entity belongs to. - * @param id the ID of this entity. - * @return A {@code RestGuildScheduledEvent} represented by the given parameters. - */ - public static RestGuildScheduledEvent create(RestClient restClient, Snowflake guildId, Snowflake id) { - return new RestGuildScheduledEvent(restClient, guildId.asLong(), id.asLong()); - } - - static RestGuildScheduledEvent create(RestClient restClient, long guildId, long id) { - return new RestGuildScheduledEvent(restClient, guildId, id); - } - - /** - * Returns the ID of the guild this scheduled event belongs to. - * - * @return The ID of the guild this scheduled event belongs to. - */ - public Snowflake getGuildId() { - return Snowflake.of(guildId); - } - - /** - * Returns the ID of this scheduled event. - * - * @return The ID of this scheduled event. - */ - public Snowflake getId() { - return Snowflake.of(id); - } - - /** - * Return the guild tied to thi role as a REST operations handle. - * - * @return The parent guild for this scheduled event. - */ - public RestGuild guild() { - return RestGuild.create(restClient, guildId); - } - - //TODO: Edit - - //TODO: Delete - - //TODO: get users - - /** - * Retrieve this scheduled event's data upon subscription. - * - * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData} belonging to - * this scheduled event. If an error is received, it is emitted through the {@code Mono}. - */ - //TODO: get data -} diff --git a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java new file mode 100644 index 000000000..db33a68c4 --- /dev/null +++ b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java @@ -0,0 +1,179 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.rest.entity; + +import discord4j.common.util.Snowflake; +import discord4j.discordjson.json.GuildScheduledEventData; +import discord4j.discordjson.json.GuildScheduledEventModifyRequest; +import discord4j.discordjson.json.GuildScheduledEventUserData; +import discord4j.rest.RestClient; +import discord4j.rest.util.PaginationUtil; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.annotation.Nullable; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Represents a guild scheduled event within Discord. + */ +public class RestScheduledEvent { + + private final RestClient restClient; + + private final long guildId; + + private final long id; + + private RestScheduledEvent(RestClient restClient, long guildId, long id) { + this.restClient = restClient; + this.guildId = guildId; + this.id = id; + } + + /** + * Create a {@link RestScheduledEvent} for the given parameters. This method does not perform any API request. + * + * @param restClient The client to make API requests. + * @param guildId The ID of the guild this entity belongs to. + * @param id the ID of this entity. + * @return A {@code RestGuildScheduledEvent} represented by the given parameters. + */ + public static RestScheduledEvent create(RestClient restClient, Snowflake guildId, Snowflake id) { + return new RestScheduledEvent(restClient, guildId.asLong(), id.asLong()); + } + + static RestScheduledEvent create(RestClient restClient, long guildId, long id) { + return new RestScheduledEvent(restClient, guildId, id); + } + + /** + * Returns the ID of the guild this scheduled event belongs to. + * + * @return The ID of the guild this scheduled event belongs to. + */ + public Snowflake getGuildId() { + return Snowflake.of(guildId); + } + + /** + * Returns the ID of this scheduled event. + * + * @return The ID of this scheduled event. + */ + public Snowflake getId() { + return Snowflake.of(id); + } + + /** + * Return the guild tied to thi role as a REST operations handle. + * + * @return The parent guild for this scheduled event. + */ + public RestGuild guild() { + return RestGuild.create(restClient, guildId); + } + + /** + * Requests to edit this event. + * + * @param request A {@link GuildScheduledEventModifyRequest} to parameterize this request. + * @param reason The reason, if present + * @return A {@link Mono} where, upon successful completion, emits the edited {@link GuildScheduledEventData}. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono edit(final GuildScheduledEventModifyRequest request, @Nullable String reason) { + return restClient.getGuildService().modifyScheduledEvent(guildId,id, request, reason); + } + + /** + * Requests to delete this event while optionally specifying the reason. + * + * @param reason The reason, if present. + * @return A {@link Mono} where, upon successful completion, emits nothing; indicating the event was deleted. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono delete(@Nullable final String reason) { + return restClient.getGuildService().deleteScheduledEvent(guildId, id, reason); + } + + /** + * Request to retrieve all interested users before the specified ID. + *

+ * The returned {@code Flux} will emit items in reverse-chronological order (newest to oldest). It is + * recommended to limit the emitted items by invoking either {@link Flux#takeWhile(Predicate)} (to retrieve IDs + * within a specified range) or {@link Flux#take(long)} (to retrieve a specific amount of IDs). + *

+ * The following example will get all users from {@code userId} to {@code myOtherUserId}: + * {@code getInterestedUsersBefore(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) >= 0)} + * + * @param userId The ID of the newest user to retrieve. + * @param withMember Whether to optionally include the member object in the returned data. + * @return A {@link Flux} that continually emits all {@link GuildScheduledEventUserData users} before + * the specified ID. If an error is received, it is emitted through the {@code Flux}. + * @see + * + * Get Guild Scheduled Event Users + */ + public Flux getInterestedUsersBefore(Snowflake userId, @Nullable Boolean withMember) { + Function, Flux> doRequest = params -> { + Optional.ofNullable(withMember).ifPresent(value -> params.put("with_member", value)); + return restClient.getGuildService().getScheduledEventUsers(guildId, id, params); + }; + return PaginationUtil.paginateBefore(doRequest, data -> Snowflake.asLong(data.user().id()), userId.asLong(), 100); + } + + /** + * Request to retrieve all interested users after the specified ID. + *

+ * The returned {@code Flux} will emit items in chronological order (older to newest). It is recommended to limit + * the emitted items by invoking either {@link Flux#takeWhile(Predicate)} (to retrieve IDs within a specified range) + * or {@link Flux#take(long)} (to retrieve a specific amount of IDs). + *

+ * The following example will get all users from {@code userId} to {@code myOtherUserId}: + * {@code getInterestedUsersAfter(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) <= 0)} + * + * @param userId The ID of the oldest user to retrieve. + * @param withMember Whether to optionally include the member object in the returned data. + * @return A {@link Flux} that continually emits all {@link GuildScheduledEventUserData users} after + * the specified ID. If an error is received, it is emitted through the {@code Flux}. + * @see + * + * Get Guild Scheduled Event Users + */ + public Flux getInterestedUsersAfter(Snowflake userId, @Nullable Boolean withMember) { + Function, Flux> doRequest = params -> { + Optional.ofNullable(withMember).ifPresent(value -> params.put("with_member", value)); + return restClient.getGuildService().getScheduledEventUsers(guildId, id, params); + }; + return PaginationUtil.paginateAfter(doRequest, data -> Snowflake.asLong(data.user().id()), userId.asLong(), 100); + } + + /** + * Retrieve this scheduled event's data upon subscription. + * + * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData} belonging to + * this scheduled event. If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getData() { + return restClient.getGuildService().getScheduledEvent(guildId, id); + } +} diff --git a/rest/src/main/java/discord4j/rest/service/GuildService.java b/rest/src/main/java/discord4j/rest/service/GuildService.java index 70adda0e8..87ffeeb71 100644 --- a/rest/src/main/java/discord4j/rest/service/GuildService.java +++ b/rest/src/main/java/discord4j/rest/service/GuildService.java @@ -368,12 +368,11 @@ public Mono deleteScheduledEvent(long guildId, long eventId, @Nullable Str .bodyToMono(Void.class); } - //TODO: Discord might be changing the data structure of the response, awaiting confirmation. - public Flux getScheduledEventUsers(long guildId, long eventId, Map queryParams) { + public Flux getScheduledEventUsers(long guildId, long eventId, Map queryParams) { return Routes.GUILD_SCHEDULED_EVENT_USERS_GET.newRequest(guildId, eventId) .query(queryParams) .exchange(getRouter()) - .bodyToMono(UserData[].class) + .bodyToMono(GuildScheduledEventUserData[].class) .flatMapMany(Flux::fromArray); } } From 6f43ea51264cc78858905491e1ca80f83c531f4a Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Thu, 18 Nov 2021 13:30:13 -0600 Subject: [PATCH 05/21] Update methods where object models changed due to api changes --- rest/src/main/java/discord4j/rest/entity/RestGuild.java | 9 ++++++--- .../java/discord4j/rest/entity/RestScheduledEvent.java | 8 ++++++-- .../main/java/discord4j/rest/service/GuildService.java | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuild.java b/rest/src/main/java/discord4j/rest/entity/RestGuild.java index 7ceedaa48..24924c4e9 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestGuild.java +++ b/rest/src/main/java/discord4j/rest/entity/RestGuild.java @@ -344,17 +344,20 @@ public Flux getTemplates() { * Requests to retrieve the scheduled event under this guild. * * @param eventId The ID of the event + * @param withUserCount Whether to optionally include the number of "interested" users * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData}. If an * error is received, it is emitted through the {@code Mono}. */ - public Mono getScheduledEvent(Snowflake eventId) { - return restClient.getGuildService().getScheduledEvent(id, eventId.asLong()); + public Mono getScheduledEvent(Snowflake eventId, @Nullable Boolean withUserCount) { + Map queryParams = new HashMap<>(); + Optional.ofNullable(withUserCount).ifPresent(value -> queryParams.put("with_user_count", value)); + return restClient.getGuildService().getScheduledEvent(id, eventId.asLong(), queryParams); } /** * Requests to retrieve the scheduled events under this guild. * - * @param withUserCount Whether to include number of users subscribed to each event + * @param withUserCount Whether to optionally include the number of "interested" users for each event * @return A {@link Flux} that continually emits all the {@link GuildScheduledEventData} associated with this guild. * If an error is received, it is emitted through the {@code Flux}. */ diff --git a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java index db33a68c4..eb782e5dd 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java +++ b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java @@ -27,6 +27,7 @@ import reactor.core.publisher.Mono; import reactor.util.annotation.Nullable; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -170,10 +171,13 @@ public Flux getInterestedUsersAfter(Snowflake userI /** * Retrieve this scheduled event's data upon subscription. * + * @param withUserCount Whether to optionally include the "interested" user count in the returned data. * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData} belonging to * this scheduled event. If an error is received, it is emitted through the {@code Mono}. */ - public Mono getData() { - return restClient.getGuildService().getScheduledEvent(guildId, id); + public Mono getData(@Nullable Boolean withUserCount) { + Map queryParams = new HashMap<>(); + Optional.ofNullable(withUserCount).ifPresent(value -> queryParams.put("with_user_count", value)); + return restClient.getGuildService().getScheduledEvent(guildId, id, queryParams); } } diff --git a/rest/src/main/java/discord4j/rest/service/GuildService.java b/rest/src/main/java/discord4j/rest/service/GuildService.java index 87ffeeb71..05cf868b5 100644 --- a/rest/src/main/java/discord4j/rest/service/GuildService.java +++ b/rest/src/main/java/discord4j/rest/service/GuildService.java @@ -332,8 +332,9 @@ public Mono modifyOthersVoiceState(long guildId, long userId, UpdateUserVo .bodyToMono(Void.class); } - public Mono getScheduledEvent(long guildId, long eventId) { + public Mono getScheduledEvent(long guildId, long eventId, Map queryParams) { return Routes.GUILD_SCHEDULED_EVENT_GET.newRequest(guildId, eventId) + .query(queryParams) .exchange(getRouter()) .bodyToMono(GuildScheduledEventData.class); } From fe586021f6dbc15a618fcfe06928787bf4af6ff4 Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Thu, 2 Dec 2021 12:35:51 -0600 Subject: [PATCH 06/21] [WIP] Adding stores actions for scheduled events --- .../java/discord4j/common/store/Store.java | 8 + .../store/action/gateway/GatewayActions.java | 60 ++++ .../GuildScheduledEventCreateAction.java | 34 ++ .../GuildScheduledEventDeleteAction.java | 34 ++ .../GuildScheduledEventUpdateAction.java | 34 ++ .../GuildScheduledEventUserAddAction.java | 34 ++ .../GuildScheduledEventUserRemoveAction.java | 34 ++ .../GetGuildScheduledEventByIdAction.java | 41 +++ .../GetGuildScheduledEventUsersAction.java | 27 ++ ...GuildScheduledEventUsersInEventAction.java | 40 +++ .../read/GetGuildScheduledEventsAction.java | 28 ++ .../GetGuildScheduledEventsInGuildAction.java | 34 ++ .../common/store/action/read/ReadActions.java | 50 +++ .../common/store/api/layout/DataAccessor.java | 40 +++ .../discord4j/core/GatewayDiscordClient.java | 6 + .../object/ScheduledEventEntityMetadata.java | 74 +++++ .../core/object/ScheduledEventUser.java | 92 ++++++ .../discord4j/core/object/entity/Guild.java | 8 + .../core/object/entity/ScheduledEvent.java | 304 ++++++++++++++++++ .../core/retriever/EntityRetriever.java | 29 ++ .../core/retriever/RestEntityRetriever.java | 34 ++ .../core/retriever/StoreEntityRetriever.java | 19 ++ .../gateway/state/DispatchStoreLayer.java | 5 + .../rest/entity/RestScheduledEvent.java | 4 +- 24 files changed, 1071 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserAddAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserRemoveAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventByIdAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java create mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsInGuildAction.java create mode 100644 core/src/main/java/discord4j/core/object/ScheduledEventEntityMetadata.java create mode 100644 core/src/main/java/discord4j/core/object/ScheduledEventUser.java create mode 100644 core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java diff --git a/common/src/main/java/discord4j/common/store/Store.java b/common/src/main/java/discord4j/common/store/Store.java index fb45edd94..50b039366 100644 --- a/common/src/main/java/discord4j/common/store/Store.java +++ b/common/src/main/java/discord4j/common/store/Store.java @@ -174,6 +174,14 @@ private static ActionMapper dataAccessorToMapper(DataAccessor dataAccessor) { .getVoiceStatesInGuild(action.getGuildId())) .map(GetVoiceStateByIdAction.class, action -> dataAccessor .getVoiceStateById(action.getGuildId(), action.getUserId())) + .map(GetGuildScheduledEventsAction.class, action -> dataAccessor.getScheduledEvents()) + .map(GetGuildScheduledEventsInGuildAction.class, action -> dataAccessor + .getScheduledEventsInGuild(action.getGuildId())) + .map(GetGuildScheduledEventByIdAction.class, action -> dataAccessor + .getScheduledEventById(action.getGuildId(), action.getEventId())) + .map(GetGuildScheduledEventUsersAction.class, action -> dataAccessor.getScheduledEventUsers()) + .map(GetGuildScheduledEventUsersInEventAction.class, action -> dataAccessor + .getScheduledEventUsersInEvent(action.getGuildId(), action.getEventId())) .build(); } diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java b/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java index 43f6c1e9a..cba1b9e9c 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java @@ -350,4 +350,64 @@ public static VoiceStateUpdateDispatchAction voiceStateUpdateDispatch(int shardI public static CompleteGuildMembersAction completeGuildMembers(long guildId) { return new CompleteGuildMembersAction(guildId); } + + /** + * Creates an action to execute when a {@link GuildScheduledEventCreate} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventCreateAction} + */ + public static GuildScheduledEventCreateAction guildScheduledEventCreate(int shardIndex, + GuildScheduledEventCreate dispatch) { + return new GuildScheduledEventCreateAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventUpdate} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUpdateAction} + */ + public static GuildScheduledEventUpdateAction guildScheduledEventUpdate(int shardIndex, + GuildScheduledEventUpdate dispatch) { + return new GuildScheduledEventUpdateAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventDelete} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventDeleteAction} + */ + public static GuildScheduledEventDeleteAction guildScheduledEventDelete(int shardIndex, + GuildScheduledEventDelete dispatch) { + return new GuildScheduledEventDeleteAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventUserAdd} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUserAddAction} + */ + public static GuildScheduledEventUserAddAction guildScheduledEventUserAdd(int shardIndex, + GuildScheduledEventUserAdd dispatch) { + return new GuildScheduledEventUserAddAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventUserRemove} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUserRemoveAction} + */ + public static GuildScheduledEventUserRemoveAction guildScheduledEventUserRemove(int shardIndex, + GuildScheduledEventUserRemove dispatch) { + return new GuildScheduledEventUserRemoveAction(shardIndex, dispatch); + } } diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java new file mode 100644 index 000000000..7336f819a --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.gateway; + +import discord4j.discordjson.json.gateway.GuildScheduledEventCreate; + +public class GuildScheduledEventCreateAction extends ShardAwareAction { + + private final GuildScheduledEventCreate eventCreate; + + public GuildScheduledEventCreateAction(int shardIndex, GuildScheduledEventCreate eventCreate) { + super(shardIndex); + this.eventCreate = eventCreate; + } + + public GuildScheduledEventCreate getEventCreate() { + return eventCreate; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java new file mode 100644 index 000000000..c9bb78fbc --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.gateway; + +import discord4j.discordjson.json.gateway.GuildScheduledEventDelete; + +public class GuildScheduledEventDeleteAction extends ShardAwareAction { + + private final GuildScheduledEventDelete eventDelete; + + public GuildScheduledEventDeleteAction(int shardIndex, GuildScheduledEventDelete eventDelete) { + super(shardIndex); + this.eventDelete = eventDelete; + } + + public GuildScheduledEventDelete getEventDelete() { + return eventDelete; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java new file mode 100644 index 000000000..274e93795 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.gateway; + +import discord4j.discordjson.json.gateway.GuildScheduledEventUpdate; + +public class GuildScheduledEventUpdateAction extends ShardAwareAction { + + private final GuildScheduledEventUpdate eventUpdate; + + public GuildScheduledEventUpdateAction(int shardIndex, GuildScheduledEventUpdate eventUpdate) { + super(shardIndex); + this.eventUpdate = eventUpdate; + } + + public GuildScheduledEventUpdate getEventUpdate() { + return eventUpdate; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserAddAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserAddAction.java new file mode 100644 index 000000000..9822d9ff3 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserAddAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.gateway; + +import discord4j.discordjson.json.gateway.GuildScheduledEventUserAdd; + +public class GuildScheduledEventUserAddAction extends ShardAwareAction { + + private final GuildScheduledEventUserAdd userAdd; + + public GuildScheduledEventUserAddAction(int shardIndex, GuildScheduledEventUserAdd userAdd) { + super(shardIndex); + this.userAdd = userAdd; + } + + public GuildScheduledEventUserAdd getUserAdd() { + return userAdd; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserRemoveAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserRemoveAction.java new file mode 100644 index 000000000..3cfacdf3a --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUserRemoveAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.gateway; + +import discord4j.discordjson.json.gateway.GuildScheduledEventUserRemove; + +public class GuildScheduledEventUserRemoveAction extends ShardAwareAction{ + + private final GuildScheduledEventUserRemove userRemove; + + public GuildScheduledEventUserRemoveAction(int shardIndex, GuildScheduledEventUserRemove userRemove) { + super(shardIndex); + this.userRemove = userRemove; + } + + public GuildScheduledEventUserRemove getUserRemove() { + return userRemove; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventByIdAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventByIdAction.java new file mode 100644 index 000000000..d4cf377d0 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventByIdAction.java @@ -0,0 +1,41 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.read; + +import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.json.GuildScheduledEventData; + +public class GetGuildScheduledEventByIdAction implements StoreAction { + + private final long guildId; + + private final long eventId; + + GetGuildScheduledEventByIdAction(long guildId, long eventId) { + this.guildId = guildId; + this.eventId = eventId; + } + + public long getGuildId() { + return guildId; + } + + public long getEventId() { + return eventId; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersAction.java new file mode 100644 index 000000000..9f3e07785 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersAction.java @@ -0,0 +1,27 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.read; + +import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.json.GuildScheduledEventUserData; + +public class GetGuildScheduledEventUsersAction implements StoreAction { + + GetGuildScheduledEventUsersAction() { + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java new file mode 100644 index 000000000..0ef0652a3 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java @@ -0,0 +1,40 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.read; + +import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.json.GuildScheduledEventUserData; + +public class GetGuildScheduledEventUsersInEventAction implements StoreAction { + + private final long guildId; + private final long eventId; + + public GetGuildScheduledEventUsersInEventAction(long guildId, long eventId) { + this.guildId = guildId; + this.eventId = eventId; + } + + public long getGuildId() { + return guildId; + } + + public long getEventId() { + return eventId; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java new file mode 100644 index 000000000..99a3c10d9 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java @@ -0,0 +1,28 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.read; + +import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.json.GuildScheduledEventData; + +public class GetGuildScheduledEventsAction implements StoreAction { + + GetGuildScheduledEventsAction() { + + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsInGuildAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsInGuildAction.java new file mode 100644 index 000000000..8dfdcdb24 --- /dev/null +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsInGuildAction.java @@ -0,0 +1,34 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.common.store.action.read; + +import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.json.GuildScheduledEventData; + +public class GetGuildScheduledEventsInGuildAction implements StoreAction { + + private final long guildId; + + GetGuildScheduledEventsInGuildAction(long guildId) { + this.guildId = guildId; + } + + public long getGuildId() { + return guildId; + } +} diff --git a/common/src/main/java/discord4j/common/store/action/read/ReadActions.java b/common/src/main/java/discord4j/common/store/action/read/ReadActions.java index 92fa9b3bc..ec7eef0d4 100644 --- a/common/src/main/java/discord4j/common/store/action/read/ReadActions.java +++ b/common/src/main/java/discord4j/common/store/action/read/ReadActions.java @@ -516,4 +516,54 @@ public static GetVoiceStatesInGuildAction getVoiceStatesInGuild(long guildId) { public static GetVoiceStateByIdAction getVoiceStateById(long guildId, long userId) { return new GetVoiceStateByIdAction(guildId, userId); } + + /** + * Creates an action to retrieve data for all scheduled events in a store. + * + * @return a new {@link GetGuildScheduledEventsAction} + */ + public static GetGuildScheduledEventsAction getEvents() { + return new GetGuildScheduledEventsAction(); + } + + /** + * Creates an action to retrieve data for all scheduled events in a store for the given guild ID. + * + * @param guildId the guild ID + * @return a new {@link GetGuildScheduledEventsAction} + */ + public static GetGuildScheduledEventsInGuildAction getEvents(long guildId) { + return new GetGuildScheduledEventsInGuildAction(guildId); + } + + /** + * Creates an action to retrieve data for the scheduled event corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return a new {@link GetGuildScheduledEventByIdAction} + */ + public static GetGuildScheduledEventByIdAction getEvent(long guildId, long eventId) { + return new GetGuildScheduledEventByIdAction(guildId, eventId); + } + + /** + * Creates an action to retrieve all data for scheduled event users in a store. + * + * @return a new {@link GetGuildScheduledEventUsersAction} + */ + public static GetGuildScheduledEventUsersAction getEventUsers() { + return new GetGuildScheduledEventUsersAction(); + } + + /** + * Creates an action to retrieve data for the scheduled event users corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return a new {@link GetGuildScheduledEventUsersAction} + */ + public static GetGuildScheduledEventUsersInEventAction getEventUsers(long guildId, long eventId) { + return new GetGuildScheduledEventUsersInEventAction(guildId, eventId); + } } diff --git a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java index 5094e4d13..bbabb4c80 100644 --- a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java +++ b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java @@ -433,4 +433,44 @@ default Mono getStickerById(long guildId, long stickerId) { * @return A {@link Mono} emitting the voice state, or empty if not found */ Mono getVoiceStateById(long guildId, long userId); + + /** + * Retrieves data for all guild scheduled events in the store. + * + * @return A {@link Flux} emitting the scheduled events, or empty if none is present + */ + Flux getScheduledEvents(); + + /** + * Retrieves data for all guild scheduled events corresponding to the given guild ID. + * + * @param guildId the guild ID + * @return A {@link Flux} emitting the scheduled events, or empty if none is present + */ + Flux getScheduledEventsInGuild(long guildId); + + /** + * Retrieves data for the guild scheduled event corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return A {@link Mono} emitting the scheduled event, or empty if not found + */ + Mono getScheduledEventById(long guildId, long eventId); + + /** + * Retrieves data for all guild scheduled event users in the store + * + * @return A {@link Flux} emitting the scheduled event users, or empty if none is present + */ + Flux getScheduledEventUsers(); + + /** + * Retrieves data for all guild scheduled event users corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return A {@link Flux} emitting the scheduled event users, or empty if none is present + */ + Flux getScheduledEventUsersInEvent(long guildId, long eventId); } diff --git a/core/src/main/java/discord4j/core/GatewayDiscordClient.java b/core/src/main/java/discord4j/core/GatewayDiscordClient.java index cdbf3ad8f..8ab091b99 100644 --- a/core/src/main/java/discord4j/core/GatewayDiscordClient.java +++ b/core/src/main/java/discord4j/core/GatewayDiscordClient.java @@ -788,6 +788,12 @@ public Flux getGuildEmojis(Snowflake guildId) { return entityRetriever.getGuildEmojis(guildId); } + //TODO: get guild scheduled events + //TODO: get guild scheduled events with retrieval strategy + + //TODO: get guild scheduled event by ID + //TODO: get guild scheduled event by ID with retrieval strategy + @Override public Flux getGuildStickers(Snowflake guildId) { return entityRetriever.getGuildStickers(guildId); diff --git a/core/src/main/java/discord4j/core/object/ScheduledEventEntityMetadata.java b/core/src/main/java/discord4j/core/object/ScheduledEventEntityMetadata.java new file mode 100644 index 000000000..daf34add7 --- /dev/null +++ b/core/src/main/java/discord4j/core/object/ScheduledEventEntityMetadata.java @@ -0,0 +1,74 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.object; + +import discord4j.core.GatewayDiscordClient; +import discord4j.discordjson.json.GuildScheduledEventEntityMetadataData; + +import java.util.Objects; +import java.util.Optional; + +public class ScheduledEventEntityMetadata implements DiscordObject { + + /** The gateway associated with this object. */ + private final GatewayDiscordClient gateway; + + /** The raw data as represented by Discord. */ + private final GuildScheduledEventEntityMetadataData data; + + /** + * Constructs a {@code ScheduledEventEntityMetadata} with an associated {@link GatewayDiscordClient} and Discord data. + * + * @param gateway The {@link GatewayDiscordClient} associated with this object, must be non-null. + * @param data The raw data as represented by Discord, must be non-null. + */ + public ScheduledEventEntityMetadata(GatewayDiscordClient gateway, GuildScheduledEventEntityMetadataData data) { + this.gateway = Objects.requireNonNull(gateway); + this.data = Objects.requireNonNull(data); + } + + @Override + public GatewayDiscordClient getClient() { + return gateway; + } + + /** + * Gets the data of the entity metadata. + * + * @return The data of the entity metadata. + */ + public GuildScheduledEventEntityMetadataData getData() { + return data; + } + + /** + * Gets the location of the entity metadata, if present. + * + * @return The location of the entity metadata, or {@code Optional#empty()} if not present. + */ + public Optional getLocation() { + return data.location().toOptional(); + } + + @Override + public String toString() { + return "ScheduledEventEntityMetadata{"+ + "data=" + data + + '}'; + } +} diff --git a/core/src/main/java/discord4j/core/object/ScheduledEventUser.java b/core/src/main/java/discord4j/core/object/ScheduledEventUser.java new file mode 100644 index 000000000..41a36c2ca --- /dev/null +++ b/core/src/main/java/discord4j/core/object/ScheduledEventUser.java @@ -0,0 +1,92 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.object; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.PartialMember; +import discord4j.core.object.entity.User; +import discord4j.discordjson.json.GuildScheduledEventUserData; + +import java.util.Objects; +import java.util.Optional; + +public class ScheduledEventUser implements DiscordObject { + + /** The gateway associated with this object. */ + private final GatewayDiscordClient gateway; + + /** The raw data as represented by Discord. */ + private final GuildScheduledEventUserData data; + + /** The ID of the guild the event belongs to. */ + private final Snowflake guildId; + + /** + * Constructs a {@code ScheduledEventUser} with an associated {@link GatewayDiscordClient} and Discord data. + * + * @param gateway The {@link GatewayDiscordClient} associated with this object, must be non-null. + * @param data The raw data as represented by Discord, must be non-null. + * @param guildId The ID of the guild the event belongs to, must be non-null. + */ + public ScheduledEventUser(GatewayDiscordClient gateway, GuildScheduledEventUserData data, Snowflake guildId) { + this.gateway = Objects.requireNonNull(gateway); + this.data = Objects.requireNonNull(data); + this.guildId = Objects.requireNonNull(guildId); + } + + @Override + public GatewayDiscordClient getClient() { + return gateway; + } + + /** + * Gets the ID of the event the user is interested in. + * + * @return The ID of the event the user is interested in. + */ + public Snowflake getEventId() { + return Snowflake.of(data.guildScheduledEventId()); + } + + /** + * Gets the {@link User} interested in the event. + * + * @return The {@code User} interested in the event. + */ + public User getUser() { + return new User(gateway, data.user()); + } + + /** + * Gets the {@link PartialMember}, if the user is a member of the guild the event is belongs to. + * + * @return The {@code PartialMember}, if the user is a member of the guild the event is belongs to. + */ + public Optional getMember() { + return data.member().toOptional() + .map(memberData -> new PartialMember(gateway, data.user(), memberData, guildId.asLong())); + } + + @Override + public String toString() { + return "ScheduledEventUser{"+ + "data=" + data + + '}'; + } +} diff --git a/core/src/main/java/discord4j/core/object/entity/Guild.java b/core/src/main/java/discord4j/core/object/entity/Guild.java index 6e6f55260..b2b9d9a03 100644 --- a/core/src/main/java/discord4j/core/object/entity/Guild.java +++ b/core/src/main/java/discord4j/core/object/entity/Guild.java @@ -1905,6 +1905,14 @@ public Mono getAutoModRule(Snowflake autoModRuleId) { .map(data -> new AutoModRule(gateway, data)); } + //TODO: get scheduled events + //TODO: get scheduled events with retrieval strat + + //TODO: get scheduled event + //TODO: get scheduled event with retrieval strat + + //TODO: create scheduled event + @Override public boolean equals(@Nullable final Object obj) { return EntityUtil.equals(this, obj); diff --git a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java new file mode 100644 index 000000000..a0990b9c2 --- /dev/null +++ b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java @@ -0,0 +1,304 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.object.entity; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.ScheduledEventEntityMetadata; +import discord4j.core.object.entity.channel.GuildChannel; +import discord4j.discordjson.json.GuildScheduledEventData; +import reactor.core.publisher.Mono; + +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; + +/** + * A Discord Guild Scheduled Event + * + * @see Guild Scheduled Event Resource + */ +public class ScheduledEvent implements Entity { + + /** The gateway associated to this object */ + private final GatewayDiscordClient gateway; + + /** The raw data as represented by Discord. */ + private final GuildScheduledEventData data; + + /** + * Constructs a {@code ScheduledEvent} with an associated {@link GatewayDiscordClient} and Discord data. + * @param gateway The {@link GatewayDiscordClient} associated to this object, must be non-null. + * @param data The raw data as represented by Discord, must be non-null. + */ + public ScheduledEvent(final GatewayDiscordClient gateway, final GuildScheduledEventData data) { + this.gateway = Objects.requireNonNull(gateway); + this.data = Objects.requireNonNull(data); + } + + @Override + public GatewayDiscordClient getClient() { + return gateway; + } + + @Override + public Snowflake getId() { + return Snowflake.of(data.id()); + } + + /** + * Gets the data of the scheduled event. + * + * @return The data of the scheduled event. + */ + public GuildScheduledEventData getData() { + return data; + } + + /** + * Gets the guild ID of the scheduled event. + * + * @return The guild ID of the scheduled event. + */ + public Snowflake getGuildId() { + return Snowflake.of(data.guildId()); + } + + /** + * Gets the ID of the creator of the event. + * + * @return The ID of the creator of the event. + */ + public Optional getCreatorId() { + return data.creatorId().map(Snowflake::of); + } + + /** + * Requests to retrieve the creator if this event, if present. + * + * @return A {@link Mono} where, if the creator is present, emits the {@link Member creator} of the event, + * otherwise emits an {@link Mono#empty() empty mono}. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getCreator() { + return Mono.justOrEmpty(getCreatorId()).flatMap(id -> gateway.getMemberById(getGuildId(), id)); + } + + /** + * Gets the ID of the channel where the event will be hosted, if present. + * + * @return The ID of the channel where the event will be hosted, if present. + */ + public Optional getChannelId() { + return data.channelId().map(Snowflake::of); + } + + /** + * Requests to retrieve the channel this event will be hosted in, if present. + *

+ * Note: This channel could be a stage or voice channel, see {@link #getEntityType()} to determine the type safely. + * + * @return A {@link Mono} where, if the channel is present, emits the {@link GuildChannel channel} this event will + * be hosted in, otherwise emits an {@link Mono#empty() empty mono}. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getChannel() { + return Mono.justOrEmpty(getChannelId()) + .flatMap(gateway::getChannelById) + .ofType(GuildChannel.class); + } + + /** + * Gets the scheduled start time of the event. + * + * @return The scheduled start time of the event. + */ + public Instant getStartTime() { + return data.scheduledStartTime(); + } + + /** + * Gets the scheduled end time of the event, if present. + *

+ * Note: Note: This metadata will always be present when the entity type is {@link EntityType#EXTERNAL external}. + * @return The scheduled end time of the event, if present. + */ + public Optional getEndTime() { + return data.scheduledEndTime(); + } + + /** Gets the privacy level of the event + * + * @return The privacy level of the event + */ + public PrivacyLevel getPrivacyLevel() { + return PrivacyLevel.of(data.privacyLevel()); + } + + /** + * Gets the status of the event. + * + * @return The status of the event. + */ + public Status getStatus() { + return Status.of(data.status()); + } + + /** + * Gets the entity type of the event. + * + * @return The entity type of the event. + */ + public EntityType getEntityType() { + return EntityType.of(data.entityType()); + } + + /** + * Gets the ID of the entity this event will be hosted in, if present. + *

+ * Note: This property currently matches {@link #getChannelId()}, it is believed this is available for future + * flexibility for Discord and should not be relied on. + * + * @return The ID of the entity this event will be hosted in, if present. + */ + public Optional getEntityId() { + return getData().entityId().map(Snowflake::of); + } + + /** + * Gets the entity metadata of the event, if present. + *

+ * Note: This metadata will always be present when the entity type is {@link EntityType#EXTERNAL external}. + * @return The entity metadata of the event, if present. + */ + public Optional getEntityMetadata() { + return data.entityMetadata().map(data -> new ScheduledEventEntityMetadata(gateway,data)); + } + + /** + * Gets the location of the event, if present. + *

+ * Note: This location is pulled from {@link #getEntityMetadata().getLocation()} if present. + * @return The location of the event, if present. + */ + public Optional getLocation() { + return getEntityMetadata().flatMap(ScheduledEventEntityMetadata::getLocation); + } + + /** + * Gets the count of users who have said they are "interested" in the event, if present. + * + * @return The count of users who have said they are "interested" in the event, if present. + */ + public Optional getInterestedUserCount() { + return data.userCount().toOptional(); + } + + //TODO: get users + + //TODO: edit + + //TODO: delete + + /** + * Represents a scheduled event's privacy level. + */ + public enum PrivacyLevel { + UNKNOWN(-1), + GUILD_ONLY(2); + + private final int value; + + PrivacyLevel(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static PrivacyLevel of(int value) { + switch (value) { + case 2: return GUILD_ONLY; + default: return UNKNOWN; + } + } + } + + /** + * Represents a scheduled event's entity type + */ + public enum EntityType { + UNKNOWN(-1), + STAGE_INSTANCE(1), + VOICE(2), + EXTERNAL(3); + + private final int value; + + EntityType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static EntityType of(int value) { + switch (value) { + case 1: return STAGE_INSTANCE; + case 2: return VOICE; + case 3: return EXTERNAL; + default: return UNKNOWN; + } + } + } + + /** + * Represents a scheduled event's status + */ + public enum Status { + UNKNOWN(-1), + SCHEDULED(1), + ACTIVE(2), + COMPLETED(3), + CANCELED(4); + + private final int value; + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Status of(int value) { + switch (value) { + case 1: return SCHEDULED; + case 2: return ACTIVE; + case 3: return COMPLETED; + case 4: return CANCELED; + default: return UNKNOWN; + } + } + } + + +} diff --git a/core/src/main/java/discord4j/core/retriever/EntityRetriever.java b/core/src/main/java/discord4j/core/retriever/EntityRetriever.java index 4e4ce1fe4..59710da3b 100644 --- a/core/src/main/java/discord4j/core/retriever/EntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/EntityRetriever.java @@ -16,6 +16,7 @@ */ package discord4j.core.retriever; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.automod.AutoModRule; import discord4j.core.object.entity.Guild; import discord4j.core.object.entity.GuildEmoji; @@ -23,6 +24,7 @@ import discord4j.core.object.entity.Member; import discord4j.core.object.entity.Message; import discord4j.core.object.entity.Role; +import discord4j.core.object.entity.ScheduledEvent; import discord4j.core.object.entity.User; import discord4j.core.object.entity.channel.Channel; import discord4j.core.object.entity.channel.GuildChannel; @@ -193,4 +195,31 @@ public interface EntityRetriever { * it is emitted through the {@code Flux}. */ Flux getGuildAutoModRules(Snowflake guildId); + + /** + * Requests to retrieve the {@link ScheduledEvent} represented by the supplied IDs. + * + * @param guildId The ID of the guild. + * @param eventId The ID of the scheduled event. + * @return A {@link Mono} where, upon successful completion, emits the {@link ScheduledEvent} as represented by the + * supplied IDs. If an error is received, it is emitted through the {@code Mono}. + */ + Mono getScheduledEventById(Snowflake guildId, Snowflake eventId); + + /** + * Requests to retrieve the guild's scheduled events. + * + * @return A {@link Flux} that continually emits the guild's {@link ScheduledEvent events}. If an error is received, + * it is emitted through the {@code Flux}. + */ + Flux getScheduledEvents(Snowflake guildId); + + /** + * Requests to retrieve the users that have RSVPed as "interested" to the event represented by the supplied IDs. + * + * @return A {@link Flux} that continually emits the event's {@link ScheduledEventUser users}. + * If an error is received, it is emitted through the {@code Flux}. + */ + Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId); + } diff --git a/core/src/main/java/discord4j/core/retriever/RestEntityRetriever.java b/core/src/main/java/discord4j/core/retriever/RestEntityRetriever.java index 934e046d3..9f4d9d18a 100644 --- a/core/src/main/java/discord4j/core/retriever/RestEntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/RestEntityRetriever.java @@ -17,6 +17,7 @@ package discord4j.core.retriever; import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.automod.AutoModRule; import discord4j.core.object.entity.*; import discord4j.core.object.entity.channel.Channel; @@ -30,6 +31,7 @@ import reactor.core.publisher.Mono; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -170,6 +172,38 @@ public Flux getGuildAutoModRules(Snowflake guildId) { .map(data -> new AutoModRule(gateway, data)); } + @Override + public Mono getScheduledEventById(Snowflake guildId, Snowflake eventId) { + Map queryParams = new HashMap<>(); + queryParams.put("with_user_count", true); + + return rest.getGuildService() + .getScheduledEvent(guildId.asLong(), eventId.asLong(), queryParams) + .map(data -> new ScheduledEvent(gateway, data)); + } + + @Override + public Flux getScheduledEvents(Snowflake guildId) { + Map queryParams = new HashMap<>(); + queryParams.put("with_user_count", true); + + return rest.getGuildService() + .getScheduledEvents(guildId.asLong(), queryParams) + .map(data -> new ScheduledEvent(gateway, data)); + } + + @Override + public Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId) { + Function, Flux> doRequest = params -> { + params.put("with_member", true); + return rest.getGuildService().getScheduledEventUsers(guildId.asLong(), eventId.asLong(), params); + }; + + return PaginationUtil.paginateAfter(doRequest, data -> Snowflake.asLong(data.user().id()), 0, 100) + .map(data -> new ScheduledEventUser(gateway, data, guildId)); + + } + private GuildData toGuildData(GuildUpdateData guild) { return GuildData.builder() .from(guild) diff --git a/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java b/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java index 818420426..e22b9471f 100644 --- a/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java @@ -21,6 +21,7 @@ import discord4j.common.store.api.object.ExactResultNotAvailableException; import discord4j.common.util.Snowflake; import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.automod.AutoModRule; import discord4j.core.object.entity.*; import discord4j.core.object.entity.channel.Channel; @@ -146,4 +147,22 @@ public Flux getGuildAutoModRules(Snowflake guildId) { return Flux.from(store.execute(ReadActions.getAutoModRulesInGuild(guildId.asLong()))) .map(data -> new AutoModRule(gateway, data)); } + + //TODO: Implement + @Override + public Mono getScheduledEventById(Snowflake guildId, Snowflake eventId) { + return null; + } + + //TODO: Implement + @Override + public Flux getScheduledEvents(Snowflake guildId) { + return null; + } + + //TODO: Implement + @Override + public Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId) { + return null; + } } diff --git a/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java b/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java index cceccd9db..a00c8b8ae 100644 --- a/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java +++ b/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java @@ -74,6 +74,11 @@ public class DispatchStoreLayer { add(Ready.class::isInstance, (Integer shard, Ready dispatch) -> GatewayActions.ready(dispatch)); add(UserUpdate.class::isInstance, GatewayActions::userUpdate); add(VoiceStateUpdateDispatch.class::isInstance, GatewayActions::voiceStateUpdateDispatch); + add(GuildScheduledEventCreate.class::isInstance, GatewayActions::guildScheduledEventCreate); + add(GuildScheduledEventUpdate.class::isInstance, GatewayActions::guildScheduledEventUpdate); + add(GuildScheduledEventDelete.class::isInstance, GatewayActions::guildScheduledEventDelete); + add(GuildScheduledEventUserAdd.class::isInstance, GatewayActions::guildScheduledEventUserAdd); + add(GuildScheduledEventUserRemove.class::isInstance, GatewayActions::guildScheduledEventUserRemove); add(dispatch -> dispatch instanceof GatewayStateChange && ((GatewayStateChange) dispatch).getState() == GatewayStateChange.State.DISCONNECTED, (shard, dispatch) -> GatewayActions.invalidateShard(shard, InvalidationCause.LOGOUT)); diff --git a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java index eb782e5dd..56abdfa32 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java +++ b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java @@ -127,7 +127,7 @@ public Mono delete(@Nullable final String reason) { * {@code getInterestedUsersBefore(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) >= 0)} * * @param userId The ID of the newest user to retrieve. - * @param withMember Whether to optionally include the member object in the returned data. + * @param withMember Whether to optionally include the member object in the returned data (if the user is a member). * @return A {@link Flux} that continually emits all {@link GuildScheduledEventUserData users} before * the specified ID. If an error is received, it is emitted through the {@code Flux}. * @see @@ -153,7 +153,7 @@ public Flux getInterestedUsersBefore(Snowflake user * {@code getInterestedUsersAfter(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) <= 0)} * * @param userId The ID of the oldest user to retrieve. - * @param withMember Whether to optionally include the member object in the returned data. + * @param withMember Whether to optionally include the member object in the returned data (if the user is a member). * @return A {@link Flux} that continually emits all {@link GuildScheduledEventUserData users} after * the specified ID. If an error is received, it is emitted through the {@code Flux}. * @see From a0e3a66baf76fd6e08a103a85132ae7f0c114b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 10 Apr 2023 19:23:14 +0200 Subject: [PATCH 07/21] feat(scheduled events): implement core and store methods for scheduled events --- .../java/discord4j/common/store/Store.java | 8 +- .../store/action/gateway/GatewayActions.java | 117 +++++++------- .../GuildScheduledEventCreateAction.java | 28 +--- .../GuildScheduledEventDeleteAction.java | 31 +--- .../GuildScheduledEventUpdateAction.java | 31 +--- ...GuildScheduledEventUsersInEventAction.java | 3 +- .../read/GetGuildScheduledEventsAction.java | 28 ---- .../common/store/action/read/ReadActions.java | 81 ++++------ .../common/store/api/layout/DataAccessor.java | 65 ++++---- .../store/api/layout/GatewayDataUpdater.java | 56 +++++++ .../common/store/impl/LocalStoreLayout.java | 73 +++++++++ .../store/legacy/LegacyStoreLayout.java | 77 ++++++++++ .../common/store/legacy/StateHolder.java | 43 ++++-- .../discord4j/core/GatewayDiscordClient.java | 23 ++- .../discord4j/core/object/entity/Guild.java | 53 ++++++- .../core/object/entity/ScheduledEvent.java | 145 +++++++++++++++--- .../retriever/FallbackEntityRetriever.java | 24 ++- .../core/retriever/StoreEntityRetriever.java | 18 ++- .../ScheduledEventCreateSpecGenerator.java | 66 ++++++++ .../spec/ScheduledEventEditSpecGenerator.java | 66 ++++++++ ...duledEventEntityMetadataSpecGenerator.java | 18 +++ .../gateway/state/DispatchStoreLayer.java | 10 +- 22 files changed, 762 insertions(+), 302 deletions(-) delete mode 100644 common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java create mode 100644 core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java create mode 100644 core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java create mode 100644 core/src/main/java/discord4j/core/spec/ScheduledEventEntityMetadataSpecGenerator.java diff --git a/common/src/main/java/discord4j/common/store/Store.java b/common/src/main/java/discord4j/common/store/Store.java index 50b039366..6b3e2a67b 100644 --- a/common/src/main/java/discord4j/common/store/Store.java +++ b/common/src/main/java/discord4j/common/store/Store.java @@ -174,12 +174,10 @@ private static ActionMapper dataAccessorToMapper(DataAccessor dataAccessor) { .getVoiceStatesInGuild(action.getGuildId())) .map(GetVoiceStateByIdAction.class, action -> dataAccessor .getVoiceStateById(action.getGuildId(), action.getUserId())) - .map(GetGuildScheduledEventsAction.class, action -> dataAccessor.getScheduledEvents()) .map(GetGuildScheduledEventsInGuildAction.class, action -> dataAccessor .getScheduledEventsInGuild(action.getGuildId())) .map(GetGuildScheduledEventByIdAction.class, action -> dataAccessor .getScheduledEventById(action.getGuildId(), action.getEventId())) - .map(GetGuildScheduledEventUsersAction.class, action -> dataAccessor.getScheduledEventUsers()) .map(GetGuildScheduledEventUsersInEventAction.class, action -> dataAccessor .getScheduledEventUsersInEvent(action.getGuildId(), action.getEventId())) .build(); @@ -216,6 +214,12 @@ private static ActionMapper gatewayDataUpdaterToMapper(GatewayDataUpdater gatewa .onGuildRoleDelete(action.getShardIndex(), action.getGuildRoleDelete())) .map(GuildRoleUpdateAction.class, action -> gatewayDataUpdater .onGuildRoleUpdate(action.getShardIndex(), action.getGuildRoleUpdate())) + .map(GuildScheduledEventCreateAction.class, action -> gatewayDataUpdater + .onGuildScheduledEventCreate(action.getShardIndex(), action.getGuildScheduledEventCreate())) + .map(GuildScheduledEventUpdateAction.class, action -> gatewayDataUpdater + .onGuildScheduledEventUpdate(action.getShardIndex(), action.getGuildScheduledEventUpdate())) + .map(GuildScheduledEventDeleteAction.class, action -> gatewayDataUpdater + .onGuildScheduledEventDelete(action.getShardIndex(), action.getGuildScheduledEventDelete())) .map(GuildUpdateAction.class, action -> gatewayDataUpdater .onGuildUpdate(action.getShardIndex(), action.getGuildUpdate())) .map(InvalidateShardAction.class, action -> gatewayDataUpdater diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java b/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java index cba1b9e9c..c6f8154c6 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GatewayActions.java @@ -185,6 +185,64 @@ public static GuildRoleUpdateAction guildRoleUpdate(int shardIndex, GuildRoleUpd return new GuildRoleUpdateAction(shardIndex, dispatch); } + /** + * Creates an action to execute when a {@link GuildScheduledEventCreate} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventCreate} + */ + public static GuildScheduledEventCreateAction guildScheduledEventCreate(int shardIndex, GuildScheduledEventCreate dispatch) { + return new GuildScheduledEventCreateAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventUpdate} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUpdate} + */ + public static GuildScheduledEventUpdateAction guildScheduledEventUpdate(int shardIndex, GuildScheduledEventUpdate dispatch) { + return new GuildScheduledEventUpdateAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventDelete} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventDelete} + */ + public static GuildScheduledEventDeleteAction guildScheduledEventDelete(int shardIndex, GuildScheduledEventDelete dispatch) { + return new GuildScheduledEventDeleteAction(shardIndex, dispatch); + } + + + /** + * Creates an action to execute when a {@link GuildScheduledEventUserAdd} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUserAddAction} + */ + public static GuildScheduledEventUserAddAction guildScheduledEventUserAdd(int shardIndex, + GuildScheduledEventUserAdd dispatch) { + return new GuildScheduledEventUserAddAction(shardIndex, dispatch); + } + + /** + * Creates an action to execute when a {@link GuildScheduledEventUserRemove} is received from the gateway. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a new {@link GuildScheduledEventUserRemoveAction} + */ + public static GuildScheduledEventUserRemoveAction guildScheduledEventUserRemove(int shardIndex, + GuildScheduledEventUserRemove dispatch) { + return new GuildScheduledEventUserRemoveAction(shardIndex, dispatch); + } + /** * Creates an action to execute when a {@link GuildUpdate} is received from the gateway. * @@ -351,63 +409,4 @@ public static CompleteGuildMembersAction completeGuildMembers(long guildId) { return new CompleteGuildMembersAction(guildId); } - /** - * Creates an action to execute when a {@link GuildScheduledEventCreate} is received from the gateway. - * - * @param shardIndex the index of the shard where the dispatch comes from - * @param dispatch the dispatch data coming from Discord gateway - * @return a new {@link GuildScheduledEventCreateAction} - */ - public static GuildScheduledEventCreateAction guildScheduledEventCreate(int shardIndex, - GuildScheduledEventCreate dispatch) { - return new GuildScheduledEventCreateAction(shardIndex, dispatch); - } - - /** - * Creates an action to execute when a {@link GuildScheduledEventUpdate} is received from the gateway. - * - * @param shardIndex the index of the shard where the dispatch comes from - * @param dispatch the dispatch data coming from Discord gateway - * @return a new {@link GuildScheduledEventUpdateAction} - */ - public static GuildScheduledEventUpdateAction guildScheduledEventUpdate(int shardIndex, - GuildScheduledEventUpdate dispatch) { - return new GuildScheduledEventUpdateAction(shardIndex, dispatch); - } - - /** - * Creates an action to execute when a {@link GuildScheduledEventDelete} is received from the gateway. - * - * @param shardIndex the index of the shard where the dispatch comes from - * @param dispatch the dispatch data coming from Discord gateway - * @return a new {@link GuildScheduledEventDeleteAction} - */ - public static GuildScheduledEventDeleteAction guildScheduledEventDelete(int shardIndex, - GuildScheduledEventDelete dispatch) { - return new GuildScheduledEventDeleteAction(shardIndex, dispatch); - } - - /** - * Creates an action to execute when a {@link GuildScheduledEventUserAdd} is received from the gateway. - * - * @param shardIndex the index of the shard where the dispatch comes from - * @param dispatch the dispatch data coming from Discord gateway - * @return a new {@link GuildScheduledEventUserAddAction} - */ - public static GuildScheduledEventUserAddAction guildScheduledEventUserAdd(int shardIndex, - GuildScheduledEventUserAdd dispatch) { - return new GuildScheduledEventUserAddAction(shardIndex, dispatch); - } - - /** - * Creates an action to execute when a {@link GuildScheduledEventUserRemove} is received from the gateway. - * - * @param shardIndex the index of the shard where the dispatch comes from - * @param dispatch the dispatch data coming from Discord gateway - * @return a new {@link GuildScheduledEventUserRemoveAction} - */ - public static GuildScheduledEventUserRemoveAction guildScheduledEventUserRemove(int shardIndex, - GuildScheduledEventUserRemove dispatch) { - return new GuildScheduledEventUserRemoveAction(shardIndex, dispatch); - } } diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java index 7336f819a..c1efe7a8d 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java @@ -1,34 +1,18 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - package discord4j.common.store.action.gateway; import discord4j.discordjson.json.gateway.GuildScheduledEventCreate; public class GuildScheduledEventCreateAction extends ShardAwareAction { - private final GuildScheduledEventCreate eventCreate; + private final GuildScheduledEventCreate guildScheduledEventCreate; - public GuildScheduledEventCreateAction(int shardIndex, GuildScheduledEventCreate eventCreate) { + GuildScheduledEventCreateAction(int shardIndex, GuildScheduledEventCreate guildScheduledEventCreate) { super(shardIndex); - this.eventCreate = eventCreate; + this.guildScheduledEventCreate = guildScheduledEventCreate; } - public GuildScheduledEventCreate getEventCreate() { - return eventCreate; + public GuildScheduledEventCreate getGuildScheduledEventCreate() { + return guildScheduledEventCreate; } + } diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java index c9bb78fbc..0dd8284bf 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java @@ -1,34 +1,19 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - package discord4j.common.store.action.gateway; +import discord4j.discordjson.json.GuildScheduledEventData; import discord4j.discordjson.json.gateway.GuildScheduledEventDelete; -public class GuildScheduledEventDeleteAction extends ShardAwareAction { +public class GuildScheduledEventDeleteAction extends ShardAwareAction { - private final GuildScheduledEventDelete eventDelete; + private final GuildScheduledEventDelete guildScheduledEventDelete; - public GuildScheduledEventDeleteAction(int shardIndex, GuildScheduledEventDelete eventDelete) { + GuildScheduledEventDeleteAction(int shardIndex, GuildScheduledEventDelete guildScheduledEventDelete) { super(shardIndex); - this.eventDelete = eventDelete; + this.guildScheduledEventDelete = guildScheduledEventDelete; } - public GuildScheduledEventDelete getEventDelete() { - return eventDelete; + public GuildScheduledEventDelete getGuildScheduledEventDelete() { + return guildScheduledEventDelete; } + } diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java index 274e93795..8f534cb4f 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java @@ -1,34 +1,19 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - package discord4j.common.store.action.gateway; +import discord4j.discordjson.json.GuildScheduledEventData; import discord4j.discordjson.json.gateway.GuildScheduledEventUpdate; -public class GuildScheduledEventUpdateAction extends ShardAwareAction { +public class GuildScheduledEventUpdateAction extends ShardAwareAction { - private final GuildScheduledEventUpdate eventUpdate; + private final GuildScheduledEventUpdate guildScheduledEventUpdate; - public GuildScheduledEventUpdateAction(int shardIndex, GuildScheduledEventUpdate eventUpdate) { + GuildScheduledEventUpdateAction(int shardIndex, GuildScheduledEventUpdate guildScheduledEventUpdate) { super(shardIndex); - this.eventUpdate = eventUpdate; + this.guildScheduledEventUpdate = guildScheduledEventUpdate; } - public GuildScheduledEventUpdate getEventUpdate() { - return eventUpdate; + public GuildScheduledEventUpdate getGuildScheduledEventUpdate() { + return guildScheduledEventUpdate; } + } diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java index 0ef0652a3..940e51b26 100644 --- a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java +++ b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventUsersInEventAction.java @@ -18,9 +18,10 @@ package discord4j.common.store.action.read; import discord4j.common.store.api.StoreAction; +import discord4j.discordjson.Id; import discord4j.discordjson.json.GuildScheduledEventUserData; -public class GetGuildScheduledEventUsersInEventAction implements StoreAction { +public class GetGuildScheduledEventUsersInEventAction implements StoreAction { private final long guildId; private final long eventId; diff --git a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java b/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java deleted file mode 100644 index 99a3c10d9..000000000 --- a/common/src/main/java/discord4j/common/store/action/read/GetGuildScheduledEventsAction.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Discord4J. - * - * Discord4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Discord4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Discord4J. If not, see . - */ - -package discord4j.common.store.action.read; - -import discord4j.common.store.api.StoreAction; -import discord4j.discordjson.json.GuildScheduledEventData; - -public class GetGuildScheduledEventsAction implements StoreAction { - - GetGuildScheduledEventsAction() { - - } -} diff --git a/common/src/main/java/discord4j/common/store/action/read/ReadActions.java b/common/src/main/java/discord4j/common/store/action/read/ReadActions.java index ec7eef0d4..68bac9a70 100644 --- a/common/src/main/java/discord4j/common/store/action/read/ReadActions.java +++ b/common/src/main/java/discord4j/common/store/action/read/ReadActions.java @@ -457,6 +457,38 @@ public static GetRoleByIdAction getRoleById(long guildId, long roleId) { return new GetRoleByIdAction(guildId, roleId); } + /** + * Creates an action to retrieve data associated to scheduled events in a given guild + * + * @param guildId the related events guild id + * @return a new {@link GetGuildScheduledEventsInGuildAction} + */ + public static GetGuildScheduledEventsInGuildAction getScheduledEventsInGuild(long guildId) { + return new GetGuildScheduledEventsInGuildAction(guildId); + } + + /** + * Creates an action to retrieve data for the scheduled event corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return a new {@link GetGuildScheduledEventByIdAction} + */ + public static GetGuildScheduledEventByIdAction getScheduledEventById(long guildId, long eventId) { + return new GetGuildScheduledEventByIdAction(guildId, eventId); + } + + /** + * Creates an action to retrieve data for the scheduled event users corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return a new {@link GetGuildScheduledEventUsersAction} + */ + public static GetGuildScheduledEventUsersInEventAction getScheduledEventsUsers(long guildId, long eventId) { + return new GetGuildScheduledEventUsersInEventAction(guildId, eventId); + } + /** * Creates an action to retrieve data for all users present in a store. * @@ -517,53 +549,4 @@ public static GetVoiceStateByIdAction getVoiceStateById(long guildId, long userI return new GetVoiceStateByIdAction(guildId, userId); } - /** - * Creates an action to retrieve data for all scheduled events in a store. - * - * @return a new {@link GetGuildScheduledEventsAction} - */ - public static GetGuildScheduledEventsAction getEvents() { - return new GetGuildScheduledEventsAction(); - } - - /** - * Creates an action to retrieve data for all scheduled events in a store for the given guild ID. - * - * @param guildId the guild ID - * @return a new {@link GetGuildScheduledEventsAction} - */ - public static GetGuildScheduledEventsInGuildAction getEvents(long guildId) { - return new GetGuildScheduledEventsInGuildAction(guildId); - } - - /** - * Creates an action to retrieve data for the scheduled event corresponding to the given guild ID and event ID. - * - * @param guildId the guild ID - * @param eventId the event ID - * @return a new {@link GetGuildScheduledEventByIdAction} - */ - public static GetGuildScheduledEventByIdAction getEvent(long guildId, long eventId) { - return new GetGuildScheduledEventByIdAction(guildId, eventId); - } - - /** - * Creates an action to retrieve all data for scheduled event users in a store. - * - * @return a new {@link GetGuildScheduledEventUsersAction} - */ - public static GetGuildScheduledEventUsersAction getEventUsers() { - return new GetGuildScheduledEventUsersAction(); - } - - /** - * Creates an action to retrieve data for the scheduled event users corresponding to the given guild ID and event ID. - * - * @param guildId the guild ID - * @param eventId the event ID - * @return a new {@link GetGuildScheduledEventUsersAction} - */ - public static GetGuildScheduledEventUsersInEventAction getEventUsers(long guildId, long eventId) { - return new GetGuildScheduledEventUsersInEventAction(guildId, eventId); - } } diff --git a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java index bbabb4c80..1dbf97611 100644 --- a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java +++ b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java @@ -18,6 +18,7 @@ package discord4j.common.store.api.layout; import discord4j.common.store.api.object.ExactResultNotAvailableException; +import discord4j.discordjson.Id; import discord4j.discordjson.json.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -280,6 +281,32 @@ default Mono getStickerById(long guildId, long stickerId) { */ Mono getGuildById(long guildId); + /** + * Retrieves data for all guild scheduled events corresponding to the given guild ID. + * + * @param guildId the guild ID + * @return A {@link Flux} emitting the scheduled events, or empty if none is present + */ + Flux getScheduledEventsInGuild(long guildId); + + /** + * Retrieves data for the guild scheduled event corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return A {@link Mono} emitting the scheduled event, or empty if not found + */ + Mono getScheduledEventById(long guildId, long eventId); + + /** + * Retrieves data for all guild scheduled event users corresponding to the given guild ID and event ID. + * + * @param guildId the guild ID + * @param eventId the event ID + * @return A {@link Flux} emitting the scheduled event users ids, or empty if none is present + */ + Flux getScheduledEventUsersInEvent(long guildId, long eventId); + /** * Retrieves data for all members present in the store. * @@ -434,43 +461,5 @@ default Mono getStickerById(long guildId, long stickerId) { */ Mono getVoiceStateById(long guildId, long userId); - /** - * Retrieves data for all guild scheduled events in the store. - * - * @return A {@link Flux} emitting the scheduled events, or empty if none is present - */ - Flux getScheduledEvents(); - /** - * Retrieves data for all guild scheduled events corresponding to the given guild ID. - * - * @param guildId the guild ID - * @return A {@link Flux} emitting the scheduled events, or empty if none is present - */ - Flux getScheduledEventsInGuild(long guildId); - - /** - * Retrieves data for the guild scheduled event corresponding to the given guild ID and event ID. - * - * @param guildId the guild ID - * @param eventId the event ID - * @return A {@link Mono} emitting the scheduled event, or empty if not found - */ - Mono getScheduledEventById(long guildId, long eventId); - - /** - * Retrieves data for all guild scheduled event users in the store - * - * @return A {@link Flux} emitting the scheduled event users, or empty if none is present - */ - Flux getScheduledEventUsers(); - - /** - * Retrieves data for all guild scheduled event users corresponding to the given guild ID and event ID. - * - * @param guildId the guild ID - * @param eventId the event ID - * @return A {@link Flux} emitting the scheduled event users, or empty if none is present - */ - Flux getScheduledEventUsersInEvent(long guildId, long eventId); } diff --git a/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java b/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java index 39648ba71..fd2a61e53 100644 --- a/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java +++ b/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java @@ -194,6 +194,62 @@ public interface GatewayDataUpdater { */ Mono onGuildRoleUpdate(int shardIndex, GuildRoleUpdate dispatch); + /** + * Updates the internal state of the store according to the {@link GuildScheduledEventCreate} gateway dispatch. This + * will typically perform an insert operation on a new {@link GuildScheduledEventData} in the store. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a {@link Mono} completing when the operation is done + */ + Mono onGuildScheduledEventCreate(int shardIndex, GuildScheduledEventCreate dispatch); + + /** + * Updates the internal state of the store according to the {@link GuildScheduledEventUpdate} gateway dispatch. This + * will typically perform an update operation on a related {@link GuildScheduledEventData} already present in the + * store. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a {@link Mono} completing when the operation is done, optionally returning {@link GuildScheduledEventData} + * in a state before the update + */ + Mono onGuildScheduledEventUpdate(int shardIndex, GuildScheduledEventUpdate dispatch); + + /** + * Updates the internal state of the store according to the {@link GuildScheduledEventCreate} gateway dispatch. This + * will typically perform a delete operation on a related {@link GuildScheduledEventData} in the store, + * if present. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a {@link Mono} completing when the operation is done, optionally returning the + * {@link GuildScheduledEventData} in a state before the deletion. + */ + Mono onGuildScheduledEventDelete(int shardIndex, GuildScheduledEventDelete dispatch); + + /** + * Updates the internal state of the store according to the {@link GuildScheduledEventUserAdd} gateway dispatch. + * This will typically perform an insert operation on a related {@link java.util.List} handling a relationship + * between a {@link GuildScheduledEventData} and the provided {@link GuildScheduledEventUserData}. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a {@link Mono} completing when the operation is done + */ + Mono onGuildScheduledEventUserAdd(int shardIndex, GuildScheduledEventUserAdd dispatch); + + /** + * Updates the internal state of the store according to the {@link GuildScheduledEventUserAdd} gateway dispatch. + * This will typically perform a delete operation on a related {@link java.util.List} handling a relationship + * between a {@link GuildScheduledEventData} and the provided {@link GuildScheduledEventUserData}. + * + * @param shardIndex the index of the shard where the dispatch comes from + * @param dispatch the dispatch data coming from Discord gateway + * @return a {@link Mono} completing when the operation is done + */ + Mono onGuildScheduledEventUserRemove(int shardIndex, GuildScheduledEventUserRemove dispatch); + /** * Updates the internal state of the store according to the given {@link GuildUpdate} gateway dispatch. This will * typically perform an update operation on a related {@link GuildData} that is already present in the store. diff --git a/common/src/main/java/discord4j/common/store/impl/LocalStoreLayout.java b/common/src/main/java/discord4j/common/store/impl/LocalStoreLayout.java index e094250c6..64fb37c4b 100644 --- a/common/src/main/java/discord4j/common/store/impl/LocalStoreLayout.java +++ b/common/src/main/java/discord4j/common/store/impl/LocalStoreLayout.java @@ -74,6 +74,9 @@ public class LocalStoreLayout implements StoreLayout, DataAccessor, GatewayDataU private final ConcurrentMap roles = new ConcurrentHashMap<>(); + private final ConcurrentMap scheduledEvents = new ConcurrentHashMap<>(); + private final ConcurrentMap> scheduledEventsUsers = new ConcurrentHashMap<>(); + private final ConcurrentMap> users = StorageBackend.caffeine(Caffeine::weakValues).newMap(); @@ -279,6 +282,25 @@ public Mono getGuildById(long guildId) { return Mono.justOrEmpty(guilds.get(guildId)).map(WrappedGuildData::unwrap); } + @Override + public Flux getScheduledEventsInGuild(long guildId) { + return Mono.justOrEmpty(contentByGuild.get(guildId)) + .flatMapIterable(content -> content.eventIds) + .mapNotNull(scheduledEvents::get); + } + + @Override + public Mono getScheduledEventById(long guildId, long eventId) { + return Mono.justOrEmpty(scheduledEvents.get(eventId)) + .filter(event -> event.guildId().asLong() == guildId); + } + + @Override + public Flux getScheduledEventUsersInEvent(long guildId, long eventId) { + return Flux.fromIterable(scheduledEventsUsers.getOrDefault(new Long2(guildId, eventId), new ArrayList<>())) + .map(Id::of); + } + @Override public Flux getMembers() { return Flux.fromIterable(members.values()).map(WithUser::get); @@ -570,6 +592,55 @@ public Mono onGuildRoleUpdate(int shardIndex, GuildRoleUpdate dispatch return Mono.fromCallable(() -> saveRole(dispatch.guildId().asLong(), dispatch.role())); } + @Override + public Mono onGuildScheduledEventCreate(int shardIndex, GuildScheduledEventCreate dispatch) { + final long eventId = dispatch.scheduledEvent().id().asLong(); + + // Add event to guild->events index + GuildContent guildContent = computeGuildContent(dispatch.scheduledEvent().guildId().asLong()); + guildContent.eventIds.add(eventId); + + // Store the event + return Mono.fromRunnable(() -> scheduledEvents.put(eventId, ImmutableGuildScheduledEventData.copyOf(dispatch.scheduledEvent()))); + } + + @Override + public Mono onGuildScheduledEventUpdate(int shardIndex, GuildScheduledEventUpdate dispatch) { + final long eventId = dispatch.scheduledEvent().id().asLong(); + + // Update the event + return Mono.fromCallable(() -> scheduledEvents.replace(eventId, ImmutableGuildScheduledEventData.copyOf(dispatch.scheduledEvent()))); + } + + @Override + public Mono onGuildScheduledEventDelete(int shardIndex, GuildScheduledEventDelete dispatch) { + final long eventId = dispatch.scheduledEvent().id().asLong(); + + // Remove event from guild->events index + GuildContent guildContent = computeGuildContent(dispatch.scheduledEvent().guildId().asLong()); + guildContent.eventIds.remove(eventId); + + // Remove the event + return Mono.fromRunnable(() -> { + scheduledEvents.remove(eventId); + scheduledEventsUsers.remove(new Long2(dispatch.scheduledEvent().guildId().asLong(), eventId)); + }); + } + + @Override + public Mono onGuildScheduledEventUserAdd(int shardIndex, GuildScheduledEventUserAdd dispatch) { + final Long2 key = new Long2(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); + + return Mono.fromRunnable(() -> scheduledEventsUsers.computeIfAbsent(key, ignored -> new ArrayList<>()).add(dispatch.userId().asLong())); + } + + @Override + public Mono onGuildScheduledEventUserRemove(int shardIndex, GuildScheduledEventUserRemove dispatch) { + final Long2 key = new Long2(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); + + return Mono.fromRunnable(() -> scheduledEventsUsers.computeIfAbsent(key, ignored -> new ArrayList<>()).remove(dispatch.userId().asLong())); + } + @Override public Mono onGuildUpdate(int shardIndex, GuildUpdate dispatch) { long guildId = dispatch.guild().id().asLong(); @@ -1000,6 +1071,7 @@ private class GuildContent { private final long guildId; private final Set channelIds = new HashSet<>(); private final Set emojiIds = new HashSet<>(); + private final Set eventIds = new HashSet<>(); private final Set stickerIds = new HashSet<>(); private final Set memberIds = new HashSet<>(); private final Set presenceIds = new HashSet<>(); @@ -1032,6 +1104,7 @@ private GuildData dispose() { members.keySet().removeAll(memberIds); presences.keySet().removeAll(presenceIds); roles.keySet().removeAll(roleIds); + scheduledEvents.keySet().removeAll(eventIds); voiceStates.keySet().removeAll(voiceStateIds); return ifNonNullMap(old, WrappedGuildData::unwrap); } diff --git a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java index 633bd0b85..bf4dcf3c0 100644 --- a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java +++ b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java @@ -230,6 +230,24 @@ public Mono getGuildById(long guildId) { return stateHolder.getGuildStore().find(guildId); } + @Override + public Flux getScheduledEventsInGuild(long guildId) { + return stateHolder.getGuildEventsStore() + .findInRange(LongLongTuple2.of(guildId, 0), LongLongTuple2.of(guildId, Long.MAX_VALUE)); + } + + @Override + public Mono getScheduledEventById(long guildId, long eventId) { + return stateHolder.getGuildEventsStore().find(LongLongTuple2.of(guildId, eventId)); + } + + @SuppressWarnings("unchecked") + @Override + public Flux getScheduledEventUsersInEvent(long guildId, long eventId) { + return stateHolder.getGuildEventsUsersStore().find(LongLongTuple2.of(guildId, eventId)) + .flatMapIterable(list -> list); + } + @Override public Flux getMembers() { return stateHolder.getMemberStore().values(); @@ -898,6 +916,65 @@ public Mono onGuildRoleUpdate(int shardIndex, GuildRoleUpdate dispatch .switchIfEmpty(saveNew.then(Mono.empty())); } + @Override + public Mono onGuildScheduledEventCreate(int shardIndex, GuildScheduledEventCreate dispatch) { + LongLongTuple2 key = LongLongTuple2.of(dispatch.scheduledEvent().guildId().asLong(), dispatch.scheduledEvent().id().asLong()); + + return stateHolder.getGuildEventsStore().save(key, dispatch.scheduledEvent()); + } + + @Override + public Mono onGuildScheduledEventUpdate(int shardIndex, GuildScheduledEventUpdate dispatch) { + LongLongTuple2 key = LongLongTuple2.of(dispatch.scheduledEvent().guildId().asLong(), dispatch.scheduledEvent().id().asLong()); + + Mono saveNew = stateHolder.getGuildEventsStore().save(key, dispatch.scheduledEvent()); + + return stateHolder.getGuildEventsStore() + .find(key) + .flatMap(saveNew::thenReturn) + .switchIfEmpty(saveNew.then(Mono.empty())); + } + + @Override + public Mono onGuildScheduledEventDelete(int shardIndex, GuildScheduledEventDelete dispatch) { + LongLongTuple2 key = LongLongTuple2.of(dispatch.scheduledEvent().guildId().asLong(), dispatch.scheduledEvent().id().asLong()); + + Mono deletion = stateHolder.getGuildEventsStore().delete(key); + + return stateHolder.getGuildEventsStore() + .find(key) + .flatMap(deletion::thenReturn) + .switchIfEmpty(deletion.then(Mono.empty())); + } + + + @SuppressWarnings("unchecked") + @Override + public Mono onGuildScheduledEventUserAdd(int shardIndex, GuildScheduledEventUserAdd dispatch) { + LongLongTuple2 key = LongLongTuple2.of(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); + + return stateHolder.getGuildEventsUsersStore().find(key) + .defaultIfEmpty(new HashSet()) + .map(set -> { + set.add(dispatch.userId()); + return set; + }) + .flatMap(set -> stateHolder.getGuildEventsUsersStore().save(key, set)); + } + + @Override + public Mono onGuildScheduledEventUserRemove(int shardIndex, GuildScheduledEventUserRemove dispatch) { + LongLongTuple2 key = LongLongTuple2.of(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); + + return stateHolder.getGuildEventsUsersStore().find(key) + .defaultIfEmpty(new HashSet()) + .map(set -> { + set.remove(dispatch.userId()); + return set; + }) + .flatMap(set -> stateHolder.getGuildEventsUsersStore().save(key, set)); + } + @Override public Mono onGuildUpdate(int shardIndex, GuildUpdate dispatch) { long guildId = Snowflake.asLong(dispatch.guild().id()); diff --git a/common/src/main/java/discord4j/common/store/legacy/StateHolder.java b/common/src/main/java/discord4j/common/store/legacy/StateHolder.java index d9ef318c0..10d5071f0 100644 --- a/common/src/main/java/discord4j/common/store/legacy/StateHolder.java +++ b/common/src/main/java/discord4j/common/store/legacy/StateHolder.java @@ -17,6 +17,7 @@ package discord4j.common.store.legacy; +import discord4j.discordjson.Id; import discord4j.discordjson.json.*; import discord4j.discordjson.json.gateway.PresenceUpdate; import discord4j.store.api.Store; @@ -29,6 +30,8 @@ import reactor.util.Loggers; import java.util.Collections; +import java.util.List; +import java.util.Set; /** * Holder for various pieces of state for use in caching. @@ -37,6 +40,8 @@ *

    *
  • Channel store: {@code long} keys and {@link ChannelData} values.
  • *
  • Guild store: {@code long} keys and {@link GuildData} values.
  • + *
  • Guild scheduled event store: {@code long} pair keys and {@link GuildScheduledEventData} values.
  • + *
  • Guild scheduled event users store: {@code long} pair keys and {@link Id} values.
  • *
  • Guild emoji store: {@code long} keys and {@link EmojiData} values.
  • *
  • Member store: {@code long} pair keys and {@link MemberData} values.
  • *
  • Message store: {@code long} keys and {@link MessageData} values.
  • @@ -46,6 +51,7 @@ *
  • Voice state store: {@code long} pair keys and {@link VoiceStateData} values.
  • *
*/ +@SuppressWarnings("rawtypes") public final class StateHolder { private static final Logger log = Loggers.getLogger(StateHolder.class); @@ -53,6 +59,10 @@ public final class StateHolder { private final StoreService storeService; private final LongObjStore channelStore; private final LongObjStore guildStore; + private final Store guildEventsStore; + + private final Store guildEventsUsersStore; + private final LongObjStore guildEmojiStore; private final LongObjStore guildStickerStore; private final Store memberStore; @@ -68,34 +78,40 @@ public StateHolder(final StoreService service) { service.init(new StoreContext(Collections.singletonMap("messageClass", MessageData.class))); channelStore = service.provideLongObjStore(ChannelData.class); - log.debug("Channel storage : {}", channelStore); + log.debug("Channel storage : {}", channelStore); guildStore = service.provideLongObjStore(GuildData.class); - log.debug("Guild storage : {}", guildStore); + log.debug("Guild storage : {}", guildStore); guildStickerStore = service.provideLongObjStore(StickerData.class); log.debug("Guild sticker storage : {}", guildStickerStore); + guildEventsStore = service.provideGenericStore(LongLongTuple2.class, GuildScheduledEventData.class); + log.debug("Guild event storage : {}", guildEventsStore); + + guildEventsUsersStore = service.provideGenericStore(LongLongTuple2.class, Set.class); + log.debug("Guild event users storage : {}", guildEventsUsersStore); + guildEmojiStore = service.provideLongObjStore(EmojiData.class); - log.debug("Guild emoji storage : {}", guildEmojiStore); + log.debug("Guild emoji storage : {}", guildEmojiStore); memberStore = service.provideGenericStore(LongLongTuple2.class, MemberData.class); - log.debug("Member storage : {}", memberStore); + log.debug("Member storage : {}", memberStore); messageStore = service.provideLongObjStore(MessageData.class); - log.debug("Message storage : {}", messageStore); + log.debug("Message storage : {}", messageStore); presenceStore = service.provideGenericStore(LongLongTuple2.class, PresenceData.class); - log.debug("Presence storage : {}", presenceStore); + log.debug("Presence storage : {}", presenceStore); roleStore = service.provideLongObjStore(RoleData.class); - log.debug("Role storage : {}", roleStore); + log.debug("Role storage : {}", roleStore); userStore = service.provideLongObjStore(UserData.class); - log.debug("User storage : {}", userStore); + log.debug("User storage : {}", userStore); voiceStateStore = service.provideGenericStore(LongLongTuple2.class, VoiceStateData.class); - log.debug("Voice state storage : {}", voiceStateStore); + log.debug("Voice state storage : {}", voiceStateStore); } public StoreService getStoreService() { @@ -114,6 +130,14 @@ public LongObjStore getGuildStickerStore() { return guildStickerStore; } + public Store getGuildEventsStore() { + return guildEventsStore; + } + + public Store getGuildEventsUsersStore() { + return guildEventsUsersStore; + } + public LongObjStore getGuildEmojiStore() { return guildEmojiStore; } @@ -146,6 +170,7 @@ public Mono invalidateStores() { return channelStore.invalidate() .and(guildStore.invalidate()) .and(guildEmojiStore.invalidate()) + .and(guildEventsStore.invalidate()) .and(memberStore.invalidate()) .and(messageStore.invalidate()) .and(presenceStore.invalidate()) diff --git a/core/src/main/java/discord4j/core/GatewayDiscordClient.java b/core/src/main/java/discord4j/core/GatewayDiscordClient.java index 8ab091b99..a6f96149f 100644 --- a/core/src/main/java/discord4j/core/GatewayDiscordClient.java +++ b/core/src/main/java/discord4j/core/GatewayDiscordClient.java @@ -29,6 +29,7 @@ import discord4j.core.object.GuildTemplate; import discord4j.core.object.Invite; import discord4j.core.object.Region; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.automod.AutoModRule; import discord4j.core.object.entity.*; import discord4j.core.object.entity.channel.Channel; @@ -788,12 +789,6 @@ public Flux getGuildEmojis(Snowflake guildId) { return entityRetriever.getGuildEmojis(guildId); } - //TODO: get guild scheduled events - //TODO: get guild scheduled events with retrieval strategy - - //TODO: get guild scheduled event by ID - //TODO: get guild scheduled event by ID with retrieval strategy - @Override public Flux getGuildStickers(Snowflake guildId) { return entityRetriever.getGuildStickers(guildId); @@ -803,4 +798,20 @@ public Flux getGuildStickers(Snowflake guildId) { public Flux getGuildAutoModRules(Snowflake guildId) { return entityRetriever.getGuildAutoModRules(guildId); } + + @Override + public Mono getScheduledEventById(Snowflake guildId, Snowflake eventId) { + return entityRetriever.getScheduledEventById(guildId, eventId); + } + + @Override + public Flux getScheduledEvents(Snowflake guildId) { + return entityRetriever.getScheduledEvents(guildId); + } + + @Override + public Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId) { + return entityRetriever.getScheduledEventUsers(guildId, eventId); + } + } diff --git a/core/src/main/java/discord4j/core/object/entity/Guild.java b/core/src/main/java/discord4j/core/object/entity/Guild.java index b2b9d9a03..f0740ecdc 100644 --- a/core/src/main/java/discord4j/core/object/entity/Guild.java +++ b/core/src/main/java/discord4j/core/object/entity/Guild.java @@ -1905,13 +1905,56 @@ public Mono getAutoModRule(Snowflake autoModRuleId) { .map(data -> new AutoModRule(gateway, data)); } - //TODO: get scheduled events - //TODO: get scheduled events with retrieval strat + /** + * Requests to retrieve the scheduled event using the provided ID. + * + * @param eventId the event ID + * @param withUserCount Requests to fetch the enrolled user count to Discord or not + * @return A {@link Mono} which, upon completion, emits an associated {@link ScheduledEvent} if found. + */ + public Mono getScheduledEventById(Snowflake eventId, boolean withUserCount) { + return gateway.getRestClient().getScheduledEventById(getId(), eventId).getData(withUserCount) + .map(data -> new ScheduledEvent(gateway, data)); + } + + /** + * Requests to retrieve all the scheduled events associated to this guild. + * + * @param withUserCount Requests to fetch the enrolled user count to Discord or not + * @return A {@link Flux} which emits {@link ScheduledEvent} objects. + */ + public Flux getScheduledEvents(boolean withUserCount) { + Map queryParams = new HashMap<>(); + queryParams.put("with_user_count", withUserCount); + + return gateway.getRestClient().getGuildService().getScheduledEvents(getId().asLong(), queryParams) + .map(data -> new ScheduledEvent(gateway, data)); + } - //TODO: get scheduled event - //TODO: get scheduled event with retrieval strat + /** + * Requests to create a guild scheduled event with the provided spec on this guild + * + * @param spec spec specifying {@link ScheduledEvent} parameters + * @return A {@link Mono} which, upon completion, emits the created {@link ScheduledEvent} object. Any error, if occurs, + * is emitted through the {@link Mono}. + */ + public Mono createScheduledEvent(ScheduledEventCreateSpec spec) { + return gateway.getRestClient().getGuildService().createScheduledEvent(getId().asLong(), spec.asRequest()) + .map(data -> new ScheduledEvent(gateway, data)); + } - //TODO: create scheduled event + /** + * Requests to create a guild scheduled event with the provided mandatory parameters + * + * @param name The name of the scheduled event + * @param privacyLevel the {@link discord4j.core.object.entity.ScheduledEvent.PrivacyLevel} to set + * @param scheduledStartTime the scheduled start time of the event + * @param entityType the {@link discord4j.core.object.entity.ScheduledEvent.EntityType} where the event will take place + * @return A {@link ScheduledEventCreateMono} which, upon completion, returns the created {@link ScheduledEvent} + */ + public ScheduledEventCreateMono createScheduledEvent(String name, int privacyLevel, Instant scheduledStartTime, int entityType) { + return ScheduledEventCreateMono.of(name, privacyLevel, scheduledStartTime, entityType, this); + } @Override public boolean equals(@Nullable final Object obj) { diff --git a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java index a0990b9c2..ecaa06a30 100644 --- a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java +++ b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java @@ -20,11 +20,18 @@ import discord4j.common.util.Snowflake; import discord4j.core.GatewayDiscordClient; import discord4j.core.object.ScheduledEventEntityMetadata; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.entity.channel.GuildChannel; +import discord4j.core.spec.ScheduledEventEditMono; +import discord4j.core.spec.ScheduledEventEditSpec; import discord4j.discordjson.json.GuildScheduledEventData; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.annotation.Nullable; import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -35,16 +42,21 @@ */ public class ScheduledEvent implements Entity { - /** The gateway associated to this object */ + /** + * The gateway associated to this object + */ private final GatewayDiscordClient gateway; - /** The raw data as represented by Discord. */ + /** + * The raw data as represented by Discord. + */ private final GuildScheduledEventData data; /** * Constructs a {@code ScheduledEvent} with an associated {@link GatewayDiscordClient} and Discord data. + * * @param gateway The {@link GatewayDiscordClient} associated to this object, must be non-null. - * @param data The raw data as represented by Discord, must be non-null. + * @param data The raw data as represented by Discord, must be non-null. */ public ScheduledEvent(final GatewayDiscordClient gateway, final GuildScheduledEventData data) { this.gateway = Objects.requireNonNull(gateway); @@ -136,13 +148,15 @@ public Instant getStartTime() { * Gets the scheduled end time of the event, if present. *

* Note: Note: This metadata will always be present when the entity type is {@link EntityType#EXTERNAL external}. + * * @return The scheduled end time of the event, if present. */ public Optional getEndTime() { return data.scheduledEndTime(); } - /** Gets the privacy level of the event + /** + * Gets the privacy level of the event * * @return The privacy level of the event */ @@ -184,16 +198,18 @@ public Optional getEntityId() { * Gets the entity metadata of the event, if present. *

* Note: This metadata will always be present when the entity type is {@link EntityType#EXTERNAL external}. + * * @return The entity metadata of the event, if present. */ public Optional getEntityMetadata() { - return data.entityMetadata().map(data -> new ScheduledEventEntityMetadata(gateway,data)); + return data.entityMetadata().map(data -> new ScheduledEventEntityMetadata(gateway, data)); } /** * Gets the location of the event, if present. *

* Note: This location is pulled from {@link #getEntityMetadata().getLocation()} if present. + * * @return The location of the event, if present. */ public Optional getLocation() { @@ -209,11 +225,91 @@ public Optional getInterestedUserCount() { return data.userCount().toOptional(); } - //TODO: get users + /** + * Requests to retrieve the interested users for this event. + * + * @param withMemberData requests to return member data along with user data + * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after + * the specified ID. If an error is received, it is emitted through the {@code Flux}. + */ + public Flux getInterestedUsers(boolean withMemberData) { + Map queryParams = new HashMap<>(); + queryParams.put("with_member", withMemberData); + + return gateway.getRestClient().getGuildService().getScheduledEventUsers(getGuildId().asLong(), getId().asLong(), queryParams) + .map(data -> new ScheduledEventUser(gateway, data, getGuildId())); + } + + /** + * Requests to retrieve the interested users for this event

  • before
  • the given user ID. + * + * @param userId the ID of the
  • newest
  • user to retrieve + * @param withMemberData requests to return member data along with user data + * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after + * the specified ID. If an error is received, it is emitted through the {@code Flux}. + */ + public Flux getInterestedUsersBefore(Snowflake userId, boolean withMemberData) { + return gateway.getRestClient().getScheduledEventById(getGuildId(), getId()) + .getInterestedUsersBefore(userId, withMemberData) + .map(data -> new ScheduledEventUser(gateway, data, getGuildId())); + } + + /** + * Requests to retrieve the interested users for this event
  • after
  • the given user ID. + * + * @param userId the ID of the
  • oldest
  • user to retrieve + * @param withMemberData requests to return member data along with user data + * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after + * the specified ID. If an error is received, it is emitted through the {@code Flux}. + */ + public Flux getInterestedUsersAfter(Snowflake userId, boolean withMemberData) { + return gateway.getRestClient().getScheduledEventById(getGuildId(), getId()) + .getInterestedUsersAfter(userId, withMemberData) + .map(data -> new ScheduledEventUser(gateway, data, getGuildId())); + } + + /** + * Requests to edit this scheduled event + * + * @return A {@link Mono} which, upon completion, emits the new {@link ScheduledEvent event} update. If an error + * occurs, it is emitted through the {@link Mono}. + */ + public ScheduledEventEditMono edit() { + return ScheduledEventEditMono.of(this); + } - //TODO: edit + /** + * Requests to edit this scheduled event with the provided spec. + * + * @param spec the parameters to update + * @return A {@link Mono} which, upon completion, emits the new {@link ScheduledEvent event} update. If an error + * occurs, it is emitted through the {@link Mono}. + */ + public Mono edit(ScheduledEventEditSpec spec) { + Objects.requireNonNull(spec); + return Mono.defer(() -> gateway.getRestClient().getScheduledEventById(getGuildId(), getId()).edit(spec.asRequest(), spec.reason()) + .map(data -> new ScheduledEvent(gateway, data))); + } + + /** + * Requests to delete this event with the provided reason. + * + * @param reason the reason explaining why this event is being deleted + * @return A {@link Mono} which completes when the event is deleted. + */ + public Mono delete(@Nullable String reason) { + return gateway.getRestClient().getScheduledEventById(getGuildId(), getId()) + .delete(reason); + } - //TODO: delete + /** + * Requests to delete this event. + * + * @return A {@link Mono} which completes when the event is deleted. + */ + public Mono delete() { + return delete(null); + } /** * Represents a scheduled event's privacy level. @@ -234,8 +330,10 @@ public int getValue() { public static PrivacyLevel of(int value) { switch (value) { - case 2: return GUILD_ONLY; - default: return UNKNOWN; + case 2: + return GUILD_ONLY; + default: + return UNKNOWN; } } } @@ -261,10 +359,14 @@ public int getValue() { public static EntityType of(int value) { switch (value) { - case 1: return STAGE_INSTANCE; - case 2: return VOICE; - case 3: return EXTERNAL; - default: return UNKNOWN; + case 1: + return STAGE_INSTANCE; + case 2: + return VOICE; + case 3: + return EXTERNAL; + default: + return UNKNOWN; } } } @@ -291,11 +393,16 @@ public int getValue() { public static Status of(int value) { switch (value) { - case 1: return SCHEDULED; - case 2: return ACTIVE; - case 3: return COMPLETED; - case 4: return CANCELED; - default: return UNKNOWN; + case 1: + return SCHEDULED; + case 2: + return ACTIVE; + case 3: + return COMPLETED; + case 4: + return CANCELED; + default: + return UNKNOWN; } } } diff --git a/core/src/main/java/discord4j/core/retriever/FallbackEntityRetriever.java b/core/src/main/java/discord4j/core/retriever/FallbackEntityRetriever.java index baa84643b..09d07877b 100644 --- a/core/src/main/java/discord4j/core/retriever/FallbackEntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/FallbackEntityRetriever.java @@ -16,14 +16,9 @@ */ package discord4j.core.retriever; +import discord4j.core.object.ScheduledEventUser; import discord4j.core.object.automod.AutoModRule; -import discord4j.core.object.entity.Guild; -import discord4j.core.object.entity.GuildEmoji; -import discord4j.core.object.entity.GuildSticker; -import discord4j.core.object.entity.Member; -import discord4j.core.object.entity.Message; -import discord4j.core.object.entity.Role; -import discord4j.core.object.entity.User; +import discord4j.core.object.entity.*; import discord4j.core.object.entity.channel.Channel; import discord4j.core.object.entity.channel.GuildChannel; import discord4j.common.util.Snowflake; @@ -124,4 +119,19 @@ public Flux getGuildStickers(Snowflake guildId) { public Flux getGuildAutoModRules(Snowflake guildId) { return first.getGuildAutoModRules(guildId).switchIfEmpty(fallback.getGuildAutoModRules(guildId)); } + + @Override + public Mono getScheduledEventById(Snowflake guildId, Snowflake eventId) { + return first.getScheduledEventById(guildId, eventId).switchIfEmpty(fallback.getScheduledEventById(guildId, eventId)); + } + + @Override + public Flux getScheduledEvents(Snowflake guildId) { + return first.getScheduledEvents(guildId).switchIfEmpty(fallback.getScheduledEvents(guildId)); + } + + @Override + public Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId) { + return first.getScheduledEventUsers(guildId, eventId).switchIfEmpty(fallback.getScheduledEventUsers(guildId, eventId)); + } } diff --git a/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java b/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java index e22b9471f..3d61c8101 100644 --- a/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/StoreEntityRetriever.java @@ -27,6 +27,8 @@ import discord4j.core.object.entity.channel.Channel; import discord4j.core.object.entity.channel.GuildChannel; import discord4j.core.util.EntityUtil; +import discord4j.discordjson.Id; +import discord4j.discordjson.json.GuildScheduledEventUserData; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -148,21 +150,25 @@ public Flux getGuildAutoModRules(Snowflake guildId) { .map(data -> new AutoModRule(gateway, data)); } - //TODO: Implement @Override public Mono getScheduledEventById(Snowflake guildId, Snowflake eventId) { - return null; + return Mono.from(store.execute(ReadActions.getScheduledEventById(guildId.asLong(), eventId.asLong()))) + .map(data -> new ScheduledEvent(gateway, data)); } - //TODO: Implement @Override public Flux getScheduledEvents(Snowflake guildId) { - return null; + return Flux.from(store.execute(ReadActions.getScheduledEventsInGuild(guildId.asLong()))) + .map(data -> new ScheduledEvent(gateway, data)); } - //TODO: Implement @Override public Flux getScheduledEventUsers(Snowflake guildId, Snowflake eventId) { - return null; + return Flux.from(store.execute(ReadActions.getScheduledEventsUsers(guildId.asLong(), eventId.asLong()))) + .flatMap(id -> Mono.from(store.execute(ReadActions.getUserById(id.asLong())))) + .map(userData -> new ScheduledEventUser(gateway, GuildScheduledEventUserData.builder() + .user(userData) + .guildScheduledEventId(eventId.asLong()) + .build(), guildId)); } } diff --git a/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java b/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java new file mode 100644 index 000000000..025b416af --- /dev/null +++ b/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java @@ -0,0 +1,66 @@ +package discord4j.core.spec; + +import discord4j.common.util.Snowflake; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.discordjson.Id; +import discord4j.discordjson.json.GuildScheduledEventCreateRequest; +import discord4j.discordjson.possible.Possible; +import org.immutables.value.Value; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.Mono; + +import java.time.Instant; + +import static discord4j.core.spec.InternalSpecUtils.mapPossible; + +@Value.Immutable +public interface ScheduledEventCreateSpecGenerator extends AuditSpec { + + /* Possible for events with entity type external */ + Possible channelId(); + + Possible entityMetadata(); + + String name(); + + int privacyLevel(); + + Instant scheduledStartTime(); + + Possible scheduledEndTime(); + + Possible description(); + + int entityType(); + + //TODO Add image support + + @Override + default GuildScheduledEventCreateRequest asRequest() { + return GuildScheduledEventCreateRequest.builder() + .channelId(mapPossible(channelId(), snowflake -> Id.of(snowflake.asLong()))) + .entityMetadata(mapPossible(entityMetadata(), ScheduledEventEntityMetadataSpecGenerator::asRequest)) + .name(name()) + .privacyLevel(privacyLevel()) + .scheduledStartTime(scheduledStartTime()) + .scheduledEndTime(scheduledEndTime()) + .description(description()) + .entityType(entityType()) + .build(); + } +} + +@Value.Immutable(builder = false) +abstract class ScheduledEventCreateMonoGenerator extends Mono implements ScheduledEventCreateSpecGenerator { + + abstract Guild guild(); + + @Override + public void subscribe(CoreSubscriber actual) { + guild().createScheduledEvent(ScheduledEventCreateSpec.copyOf(this)).subscribe(actual); + } + + @Override + public abstract String toString(); +} diff --git a/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java b/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java new file mode 100644 index 000000000..39fba80b2 --- /dev/null +++ b/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java @@ -0,0 +1,66 @@ +package discord4j.core.spec; + +import discord4j.common.util.Snowflake; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.discordjson.Id; +import discord4j.discordjson.json.GuildScheduledEventModifyRequest; +import discord4j.discordjson.possible.Possible; +import org.immutables.value.Value; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.Mono; + +import java.time.Instant; +import java.util.Optional; + +import static discord4j.core.spec.InternalSpecUtils.mapPossible; + +@Value.Immutable +public interface ScheduledEventEditSpecGenerator extends AuditSpec { + + /* Possible for events with entity type external */ + Possible> channelId(); + + Possible entityMetadata(); + + Possible name(); + + Possible privacyLevel(); + + Possible scheduledStartTime(); + + Possible scheduledEndTime(); + + Possible description(); + + Possible entityType(); + + //TODO Add image support + + @Override + default GuildScheduledEventModifyRequest asRequest() { + return GuildScheduledEventModifyRequest.builder() + .channelId(mapPossible(channelId(), optional -> optional.map(snowflake -> Id.of(snowflake.asLong())))) + .entityMetadata(mapPossible(entityMetadata(), ScheduledEventEntityMetadataSpecGenerator::asRequest)) + .name(name()) + .privacyLevel(privacyLevel()) + .scheduledStartTime(scheduledStartTime()) + .scheduledEndTime(scheduledEndTime()) + .description(description()) + .entityType(entityType()) + .build(); + } +} + +@Value.Immutable(builder = false) +abstract class ScheduledEventEditMonoGenerator extends Mono implements ScheduledEventEditSpecGenerator { + + abstract ScheduledEvent event(); + + @Override + public void subscribe(CoreSubscriber actual) { + event().edit(ScheduledEventEditSpec.copyOf(this)).subscribe(actual); + } + + @Override + public abstract String toString(); +} diff --git a/core/src/main/java/discord4j/core/spec/ScheduledEventEntityMetadataSpecGenerator.java b/core/src/main/java/discord4j/core/spec/ScheduledEventEntityMetadataSpecGenerator.java new file mode 100644 index 000000000..01086be6d --- /dev/null +++ b/core/src/main/java/discord4j/core/spec/ScheduledEventEntityMetadataSpecGenerator.java @@ -0,0 +1,18 @@ +package discord4j.core.spec; + +import discord4j.discordjson.json.GuildScheduledEventEntityMetadataData; +import discord4j.discordjson.possible.Possible; +import org.immutables.value.Value; + +@Value.Immutable +public interface ScheduledEventEntityMetadataSpecGenerator extends Spec { + + Possible location(); + + @Override + default GuildScheduledEventEntityMetadataData asRequest() { + return GuildScheduledEventEntityMetadataData.builder() + .location(location()) + .build(); + } +} diff --git a/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java b/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java index a00c8b8ae..176dec1f0 100644 --- a/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java +++ b/gateway/src/main/java/discord4j/gateway/state/DispatchStoreLayer.java @@ -61,6 +61,11 @@ public class DispatchStoreLayer { add(GuildRoleCreate.class::isInstance, GatewayActions::guildRoleCreate); add(GuildRoleDelete.class::isInstance, GatewayActions::guildRoleDelete); add(GuildRoleUpdate.class::isInstance, GatewayActions::guildRoleUpdate); + add(GuildScheduledEventCreate.class::isInstance, GatewayActions::guildScheduledEventCreate); + add(GuildScheduledEventUpdate.class::isInstance, GatewayActions::guildScheduledEventUpdate); + add(GuildScheduledEventDelete.class::isInstance, GatewayActions::guildScheduledEventDelete); + add(GuildScheduledEventUserAdd.class::isInstance, GatewayActions::guildScheduledEventUserAdd); + add(GuildScheduledEventUserRemove.class::isInstance, GatewayActions::guildScheduledEventUserRemove); add(GuildUpdate.class::isInstance, GatewayActions::guildUpdate); add(MessageCreate.class::isInstance, GatewayActions::messageCreate); add(MessageDelete.class::isInstance, GatewayActions::messageDelete); @@ -74,11 +79,6 @@ public class DispatchStoreLayer { add(Ready.class::isInstance, (Integer shard, Ready dispatch) -> GatewayActions.ready(dispatch)); add(UserUpdate.class::isInstance, GatewayActions::userUpdate); add(VoiceStateUpdateDispatch.class::isInstance, GatewayActions::voiceStateUpdateDispatch); - add(GuildScheduledEventCreate.class::isInstance, GatewayActions::guildScheduledEventCreate); - add(GuildScheduledEventUpdate.class::isInstance, GatewayActions::guildScheduledEventUpdate); - add(GuildScheduledEventDelete.class::isInstance, GatewayActions::guildScheduledEventDelete); - add(GuildScheduledEventUserAdd.class::isInstance, GatewayActions::guildScheduledEventUserAdd); - add(GuildScheduledEventUserRemove.class::isInstance, GatewayActions::guildScheduledEventUserRemove); add(dispatch -> dispatch instanceof GatewayStateChange && ((GatewayStateChange) dispatch).getState() == GatewayStateChange.State.DISCONNECTED, (shard, dispatch) -> GatewayActions.invalidateShard(shard, InvalidationCause.LOGOUT)); From fb284fb3ab86bc23a0057bf22b3c11e296456b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Tue, 11 Apr 2023 13:39:38 +0200 Subject: [PATCH 08/21] fix(timestamp serializing): add jackson jsr310 implementation and configure instant serialization --- build.gradle | 2 ++ common/src/main/java/discord4j/common/JacksonResources.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 1d2d0dfd1..bc60456ff 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ ext { // Dependencies reactor_bom_version = '2020.0.30' jackson_databind_version = '2.12.7.1' + jackson_datatype_jsr310_version = '2.14.2' jackson_datatype_jdk8_version = '2.12.7' caffeine_version = '2.8.8' immutables_group = 'org.immutables' @@ -58,6 +59,7 @@ allprojects { dependencies { api platform("io.projectreactor:reactor-bom:$reactor_bom_version") + api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_datatype_jsr310_version" api "com.discord4j:discord-json:$discordJsonVersion" } diff --git a/common/src/main/java/discord4j/common/JacksonResources.java b/common/src/main/java/discord4j/common/JacksonResources.java index 005153944..4f6e4346e 100644 --- a/common/src/main/java/discord4j/common/JacksonResources.java +++ b/common/src/main/java/discord4j/common/JacksonResources.java @@ -21,7 +21,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import discord4j.common.jackson.UnknownPropertyHandler; import discord4j.discordjson.possible.PossibleFilter; import discord4j.discordjson.possible.PossibleModule; @@ -39,6 +41,8 @@ public class JacksonResources { */ public static final Function INITIALIZER = mapper -> mapper .registerModule(new PossibleModule()) + .registerModule(new JavaTimeModule()) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .registerModule(new Jdk8Module()) .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.PUBLIC_ONLY) From 49ddcfbbe06b70d0859ad49b9eede139ada23ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Tue, 11 Apr 2023 13:40:05 +0200 Subject: [PATCH 09/21] doc(guild event): add example bot for guild scheduled events --- .../core/ExampleGuildScheduledEvents.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java diff --git a/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java new file mode 100644 index 000000000..cdfae183c --- /dev/null +++ b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java @@ -0,0 +1,55 @@ +package discord4j.core; + +import discord4j.common.util.Snowflake; +import discord4j.core.event.domain.lifecycle.ReadyEvent; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.core.spec.ScheduledEventCreateSpec; +import discord4j.core.spec.ScheduledEventEntityMetadataSpec; + +import java.time.Duration; +import java.time.Instant; + +/** + * Example bot creating and updating a guild scheduled event + * Requires token and guildId environment variables to be set to work properly + */ +public class ExampleGuildScheduledEvents { + + private static final String token = System.getenv("token"); + private static final long guildId = Long.parseLong(System.getenv("guildId")); + + public static final String EVENT_NAME = "Guild event test", + EVENT_DESCRIPTION = "This is a test event from a bot using Discord4J.", + EVENT_LOCATION = "Somewhere, probably still on Earth"; + + public static void main(String[] args) { + DiscordClient.create(token) + .withGateway(client -> client.on(ReadyEvent.class) + // Fetch the given guild + .flatMap(ignored -> client.getGuildById(Snowflake.of(guildId))) + // Create the event + .flatMap(guild -> guild.createScheduledEvent(ScheduledEventCreateSpec.builder() + .name(EVENT_NAME) + .description(EVENT_DESCRIPTION) + .privacyLevel(ScheduledEvent.PrivacyLevel.GUILD_ONLY.getValue()) + .entityType(3) + .entityMetadata(ScheduledEventEntityMetadataSpec.builder() + .location(EVENT_LOCATION) + .build()) + .scheduledStartTime(Instant.now().plusSeconds(3600)) // Start in one hour + .scheduledEndTime(Instant.now().plusSeconds(7200)) // End in two hours (required for external events) + .build() + )) + // Update the event + .flatMap(event -> event.edit().withName("Edited " + EVENT_NAME)) + // Advertise our event + .doOnNext(event -> System.out.format("The created event ID is %d\n", event.getId().asLong())) + // Wait a minute before deleting it + .delayElements(Duration.ofMinutes(1)) + // Delete it + .flatMap(ScheduledEvent::delete) + ) + .block(); + } + +} From 4431d4a4f95516bdcb482b275907ccb9bed58132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 17 Apr 2023 11:39:52 +0200 Subject: [PATCH 10/21] fix(serialization): use Set instead of Set for guild event users storing in LegacyStoreLayout --- .../common/store/legacy/LegacyStoreLayout.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java index bf4dcf3c0..61acc72d3 100644 --- a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java +++ b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java @@ -245,7 +245,8 @@ public Mono getScheduledEventById(long guildId, long ev @Override public Flux getScheduledEventUsersInEvent(long guildId, long eventId) { return stateHolder.getGuildEventsUsersStore().find(LongLongTuple2.of(guildId, eventId)) - .flatMapIterable(list -> list); + .flatMapIterable(list -> list) + .map(Id::of); } @Override @@ -954,9 +955,9 @@ public Mono onGuildScheduledEventUserAdd(int shardIndex, GuildScheduledEve LongLongTuple2 key = LongLongTuple2.of(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); return stateHolder.getGuildEventsUsersStore().find(key) - .defaultIfEmpty(new HashSet()) + .defaultIfEmpty(new HashSet()) .map(set -> { - set.add(dispatch.userId()); + set.add(dispatch.userId().asLong()); return set; }) .flatMap(set -> stateHolder.getGuildEventsUsersStore().save(key, set)); @@ -967,9 +968,9 @@ public Mono onGuildScheduledEventUserRemove(int shardIndex, GuildScheduled LongLongTuple2 key = LongLongTuple2.of(dispatch.guildId().asLong(), dispatch.scheduledEventId().asLong()); return stateHolder.getGuildEventsUsersStore().find(key) - .defaultIfEmpty(new HashSet()) + .defaultIfEmpty(new HashSet()) .map(set -> { - set.remove(dispatch.userId()); + set.remove(dispatch.userId().asLong()); return set; }) .flatMap(set -> stateHolder.getGuildEventsUsersStore().save(key, set)); From 84484c3beb358a30993b89d8f6782d5540e9bcd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 17 Apr 2023 11:43:08 +0200 Subject: [PATCH 11/21] refactor(data accessor): use default methods for new members to ease migraiton --- .../common/store/api/layout/DataAccessor.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java index 1dbf97611..0aa57b089 100644 --- a/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java +++ b/common/src/main/java/discord4j/common/store/api/layout/DataAccessor.java @@ -287,7 +287,9 @@ default Mono getStickerById(long guildId, long stickerId) { * @param guildId the guild ID * @return A {@link Flux} emitting the scheduled events, or empty if none is present */ - Flux getScheduledEventsInGuild(long guildId); + default Flux getScheduledEventsInGuild(long guildId) { + return Flux.empty(); + } /** * Retrieves data for the guild scheduled event corresponding to the given guild ID and event ID. @@ -296,7 +298,9 @@ default Mono getStickerById(long guildId, long stickerId) { * @param eventId the event ID * @return A {@link Mono} emitting the scheduled event, or empty if not found */ - Mono getScheduledEventById(long guildId, long eventId); + default Mono getScheduledEventById(long guildId, long eventId) { + return Mono.empty(); + } /** * Retrieves data for all guild scheduled event users corresponding to the given guild ID and event ID. @@ -305,7 +309,9 @@ default Mono getStickerById(long guildId, long stickerId) { * @param eventId the event ID * @return A {@link Flux} emitting the scheduled event users ids, or empty if none is present */ - Flux getScheduledEventUsersInEvent(long guildId, long eventId); + default Flux getScheduledEventUsersInEvent(long guildId, long eventId) { + return Flux.empty(); + } /** * Retrieves data for all members present in the store. From 0911562c1bc16ebd8a568d5bd74b7d1a05300e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 17 Apr 2023 11:44:39 +0200 Subject: [PATCH 12/21] doc(license): add missing license header in some files --- .../GuildScheduledEventCreateAction.java | 17 +++++++++++++++++ .../GuildScheduledEventDeleteAction.java | 17 +++++++++++++++++ .../GuildScheduledEventUpdateAction.java | 17 +++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java index c1efe7a8d..00f5a2cef 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventCreateAction.java @@ -1,3 +1,20 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + package discord4j.common.store.action.gateway; import discord4j.discordjson.json.gateway.GuildScheduledEventCreate; diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java index 0dd8284bf..7cc9d5fcd 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventDeleteAction.java @@ -1,3 +1,20 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + package discord4j.common.store.action.gateway; import discord4j.discordjson.json.GuildScheduledEventData; diff --git a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java index 8f534cb4f..5587e37c1 100644 --- a/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java +++ b/common/src/main/java/discord4j/common/store/action/gateway/GuildScheduledEventUpdateAction.java @@ -1,3 +1,20 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + package discord4j.common.store.action.gateway; import discord4j.discordjson.json.GuildScheduledEventData; From 6ba68431350f88efc26a56884be65cff88aaad65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 17 Apr 2023 12:10:18 +0200 Subject: [PATCH 13/21] fix(type inference): fix type inference oddities in LegacyStoreLayout This refactoring uses an explicit cast to avoid a build error regarding type inference on the Long class in involved method. --- .../java/discord4j/common/store/legacy/LegacyStoreLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java index 61acc72d3..a5fa6f7bc 100644 --- a/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java +++ b/common/src/main/java/discord4j/common/store/legacy/LegacyStoreLayout.java @@ -245,8 +245,8 @@ public Mono getScheduledEventById(long guildId, long ev @Override public Flux getScheduledEventUsersInEvent(long guildId, long eventId) { return stateHolder.getGuildEventsUsersStore().find(LongLongTuple2.of(guildId, eventId)) - .flatMapIterable(list -> list) - .map(Id::of); + .flatMapIterable(list -> list) + .map(value -> Id.of((Long) value)); } @Override From 7179a0299f9be3ab7b0828166bfefc3603d31cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Widmer?= Date: Mon, 17 Apr 2023 19:39:54 +0200 Subject: [PATCH 14/21] doc(invalid tag): fix invalid tag usage in ScheduledEvent --- .../main/java/discord4j/core/object/entity/ScheduledEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java index ecaa06a30..2f3e67dc6 100644 --- a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java +++ b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java @@ -208,7 +208,7 @@ public Optional getEntityMetadata() { /** * Gets the location of the event, if present. *

    - * Note: This location is pulled from {@link #getEntityMetadata().getLocation()} if present. + * Note: This location is pulled from {@link ScheduledEventEntityMetadata#getLocation} if present. * * @return The location of the event, if present. */ From 28bd5f548b6b89feab9130b7fe38f1e9337cdf25 Mon Sep 17 00:00:00 2001 From: quanticc Date: Mon, 8 May 2023 00:44:27 -0400 Subject: [PATCH 15/21] Use discord-json snapshots --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7180eb7bb..819c10791 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.caching=true systemProp.org.gradle.internal.publish.checksums.insecure=true version=3.2.5-SNAPSHOT -discordJsonVersion=1.6.14 +discordJsonVersion=1.6.15-SNAPSHOT storesVersion=3.2.2 From 649a7ae81f4af43eca300a3b1b775bd6b713fdc6 Mon Sep 17 00:00:00 2001 From: quanticc Date: Mon, 8 May 2023 00:45:22 -0400 Subject: [PATCH 16/21] Remove all-args Guild#createScheduledEvent method --- .../java/discord4j/core/object/entity/Guild.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/src/main/java/discord4j/core/object/entity/Guild.java b/core/src/main/java/discord4j/core/object/entity/Guild.java index f0740ecdc..462661f12 100644 --- a/core/src/main/java/discord4j/core/object/entity/Guild.java +++ b/core/src/main/java/discord4j/core/object/entity/Guild.java @@ -1943,19 +1943,6 @@ public Mono createScheduledEvent(ScheduledEventCreateSpec spec) .map(data -> new ScheduledEvent(gateway, data)); } - /** - * Requests to create a guild scheduled event with the provided mandatory parameters - * - * @param name The name of the scheduled event - * @param privacyLevel the {@link discord4j.core.object.entity.ScheduledEvent.PrivacyLevel} to set - * @param scheduledStartTime the scheduled start time of the event - * @param entityType the {@link discord4j.core.object.entity.ScheduledEvent.EntityType} where the event will take place - * @return A {@link ScheduledEventCreateMono} which, upon completion, returns the created {@link ScheduledEvent} - */ - public ScheduledEventCreateMono createScheduledEvent(String name, int privacyLevel, Instant scheduledStartTime, int entityType) { - return ScheduledEventCreateMono.of(name, privacyLevel, scheduledStartTime, entityType, this); - } - @Override public boolean equals(@Nullable final Object obj) { return EntityUtil.equals(this, obj); From 0a61eeda3531de25d45f690b2a085f15cc4e8e32 Mon Sep 17 00:00:00 2001 From: quanticc Date: Mon, 8 May 2023 00:45:52 -0400 Subject: [PATCH 17/21] Update generators to include enum type as parameter --- .../core/spec/ScheduledEventCreateSpecGenerator.java | 8 ++++---- .../core/spec/ScheduledEventEditSpecGenerator.java | 8 ++++---- .../java/discord4j/core/ExampleGuildScheduledEvents.java | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java b/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java index 025b416af..6da651dd6 100644 --- a/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java +++ b/core/src/main/java/discord4j/core/spec/ScheduledEventCreateSpecGenerator.java @@ -24,7 +24,7 @@ public interface ScheduledEventCreateSpecGenerator extends AuditSpec description(); - int entityType(); + ScheduledEvent.EntityType entityType(); //TODO Add image support @@ -42,11 +42,11 @@ default GuildScheduledEventCreateRequest asRequest() { .channelId(mapPossible(channelId(), snowflake -> Id.of(snowflake.asLong()))) .entityMetadata(mapPossible(entityMetadata(), ScheduledEventEntityMetadataSpecGenerator::asRequest)) .name(name()) - .privacyLevel(privacyLevel()) + .privacyLevel(privacyLevel().getValue()) .scheduledStartTime(scheduledStartTime()) .scheduledEndTime(scheduledEndTime()) .description(description()) - .entityType(entityType()) + .entityType(entityType().getValue()) .build(); } } diff --git a/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java b/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java index 39fba80b2..59e3b979d 100644 --- a/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java +++ b/core/src/main/java/discord4j/core/spec/ScheduledEventEditSpecGenerator.java @@ -24,7 +24,7 @@ public interface ScheduledEventEditSpecGenerator extends AuditSpec name(); - Possible privacyLevel(); + Possible privacyLevel(); Possible scheduledStartTime(); @@ -32,7 +32,7 @@ public interface ScheduledEventEditSpecGenerator extends AuditSpec description(); - Possible entityType(); + Possible entityType(); //TODO Add image support @@ -42,11 +42,11 @@ default GuildScheduledEventModifyRequest asRequest() { .channelId(mapPossible(channelId(), optional -> optional.map(snowflake -> Id.of(snowflake.asLong())))) .entityMetadata(mapPossible(entityMetadata(), ScheduledEventEntityMetadataSpecGenerator::asRequest)) .name(name()) - .privacyLevel(privacyLevel()) + .privacyLevel(mapPossible(privacyLevel(), ScheduledEvent.PrivacyLevel::getValue)) .scheduledStartTime(scheduledStartTime()) .scheduledEndTime(scheduledEndTime()) .description(description()) - .entityType(entityType()) + .entityType(mapPossible(entityType(), ScheduledEvent.EntityType::getValue)) .build(); } } diff --git a/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java index cdfae183c..d480ad570 100644 --- a/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java +++ b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java @@ -31,8 +31,8 @@ public static void main(String[] args) { .flatMap(guild -> guild.createScheduledEvent(ScheduledEventCreateSpec.builder() .name(EVENT_NAME) .description(EVENT_DESCRIPTION) - .privacyLevel(ScheduledEvent.PrivacyLevel.GUILD_ONLY.getValue()) - .entityType(3) + .privacyLevel(ScheduledEvent.PrivacyLevel.GUILD_ONLY) + .entityType(ScheduledEvent.EntityType.EXTERNAL) .entityMetadata(ScheduledEventEntityMetadataSpec.builder() .location(EVENT_LOCATION) .build()) From bb4fef4baa1f51f6d93bb3e0e7744a39cec1601f Mon Sep 17 00:00:00 2001 From: quanticc Date: Mon, 8 May 2023 00:53:04 -0400 Subject: [PATCH 18/21] Rename interested to subscribed --- .../core/object/ScheduledEventUser.java | 8 +++---- .../core/object/entity/ScheduledEvent.java | 22 +++++++++---------- .../core/retriever/EntityRetriever.java | 2 +- .../java/discord4j/rest/entity/RestGuild.java | 4 ++-- .../rest/entity/RestScheduledEvent.java | 14 ++++++------ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/discord4j/core/object/ScheduledEventUser.java b/core/src/main/java/discord4j/core/object/ScheduledEventUser.java index 41a36c2ca..b26e53b3a 100644 --- a/core/src/main/java/discord4j/core/object/ScheduledEventUser.java +++ b/core/src/main/java/discord4j/core/object/ScheduledEventUser.java @@ -56,18 +56,18 @@ public GatewayDiscordClient getClient() { } /** - * Gets the ID of the event the user is interested in. + * Gets the ID of the event the user is subscribed to. * - * @return The ID of the event the user is interested in. + * @return The ID of the event the user is subscribed to. */ public Snowflake getEventId() { return Snowflake.of(data.guildScheduledEventId()); } /** - * Gets the {@link User} interested in the event. + * Gets the {@link User} subscribed to the event. * - * @return The {@code User} interested in the event. + * @return The {@code User} subscribed to the event. */ public User getUser() { return new User(gateway, data.user()); diff --git a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java index 2f3e67dc6..538bb66a4 100644 --- a/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java +++ b/core/src/main/java/discord4j/core/object/entity/ScheduledEvent.java @@ -217,22 +217,22 @@ public Optional getLocation() { } /** - * Gets the count of users who have said they are "interested" in the event, if present. + * Gets the count of users who have subscribed to the event, if present. * - * @return The count of users who have said they are "interested" in the event, if present. + * @return The count of users who have subscribed to the event, if present. */ - public Optional getInterestedUserCount() { + public Optional getSubscribedUserCount() { return data.userCount().toOptional(); } /** - * Requests to retrieve the interested users for this event. + * Requests to retrieve the users subscribed to this event. * * @param withMemberData requests to return member data along with user data * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after * the specified ID. If an error is received, it is emitted through the {@code Flux}. */ - public Flux getInterestedUsers(boolean withMemberData) { + public Flux getSubscribedUsers(boolean withMemberData) { Map queryParams = new HashMap<>(); queryParams.put("with_member", withMemberData); @@ -241,30 +241,30 @@ public Flux getInterestedUsers(boolean withMemberData) { } /** - * Requests to retrieve the interested users for this event

  • before
  • the given user ID. + * Requests to retrieve the users subscribed to this event
  • before
  • the given user ID. * * @param userId the ID of the
  • newest
  • user to retrieve * @param withMemberData requests to return member data along with user data * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after * the specified ID. If an error is received, it is emitted through the {@code Flux}. */ - public Flux getInterestedUsersBefore(Snowflake userId, boolean withMemberData) { + public Flux getSubscribedUsersBefore(Snowflake userId, boolean withMemberData) { return gateway.getRestClient().getScheduledEventById(getGuildId(), getId()) - .getInterestedUsersBefore(userId, withMemberData) + .getSubscribedUsersBefore(userId, withMemberData) .map(data -> new ScheduledEventUser(gateway, data, getGuildId())); } /** - * Requests to retrieve the interested users for this event
  • after
  • the given user ID. + * Requests to retrieve the users subscribed to this event
  • after
  • the given user ID. * * @param userId the ID of the
  • oldest
  • user to retrieve * @param withMemberData requests to return member data along with user data * @return A {@link Flux} that continually emits all {@link ScheduledEventUser users} after * the specified ID. If an error is received, it is emitted through the {@code Flux}. */ - public Flux getInterestedUsersAfter(Snowflake userId, boolean withMemberData) { + public Flux getSubscribedUsersAfter(Snowflake userId, boolean withMemberData) { return gateway.getRestClient().getScheduledEventById(getGuildId(), getId()) - .getInterestedUsersAfter(userId, withMemberData) + .getSubscribedUsersAfter(userId, withMemberData) .map(data -> new ScheduledEventUser(gateway, data, getGuildId())); } diff --git a/core/src/main/java/discord4j/core/retriever/EntityRetriever.java b/core/src/main/java/discord4j/core/retriever/EntityRetriever.java index 59710da3b..521094f42 100644 --- a/core/src/main/java/discord4j/core/retriever/EntityRetriever.java +++ b/core/src/main/java/discord4j/core/retriever/EntityRetriever.java @@ -215,7 +215,7 @@ public interface EntityRetriever { Flux getScheduledEvents(Snowflake guildId); /** - * Requests to retrieve the users that have RSVPed as "interested" to the event represented by the supplied IDs. + * Requests to retrieve the users that subscribed to the event represented by the supplied IDs. * * @return A {@link Flux} that continually emits the event's {@link ScheduledEventUser users}. * If an error is received, it is emitted through the {@code Flux}. diff --git a/rest/src/main/java/discord4j/rest/entity/RestGuild.java b/rest/src/main/java/discord4j/rest/entity/RestGuild.java index 24924c4e9..fafd09c5d 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestGuild.java +++ b/rest/src/main/java/discord4j/rest/entity/RestGuild.java @@ -344,7 +344,7 @@ public Flux getTemplates() { * Requests to retrieve the scheduled event under this guild. * * @param eventId The ID of the event - * @param withUserCount Whether to optionally include the number of "interested" users + * @param withUserCount Whether to optionally include the number of subscribed users * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData}. If an * error is received, it is emitted through the {@code Mono}. */ @@ -357,7 +357,7 @@ public Mono getScheduledEvent(Snowflake eventId, @Nulla /** * Requests to retrieve the scheduled events under this guild. * - * @param withUserCount Whether to optionally include the number of "interested" users for each event + * @param withUserCount Whether to optionally include the number of subscribed users for each event * @return A {@link Flux} that continually emits all the {@link GuildScheduledEventData} associated with this guild. * If an error is received, it is emitted through the {@code Flux}. */ diff --git a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java index 56abdfa32..87d1f304c 100644 --- a/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java +++ b/rest/src/main/java/discord4j/rest/entity/RestScheduledEvent.java @@ -117,14 +117,14 @@ public Mono delete(@Nullable final String reason) { } /** - * Request to retrieve all interested users before the specified ID. + * Request to retrieve all subscribed users before the specified ID. *

    * The returned {@code Flux} will emit items in reverse-chronological order (newest to oldest). It is * recommended to limit the emitted items by invoking either {@link Flux#takeWhile(Predicate)} (to retrieve IDs * within a specified range) or {@link Flux#take(long)} (to retrieve a specific amount of IDs). *

    * The following example will get all users from {@code userId} to {@code myOtherUserId}: - * {@code getInterestedUsersBefore(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) >= 0)} + * {@code getSubscribedUsersBefore(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) >= 0)} * * @param userId The ID of the newest user to retrieve. * @param withMember Whether to optionally include the member object in the returned data (if the user is a member). @@ -134,7 +134,7 @@ public Mono delete(@Nullable final String reason) { * * Get Guild Scheduled Event Users */ - public Flux getInterestedUsersBefore(Snowflake userId, @Nullable Boolean withMember) { + public Flux getSubscribedUsersBefore(Snowflake userId, @Nullable Boolean withMember) { Function, Flux> doRequest = params -> { Optional.ofNullable(withMember).ifPresent(value -> params.put("with_member", value)); return restClient.getGuildService().getScheduledEventUsers(guildId, id, params); @@ -143,14 +143,14 @@ public Flux getInterestedUsersBefore(Snowflake user } /** - * Request to retrieve all interested users after the specified ID. + * Request to retrieve all subscribed users after the specified ID. *

    * The returned {@code Flux} will emit items in chronological order (older to newest). It is recommended to limit * the emitted items by invoking either {@link Flux#takeWhile(Predicate)} (to retrieve IDs within a specified range) * or {@link Flux#take(long)} (to retrieve a specific amount of IDs). *

    * The following example will get all users from {@code userId} to {@code myOtherUserId}: - * {@code getInterestedUsersAfter(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) <= 0)} + * {@code getSubscribedUsersAfter(userId).takeWhile(user -> user.getId().compareTo(myOtherUserId) <= 0)} * * @param userId The ID of the oldest user to retrieve. * @param withMember Whether to optionally include the member object in the returned data (if the user is a member). @@ -160,7 +160,7 @@ public Flux getInterestedUsersBefore(Snowflake user * * Get Guild Scheduled Event Users */ - public Flux getInterestedUsersAfter(Snowflake userId, @Nullable Boolean withMember) { + public Flux getSubscribedUsersAfter(Snowflake userId, @Nullable Boolean withMember) { Function, Flux> doRequest = params -> { Optional.ofNullable(withMember).ifPresent(value -> params.put("with_member", value)); return restClient.getGuildService().getScheduledEventUsers(guildId, id, params); @@ -171,7 +171,7 @@ public Flux getInterestedUsersAfter(Snowflake userI /** * Retrieve this scheduled event's data upon subscription. * - * @param withUserCount Whether to optionally include the "interested" user count in the returned data. + * @param withUserCount Whether to optionally include the subscribed user count in the returned data. * @return A {@link Mono} where, upon successful completion, emits the {@link GuildScheduledEventData} belonging to * this scheduled event. If an error is received, it is emitted through the {@code Mono}. */ From df7d6c3ae6553d935f932dabfb3c56ab799a7fd9 Mon Sep 17 00:00:00 2001 From: quanticc Date: Mon, 8 May 2023 01:10:23 -0400 Subject: [PATCH 19/21] Add guild scheduled event payload event names for deserialization --- .../java/discord4j/gateway/json/dispatch/EventNames.java | 5 +++++ .../discord4j/gateway/json/jackson/PayloadDeserializer.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/gateway/src/main/java/discord4j/gateway/json/dispatch/EventNames.java b/gateway/src/main/java/discord4j/gateway/json/dispatch/EventNames.java index 06f053cf2..c9ba690fc 100644 --- a/gateway/src/main/java/discord4j/gateway/json/dispatch/EventNames.java +++ b/gateway/src/main/java/discord4j/gateway/json/dispatch/EventNames.java @@ -38,6 +38,11 @@ public abstract class EventNames { public static final String GUILD_ROLE_CREATE = "GUILD_ROLE_CREATE"; public static final String GUILD_ROLE_UPDATE = "GUILD_ROLE_UPDATE"; public static final String GUILD_ROLE_DELETE = "GUILD_ROLE_DELETE"; + public static final String GUILD_SCHEDULED_EVENT_CREATE = "GUILD_SCHEDULED_EVENT_CREATE"; + public static final String GUILD_SCHEDULED_EVENT_UPDATE = "GUILD_SCHEDULED_EVENT_UPDATE"; + public static final String GUILD_SCHEDULED_EVENT_DELETE = "GUILD_SCHEDULED_EVENT_DELETE"; + public static final String GUILD_SCHEDULED_EVENT_USER_ADD = "GUILD_SCHEDULED_EVENT_USER_ADD"; + public static final String GUILD_SCHEDULED_EVENT_USER_REMOVE = "GUILD_SCHEDULED_EVENT_USER_REMOVE"; public static final String MESSAGE_CREATE = "MESSAGE_CREATE"; public static final String MESSAGE_UPDATE = "MESSAGE_UPDATE"; public static final String MESSAGE_DELETE = "MESSAGE_DELETE"; diff --git a/gateway/src/main/java/discord4j/gateway/json/jackson/PayloadDeserializer.java b/gateway/src/main/java/discord4j/gateway/json/jackson/PayloadDeserializer.java index 14039be8d..4c82082c0 100644 --- a/gateway/src/main/java/discord4j/gateway/json/jackson/PayloadDeserializer.java +++ b/gateway/src/main/java/discord4j/gateway/json/jackson/PayloadDeserializer.java @@ -59,6 +59,11 @@ public class PayloadDeserializer extends StdDeserializer> { dispatchTypes.put(EventNames.GUILD_ROLE_CREATE, GuildRoleCreate.class); dispatchTypes.put(EventNames.GUILD_ROLE_UPDATE, GuildRoleUpdate.class); dispatchTypes.put(EventNames.GUILD_ROLE_DELETE, GuildRoleDelete.class); + dispatchTypes.put(EventNames.GUILD_SCHEDULED_EVENT_CREATE, GuildScheduledEventCreate.class); + dispatchTypes.put(EventNames.GUILD_SCHEDULED_EVENT_UPDATE, GuildScheduledEventUpdate.class); + dispatchTypes.put(EventNames.GUILD_SCHEDULED_EVENT_DELETE, GuildScheduledEventDelete.class); + dispatchTypes.put(EventNames.GUILD_SCHEDULED_EVENT_USER_ADD, GuildScheduledEventUserAdd.class); + dispatchTypes.put(EventNames.GUILD_SCHEDULED_EVENT_USER_REMOVE, GuildScheduledEventUserRemove.class); dispatchTypes.put(EventNames.MESSAGE_CREATE, MessageCreate.class); dispatchTypes.put(EventNames.MESSAGE_UPDATE, MessageUpdate.class); dispatchTypes.put(EventNames.MESSAGE_DELETE, MessageDelete.class); From a322eb5cc224143232a5895a2a61930f00875866 Mon Sep 17 00:00:00 2001 From: quanticc Date: Sun, 4 Jun 2023 21:26:54 -0400 Subject: [PATCH 20/21] Add remaining core events for scheduled events --- .../store/api/layout/GatewayDataUpdater.java | 4 +- .../core/event/ReactiveEventAdapter.java | 68 +++++++++- .../core/event/dispatch/DispatchHandlers.java | 6 +- .../event/dispatch/GuildDispatchHandlers.java | 43 ++++++ .../guild/ScheduledEventCreateEvent.java | 77 +++++++++++ .../guild/ScheduledEventDeleteEvent.java | 77 +++++++++++ .../guild/ScheduledEventUpdateEvent.java | 88 ++++++++++++ .../guild/ScheduledEventUserAddEvent.java | 125 ++++++++++++++++++ .../guild/ScheduledEventUserRemoveEvent.java | 125 ++++++++++++++++++ .../core/ExampleGuildScheduledEvents.java | 108 +++++++++++---- 10 files changed, 687 insertions(+), 34 deletions(-) create mode 100644 core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventCreateEvent.java create mode 100644 core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java create mode 100644 core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUpdateEvent.java create mode 100644 core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserAddEvent.java create mode 100644 core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserRemoveEvent.java diff --git a/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java b/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java index fd2a61e53..7aa6da8b0 100644 --- a/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java +++ b/common/src/main/java/discord4j/common/store/api/layout/GatewayDataUpdater.java @@ -217,7 +217,7 @@ public interface GatewayDataUpdater { Mono onGuildScheduledEventUpdate(int shardIndex, GuildScheduledEventUpdate dispatch); /** - * Updates the internal state of the store according to the {@link GuildScheduledEventCreate} gateway dispatch. This + * Updates the internal state of the store according to the {@link GuildScheduledEventDelete} gateway dispatch. This * will typically perform a delete operation on a related {@link GuildScheduledEventData} in the store, * if present. * @@ -240,7 +240,7 @@ public interface GatewayDataUpdater { Mono onGuildScheduledEventUserAdd(int shardIndex, GuildScheduledEventUserAdd dispatch); /** - * Updates the internal state of the store according to the {@link GuildScheduledEventUserAdd} gateway dispatch. + * Updates the internal state of the store according to the {@link GuildScheduledEventUserRemove} gateway dispatch. * This will typically perform a delete operation on a related {@link java.util.List} handling a relationship * between a {@link GuildScheduledEventData} and the provided {@link GuildScheduledEventUserData}. * diff --git a/core/src/main/java/discord4j/core/event/ReactiveEventAdapter.java b/core/src/main/java/discord4j/core/event/ReactiveEventAdapter.java index 38ed81ed4..c373d5a6a 100644 --- a/core/src/main/java/discord4j/core/event/ReactiveEventAdapter.java +++ b/core/src/main/java/discord4j/core/event/ReactiveEventAdapter.java @@ -780,7 +780,8 @@ public Publisher onInteractionCreate(InteractionCreateEvent event) { } /** - * Invoked when a user starts a deferrable interaction + * Invoked when a user starts a deferrable interaction. + * * @param event the event instance * @return a {@link Publisher} that completes when this listener has done processing the event, for example, * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. @@ -889,7 +890,8 @@ public Publisher onChatInputAutoCompleteInteraction(ChatInputAutoCompleteEven } /** - * Invoked when a user starts a modal supported interaction + * Invoked when a user starts a modal supported interaction. + * * @param event the event instance * @return a {@link Publisher} that completes when this listener has done processing the event, for example, * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. @@ -979,6 +981,63 @@ public Publisher onAutoModActionExecution(AutoModActionExecutedEvent event) { return Mono.empty(); } + // ================= Scheduled event methods ================= // + + /** + * Invoked when a scheduled event is created. + * + * @param event the event instance + * @return a {@link Publisher} that completes when this listener has done processing the event, for example, + * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. + */ + public Publisher onScheduledEventCreate(ScheduledEventCreateEvent event) { + return Mono.empty(); + } + + /** + * Invoked when a scheduled event is updated. + * + * @param event the event instance + * @return a {@link Publisher} that completes when this listener has done processing the event, for example, + * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. + */ + public Publisher onScheduledEventUpdate(ScheduledEventUpdateEvent event) { + return Mono.empty(); + } + + /** + * Invoked when a scheduled event is deleted. + * + * @param event the event instance + * @return a {@link Publisher} that completes when this listener has done processing the event, for example, + * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. + */ + public Publisher onScheduledEventDelete(ScheduledEventDeleteEvent event) { + return Mono.empty(); + } + + /** + * Invoked when a user subscribes to a scheduled event. + * + * @param event the event instance + * @return a {@link Publisher} that completes when this listener has done processing the event, for example, + * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. + */ + public Publisher onScheduledEventUserAdd(ScheduledEventUserAddEvent event) { + return Mono.empty(); + } + + /** + * Invoked when a user unsubscribes from a scheduled event. + * + * @param event the event instance + * @return a {@link Publisher} that completes when this listener has done processing the event, for example, + * returning any {@link Mono}, {@link Flux} or synchronous code using {@link Mono#fromRunnable(Runnable)}. + */ + public Publisher onScheduledEventUserRemove(ScheduledEventUserRemoveEvent event) { + return Mono.empty(); + } + // ================= Core methods ================= // /** @@ -1075,6 +1134,11 @@ public Publisher hookOnEvent(Event event) { if (event instanceof AutoModRuleUpdateEvent) compatibleHooks.add(onAutoModRuleUpdate((AutoModRuleUpdateEvent) event)); if (event instanceof AutoModRuleDeleteEvent) compatibleHooks.add(onAutoModRuleDelete((AutoModRuleDeleteEvent) event)); if (event instanceof AutoModActionExecutedEvent) compatibleHooks.add(onAutoModActionExecution((AutoModActionExecutedEvent) event)); + if (event instanceof ScheduledEventCreateEvent) compatibleHooks.add(onScheduledEventCreate((ScheduledEventCreateEvent) event)); + if (event instanceof ScheduledEventUpdateEvent) compatibleHooks.add(onScheduledEventUpdate((ScheduledEventUpdateEvent) event)); + if (event instanceof ScheduledEventDeleteEvent) compatibleHooks.add(onScheduledEventDelete((ScheduledEventDeleteEvent) event)); + if (event instanceof ScheduledEventUserAddEvent) compatibleHooks.add(onScheduledEventUserAdd((ScheduledEventUserAddEvent) event)); + if (event instanceof ScheduledEventUserRemoveEvent) compatibleHooks.add(onScheduledEventUserRemove((ScheduledEventUserRemoveEvent) event)); // @formatter:on return Mono.whenDelayError(compatibleHooks); } diff --git a/core/src/main/java/discord4j/core/event/dispatch/DispatchHandlers.java b/core/src/main/java/discord4j/core/event/dispatch/DispatchHandlers.java index 8df87c5d5..baab3cf4c 100644 --- a/core/src/main/java/discord4j/core/event/dispatch/DispatchHandlers.java +++ b/core/src/main/java/discord4j/core/event/dispatch/DispatchHandlers.java @@ -40,7 +40,6 @@ import discord4j.core.object.entity.Member; import discord4j.core.object.entity.User; import discord4j.core.object.presence.Presence; -import discord4j.discordjson.json.AuditLogEntryData; import discord4j.discordjson.json.PartialUserData; import discord4j.discordjson.json.PresenceData; import discord4j.discordjson.json.UserData; @@ -82,6 +81,11 @@ public class DispatchHandlers implements DispatchEventMapper { addHandler(GuildRoleCreate.class, GuildDispatchHandlers::guildRoleCreate); addHandler(GuildRoleDelete.class, GuildDispatchHandlers::guildRoleDelete); addHandler(GuildRoleUpdate.class, GuildDispatchHandlers::guildRoleUpdate); + addHandler(GuildScheduledEventCreate.class, GuildDispatchHandlers::scheduledEventCreate); + addHandler(GuildScheduledEventUpdate.class, GuildDispatchHandlers::scheduledEventUpdate); + addHandler(GuildScheduledEventDelete.class, GuildDispatchHandlers::scheduledEventDelete); + addHandler(GuildScheduledEventUserAdd.class, GuildDispatchHandlers::scheduledEventUserAdd); + addHandler(GuildScheduledEventUserRemove.class, GuildDispatchHandlers::scheduledEventUserRemove); addHandler(GuildUpdate.class, GuildDispatchHandlers::guildUpdate); addHandler(MessageCreate.class, MessageDispatchHandlers::messageCreate); addHandler(MessageDelete.class, MessageDispatchHandlers::messageDelete); diff --git a/core/src/main/java/discord4j/core/event/dispatch/GuildDispatchHandlers.java b/core/src/main/java/discord4j/core/event/dispatch/GuildDispatchHandlers.java index ab315e3cf..0cf0b25a1 100644 --- a/core/src/main/java/discord4j/core/event/dispatch/GuildDispatchHandlers.java +++ b/core/src/main/java/discord4j/core/event/dispatch/GuildDispatchHandlers.java @@ -20,6 +20,7 @@ import discord4j.common.util.Snowflake; import discord4j.core.GatewayDiscordClient; import discord4j.core.event.domain.guild.*; +import discord4j.core.event.domain.guild.ScheduledEventCreateEvent; import discord4j.core.event.domain.role.RoleCreateEvent; import discord4j.core.event.domain.role.RoleDeleteEvent; import discord4j.core.event.domain.role.RoleUpdateEvent; @@ -259,6 +260,48 @@ static Mono guildRoleUpdate(DispatchContext scheduledEventCreate(DispatchContext context) { + GatewayDiscordClient gateway = context.getGateway(); + GuildScheduledEventData payload = context.getDispatch().scheduledEvent(); + ScheduledEvent scheduledEvent = new ScheduledEvent(gateway, payload); + return Mono.just(new ScheduledEventCreateEvent(gateway, context.getShardInfo(), scheduledEvent)); + } + + static Mono scheduledEventUpdate(DispatchContext context) { + GatewayDiscordClient gateway = context.getGateway(); + GuildScheduledEventData payload = context.getDispatch().scheduledEvent(); + ScheduledEvent scheduledEvent = new ScheduledEvent(gateway, payload); + ScheduledEvent oldScheduledEvent = context.getOldState() + .map(data -> new ScheduledEvent(gateway, data)) + .orElse(null); + return Mono.just(new ScheduledEventUpdateEvent(gateway, context.getShardInfo(), scheduledEvent, oldScheduledEvent)); + } + + static Mono scheduledEventDelete(DispatchContext context) { + GatewayDiscordClient gateway = context.getGateway(); + GuildScheduledEventData payload = context.getDispatch().scheduledEvent(); + ScheduledEvent scheduledEvent = new ScheduledEvent(gateway, payload); + return Mono.just(new ScheduledEventDeleteEvent(gateway, context.getShardInfo(), scheduledEvent)); + } + + static Mono scheduledEventUserAdd(DispatchContext context) { + GatewayDiscordClient gateway = context.getGateway(); + GuildScheduledEventUserAdd dispatch = context.getDispatch(); + return Mono.just(new ScheduledEventUserAddEvent(gateway, context.getShardInfo(), + Snowflake.asLong(dispatch.guildId()), + Snowflake.asLong(dispatch.scheduledEventId()), + Snowflake.asLong(dispatch.userId()))); + } + + static Mono scheduledEventUserRemove(DispatchContext context) { + GatewayDiscordClient gateway = context.getGateway(); + GuildScheduledEventUserRemove dispatch = context.getDispatch(); + return Mono.just(new ScheduledEventUserRemoveEvent(gateway, context.getShardInfo(), + Snowflake.asLong(dispatch.guildId()), + Snowflake.asLong(dispatch.scheduledEventId()), + Snowflake.asLong(dispatch.userId()))); + } + static Mono guildUpdate(DispatchContext context) { GatewayDiscordClient gateway = context.getGateway(); diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventCreateEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventCreateEvent.java new file mode 100644 index 000000000..2ef7430a1 --- /dev/null +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventCreateEvent.java @@ -0,0 +1,77 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.event.domain.guild; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.gateway.ShardInfo; +import reactor.core.publisher.Mono; + +/** + * Dispatched when a guild scheduled event is created. + *

    + * This event is dispatched by Discord. + * + * @see Discord + */ +public class ScheduledEventCreateEvent extends GuildEvent { + + private final ScheduledEvent scheduledEvent; + + public ScheduledEventCreateEvent(GatewayDiscordClient gateway, ShardInfo shardInfo, ScheduledEvent scheduledEvent) { + super(gateway, shardInfo); + this.scheduledEvent = scheduledEvent; + } + + /** + * Return the {@link ScheduledEvent} associated with this create event. + * + * @return a created scheduled event + */ + public ScheduledEvent getScheduledEvent() { + return scheduledEvent; + } + + /** + * Gets the {@link Snowflake} ID of the {@link Guild} involved in the event. + * + * @return the ID of the {@link Guild}. + */ + public Snowflake getGuildId() { + return scheduledEvent.getGuildId(); + } + + /** + * Requests to retrieve the {@link Guild} where a scheduled event was created. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getGuild() { + return getClient().getGuildById(getGuildId()); + } + + @Override + public String toString() { + return "ScheduledEventCreateEvent{" + + "scheduledEvent=" + scheduledEvent + + "}"; + } +} diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java new file mode 100644 index 000000000..1778d570f --- /dev/null +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java @@ -0,0 +1,77 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.event.domain.guild; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.gateway.ShardInfo; +import reactor.core.publisher.Mono; + +/** + * Dispatched when a guild scheduled event is deleted. + *

    + * This event is dispatched by Discord. + * + * @see Discord + */ +public class ScheduledEventDeleteEvent extends GuildEvent { + + private final ScheduledEvent scheduledEvent; + + public ScheduledEventDeleteEvent(GatewayDiscordClient gateway, ShardInfo shardInfo, ScheduledEvent scheduledEvent) { + super(gateway, shardInfo); + this.scheduledEvent = scheduledEvent; + } + + /** + * Return the {@link ScheduledEvent} associated with this create event. + * + * @return a deleted scheduled event + */ + public ScheduledEvent getScheduledEvent() { + return scheduledEvent; + } + + /** + * Gets the {@link Snowflake} ID of the {@link Guild} involved in the event. + * + * @return the ID of the {@link Guild}. + */ + public Snowflake getGuildId() { + return scheduledEvent.getGuildId(); + } + + /** + * Requests to retrieve the {@link Guild} where a scheduled event was created. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getGuild() { + return getClient().getGuildById(getGuildId()); + } + + @Override + public String toString() { + return "ScheduledEventDeleteEvent{" + + "scheduledEvent=" + scheduledEvent + + "} " + super.toString(); + } +} diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUpdateEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUpdateEvent.java new file mode 100644 index 000000000..aa60cae31 --- /dev/null +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUpdateEvent.java @@ -0,0 +1,88 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.event.domain.guild; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.gateway.ShardInfo; +import reactor.core.publisher.Mono; +import reactor.util.annotation.Nullable; + +import java.util.Optional; + +public class ScheduledEventUpdateEvent extends GuildEvent { + + private final ScheduledEvent current; + @Nullable + private final ScheduledEvent old; + + public ScheduledEventUpdateEvent(GatewayDiscordClient gateway, ShardInfo shardInfo, ScheduledEvent current, + @Nullable ScheduledEvent old) { + super(gateway, shardInfo); + this.current = current; + this.old = old; + } + + /** + * Return the {@link ScheduledEvent} associated with this update event. + * + * @return an updated scheduled event + */ + public ScheduledEvent getCurrent() { + return current; + } + + /** + * Return the previous {@link ScheduledEvent} entity that was updated in this event. May not be present if the + * entity is not stored. + * + * @return a previous version of an updated scheduled event, if present + */ + public Optional getOld() { + return Optional.ofNullable(old); + } + + /** + * Gets the {@link Snowflake} ID of the {@link Guild} involved in the event. + * + * @return the ID of the {@link Guild}. + */ + public Snowflake getGuildId() { + return current.getGuildId(); + } + + /** + * Requests to retrieve the {@link Guild} where a scheduled event was updated. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getGuild() { + return getClient().getGuildById(getGuildId()); + } + + @Override + public String toString() { + return "ScheduledEventUpdateEvent{" + + "current=" + current + + ", old=" + old + + "}"; + } +} diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserAddEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserAddEvent.java new file mode 100644 index 000000000..327a8cc2a --- /dev/null +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserAddEvent.java @@ -0,0 +1,125 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.event.domain.guild; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.Member; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.core.object.entity.User; +import discord4j.gateway.ShardInfo; +import reactor.core.publisher.Mono; + +/** + * Dispatched when a user has subscribed to a guild scheduled event. + *

    + * This event is dispatched by Discord. + * + * @see Discord + */ +public class ScheduledEventUserAddEvent extends GuildEvent { + + private final long guildId; + private final long scheduledEventId; + private final long userId; + + public ScheduledEventUserAddEvent(GatewayDiscordClient gateway, ShardInfo shardInfo, + long guildId, long scheduledEventId, long userId) { + super(gateway, shardInfo); + this.guildId = guildId; + this.scheduledEventId = scheduledEventId; + this.userId = userId; + } + + /** + * Return the guild ID of the scheduled event. + * + * @return a guild snowflake + */ + public Snowflake getGuildId() { + return Snowflake.of(guildId); + } + + /** + * Return the guild of the scheduled event. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getGuild() { + return getClient().getGuildById(getGuildId()); + } + + /** + * Return the scheduled event ID. + * + * @return a scheduled event snowflake + */ + public Snowflake getScheduledEventId() { + return Snowflake.of(scheduledEventId); + } + + /** + * Return the scheduled event. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link ScheduledEvent} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getScheduledEvent() { + return getClient().getScheduledEventById(getGuildId(), getScheduledEventId()); + } + + /** + * Return the subscribing user ID. + * + * @return a user snowflake + */ + public Snowflake getUserId() { + return Snowflake.of(userId); + } + + /** + * Return the subscribing user. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link User} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getUser() { + return getClient().getUserById(getUserId()); + } + + /** + * Return the subscribing guild Member. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Member} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getMember() { + return getClient().getMemberById(getGuildId(), getUserId()); + } + + @Override + public String toString() { + return "ScheduledEventUserAddEvent{" + + "guildId=" + guildId + + ", scheduledEventId=" + scheduledEventId + + ", userId=" + userId + + "}"; + } +} diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserRemoveEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserRemoveEvent.java new file mode 100644 index 000000000..d322a93aa --- /dev/null +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventUserRemoveEvent.java @@ -0,0 +1,125 @@ +/* + * This file is part of Discord4J. + * + * Discord4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Discord4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Discord4J. If not, see . + */ + +package discord4j.core.event.domain.guild; + +import discord4j.common.util.Snowflake; +import discord4j.core.GatewayDiscordClient; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.Member; +import discord4j.core.object.entity.ScheduledEvent; +import discord4j.core.object.entity.User; +import discord4j.gateway.ShardInfo; +import reactor.core.publisher.Mono; + +/** + * Dispatched when a user has unsubscribed from a guild scheduled event. + *

    + * This event is dispatched by Discord. + * + * @see Discord + */ +public class ScheduledEventUserRemoveEvent extends GuildEvent { + + private final long guildId; + private final long scheduledEventId; + private final long userId; + + public ScheduledEventUserRemoveEvent(GatewayDiscordClient gateway, ShardInfo shardInfo, + long guildId, long scheduledEventId, long userId) { + super(gateway, shardInfo); + this.guildId = guildId; + this.scheduledEventId = scheduledEventId; + this.userId = userId; + } + + /** + * Return the guild ID of the scheduled event. + * + * @return a guild snowflake + */ + public Snowflake getGuildId() { + return Snowflake.of(guildId); + } + + /** + * Return the guild of the scheduled event. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getGuild() { + return getClient().getGuildById(getGuildId()); + } + + /** + * Return the scheduled event ID. + * + * @return a scheduled event snowflake + */ + public Snowflake getScheduledEventId() { + return Snowflake.of(scheduledEventId); + } + + /** + * Return the scheduled event. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link ScheduledEvent} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getScheduledEvent() { + return getClient().getScheduledEventById(getGuildId(), getScheduledEventId()); + } + + /** + * Return the unsubscribing user ID. + * + * @return a user snowflake + */ + public Snowflake getUserId() { + return Snowflake.of(userId); + } + + /** + * Return the unsubscribing user. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link User} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getUser() { + return getClient().getUserById(getUserId()); + } + + /** + * Return the unsubscribing guild Member. + * + * @return a {@link Mono} where, upon successful completion, emits the {@link Member} involved. + * If an error is received, it is emitted through the {@code Mono}. + */ + public Mono getMember() { + return getClient().getMemberById(getGuildId(), getUserId()); + } + + @Override + public String toString() { + return "ScheduledEventUserRemoveEvent{" + + "guildId=" + guildId + + ", scheduledEventId=" + scheduledEventId + + ", userId=" + userId + + "}"; + } +} diff --git a/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java index d480ad570..58addf1a4 100644 --- a/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java +++ b/core/src/test/java/discord4j/core/ExampleGuildScheduledEvents.java @@ -1,10 +1,16 @@ package discord4j.core; import discord4j.common.util.Snowflake; +import discord4j.core.event.ReactiveEventAdapter; +import discord4j.core.event.domain.guild.*; import discord4j.core.event.domain.lifecycle.ReadyEvent; import discord4j.core.object.entity.ScheduledEvent; import discord4j.core.spec.ScheduledEventCreateSpec; import discord4j.core.spec.ScheduledEventEntityMetadataSpec; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import reactor.util.Logger; +import reactor.util.Loggers; import java.time.Duration; import java.time.Instant; @@ -15,41 +21,85 @@ */ public class ExampleGuildScheduledEvents { + private static final Logger log = Loggers.getLogger(ExampleGuildScheduledEvents.class); + private static final String token = System.getenv("token"); private static final long guildId = Long.parseLong(System.getenv("guildId")); - public static final String EVENT_NAME = "Guild event test", - EVENT_DESCRIPTION = "This is a test event from a bot using Discord4J.", - EVENT_LOCATION = "Somewhere, probably still on Earth"; + public static final String EVENT_NAME = "Guild event test"; + public static final String EVENT_DESCRIPTION = "This is a test event from a bot using Discord4J."; + public static final String EVENT_LOCATION = "Somewhere, probably still on Earth"; public static void main(String[] args) { DiscordClient.create(token) - .withGateway(client -> client.on(ReadyEvent.class) - // Fetch the given guild - .flatMap(ignored -> client.getGuildById(Snowflake.of(guildId))) - // Create the event - .flatMap(guild -> guild.createScheduledEvent(ScheduledEventCreateSpec.builder() - .name(EVENT_NAME) - .description(EVENT_DESCRIPTION) - .privacyLevel(ScheduledEvent.PrivacyLevel.GUILD_ONLY) - .entityType(ScheduledEvent.EntityType.EXTERNAL) - .entityMetadata(ScheduledEventEntityMetadataSpec.builder() - .location(EVENT_LOCATION) - .build()) - .scheduledStartTime(Instant.now().plusSeconds(3600)) // Start in one hour - .scheduledEndTime(Instant.now().plusSeconds(7200)) // End in two hours (required for external events) - .build() - )) - // Update the event - .flatMap(event -> event.edit().withName("Edited " + EVENT_NAME)) - // Advertise our event - .doOnNext(event -> System.out.format("The created event ID is %d\n", event.getId().asLong())) - // Wait a minute before deleting it - .delayElements(Duration.ofMinutes(1)) - // Delete it - .flatMap(ScheduledEvent::delete) - ) - .block(); + .withGateway(client -> { + + // Logic to create a scheduled event on ready event + Publisher createOnReady = client.on(ReadyEvent.class) + // Fetch the given guild + .flatMap(ignored -> client.getGuildById(Snowflake.of(guildId))) + // Create the event + .flatMap(guild -> guild.createScheduledEvent(ScheduledEventCreateSpec.builder() + .name(EVENT_NAME) + .description(EVENT_DESCRIPTION) + .privacyLevel(ScheduledEvent.PrivacyLevel.GUILD_ONLY) + .entityType(ScheduledEvent.EntityType.EXTERNAL) + .entityMetadata(ScheduledEventEntityMetadataSpec.builder() + .location(EVENT_LOCATION) + .build()) + .scheduledStartTime(Instant.now().plusSeconds(3600)) // Start in one hour + .scheduledEndTime(Instant.now().plusSeconds(7200)) // End in two hours (required + // for external events) + .build() + )) + // Update the event + .flatMap(event -> event.edit().withName("Edited " + EVENT_NAME)) + // Advertise our event + .doOnNext(event -> System.out.format("The created event ID is %d\n", + event.getId().asLong())) + // Wait a minute before deleting it + .delayElements(Duration.ofMinutes(1)) + // Delete it + .flatMap(ScheduledEvent::delete); + + // New listeners + Publisher eventListeners = client.on(new ReactiveEventAdapter() { + @Override + public Publisher onScheduledEventCreate(ScheduledEventCreateEvent event) { + return Mono.fromRunnable(() -> log.info("Created event: {}", + event.getScheduledEvent().getData())); + } + + @Override + public Publisher onScheduledEventUpdate(ScheduledEventUpdateEvent event) { + return Mono.fromRunnable(() -> { + log.info("Old event: {}", event.getOld().map(ScheduledEvent::getData).orElse(null)); + log.info("Updated event: {}", event.getCurrent().getData()); + }); + } + + @Override + public Publisher onScheduledEventDelete(ScheduledEventDeleteEvent event) { + return Mono.fromRunnable(() -> log.info("Deleted event: {}", + event.getScheduledEvent().getData())); + } + + @Override + public Publisher onScheduledEventUserAdd(ScheduledEventUserAddEvent event) { + return event.getMember().doOnNext(member -> log.info("Subscribed: {}", member)); + } + + @Override + public Publisher onScheduledEventUserRemove(ScheduledEventUserRemoveEvent event) { + return event.getMember().doOnNext(member -> log.info("Unsubscribed: {}", member)); + } + }); + + // Join both publishers and return them to start a subscription on startup + // To avoid event race conditions ReadyEvent listeners must go first + return Mono.when(createOnReady, eventListeners); + }) + .block(); } } From 6f1b9f2ed61d751f54346dc4c3d0d3482169534a Mon Sep 17 00:00:00 2001 From: quanticc Date: Sun, 4 Jun 2023 21:30:37 -0400 Subject: [PATCH 21/21] Fix javadoc --- .../core/event/domain/guild/ScheduledEventDeleteEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java index 1778d570f..9c68f8a55 100644 --- a/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java +++ b/core/src/main/java/discord4j/core/event/domain/guild/ScheduledEventDeleteEvent.java @@ -41,7 +41,7 @@ public ScheduledEventDeleteEvent(GatewayDiscordClient gateway, ShardInfo shardIn } /** - * Return the {@link ScheduledEvent} associated with this create event. + * Return the {@link ScheduledEvent} associated with this delete event. * * @return a deleted scheduled event */ @@ -59,7 +59,7 @@ public Snowflake getGuildId() { } /** - * Requests to retrieve the {@link Guild} where a scheduled event was created. + * Requests to retrieve the {@link Guild} where a scheduled event was deleted. * * @return a {@link Mono} where, upon successful completion, emits the {@link Guild} involved. * If an error is received, it is emitted through the {@code Mono}. @@ -72,6 +72,6 @@ public Mono getGuild() { public String toString() { return "ScheduledEventDeleteEvent{" + "scheduledEvent=" + scheduledEvent + - "} " + super.toString(); + "}"; } }