From a853cbeee84a3972da4d8660193ae385b6875305 Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Thu, 4 Feb 2021 22:38:49 -0600 Subject: [PATCH] This should add ability to limit RSVPs --- .../client/module/command/RsvpCommand.java | 59 ++ .../discal/core/database/DatabaseManager.java | 12 +- .../discal/core/object/event/RsvpData.kt | 10 + .../db/migration/V11__Add_RSVP_Limit.sql | 2 + .../endpoints/v2/rsvp/UpdateRsvpEndpoint.java | 65 +- .../main/html/templates/docs/api/v2/rsvp.html | 909 +++++++++--------- 6 files changed, 580 insertions(+), 477 deletions(-) create mode 100644 core/src/main/resources/db/migration/V11__Add_RSVP_Limit.sql diff --git a/client/src/main/java/org/dreamexposure/discal/client/module/command/RsvpCommand.java b/client/src/main/java/org/dreamexposure/discal/client/module/command/RsvpCommand.java index d73f96a3..ccf37e92 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/module/command/RsvpCommand.java +++ b/client/src/main/java/org/dreamexposure/discal/client/module/command/RsvpCommand.java @@ -8,6 +8,7 @@ import org.dreamexposure.discal.core.object.command.CommandInfo; import org.dreamexposure.discal.core.object.event.RsvpData; import org.dreamexposure.discal.core.utils.EventUtils; import org.dreamexposure.discal.core.utils.GlobalConst; +import org.dreamexposure.discal.core.utils.PermissionChecker; import org.dreamexposure.discal.core.utils.TimeUtils; import org.dreamexposure.discal.core.utils.UserUtils; @@ -70,6 +71,7 @@ public class RsvpCommand implements Command { info.getSubCommands().put("unsure", "Marks that you may or may not go to event"); info.getSubCommands().put("remove", "Removes your RSVP from the event"); info.getSubCommands().put("list", "Lists who has RSVPed to event"); + info.getSubCommands().put("limit", "Sets the amount of people that can RSVP to the event, -1 to disable"); return info; } @@ -98,6 +100,14 @@ public class RsvpCommand implements Command { return this.moduleRemove(args, event, settings); case "list": return this.moduleList(args, event, settings); + case "limit": + return PermissionChecker.hasDisCalRole(event, settings) + .flatMap(has -> { + if (has) + return this.moduleLimit(args, event, settings); + else + return Messages.sendMessage(Messages.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); + }); default: return Messages.sendMessage(Messages.getMessage("Notification.Args.InvalidSubCmd", settings), event); } @@ -117,12 +127,15 @@ public class RsvpCommand implements Command { return TimeUtils.isInPast(eventId, settings).flatMap(inPast -> { if (!inPast) { return DatabaseManager.getRsvpData(settings.getGuildID(), eventId) + .filter(data -> data.hasRoom(mem.getId().asString())) .doOnNext(data -> data.removeCompletely(mem.getId().asString())) .doOnNext(data -> data.getGoingOnTime().add(mem.getId().asString())) .flatMap(data -> DatabaseManager.updateRsvpData(data) .then(this.getRsvpEmbed(data, settings)) .flatMap(embed -> Messages.sendMessage( Messages.getMessage("RSVP.going.success", settings), embed, event)) + ).switchIfEmpty(Messages.sendMessage("You cannot RSVP to the event as the" + + " max number of people are already attending", event) ); } else { return Messages @@ -151,12 +164,15 @@ public class RsvpCommand implements Command { return TimeUtils.isInPast(eventId, settings).flatMap(inPast -> { if (!inPast) { return DatabaseManager.getRsvpData(settings.getGuildID(), eventId) + .filter(data -> data.hasRoom(mem.getId().asString())) .doOnNext(data -> data.removeCompletely(mem.getId().asString())) .doOnNext(data -> data.getGoingLate().add(mem.getId().asString())) .flatMap(data -> DatabaseManager.updateRsvpData(data) .then(this.getRsvpEmbed(data, settings)) .flatMap(embed -> Messages.sendMessage( Messages.getMessage("RSVP.late.success", settings), embed, event)) + ).switchIfEmpty(Messages.sendMessage("You cannot RSVP to the event as the" + + " max number of people are already attending", event) ); } else { return Messages @@ -276,6 +292,45 @@ public class RsvpCommand implements Command { }).then(); } + //!rsvp limit + private Mono moduleLimit(String[] args, MessageCreateEvent event, GuildSettings settings) { + return Mono.defer(() -> { + if (args.length == 3) { + return Mono.just(args[1]).flatMap(eventId -> EventUtils.eventExists(settings, eventId) + .flatMap(exists -> { + if (exists) { + return TimeUtils.isInPast(eventId, settings).flatMap(inPast -> { + if (!inPast) { + //Okay, finally, we can change the limit + try { + int newLimit = Integer.parseInt(args[2]); + return DatabaseManager.getRsvpData(settings.getGuildID(), eventId) + .doOnNext(data -> data.setLimit(newLimit)) + .flatMap(data -> DatabaseManager.updateRsvpData(data) + .then(this.getRsvpEmbed(data, settings)) + .flatMap(embed -> Messages.sendMessage("RSVP Limit changed", embed, event)) + ); + } catch (NumberFormatException e) { + return Messages.sendMessage(Messages.getMessage("Notification.Args.Value" + + ".Integer", settings), event); + } + } else { + return Messages.sendMessage(Messages.getMessage("Notifications.Event.InPast", + settings), event); + } + }); + } else { + return Messages.sendMessage(Messages.getMessage("Notifications.Event.NotExist", settings), + event); + } + })); + } else { + return Messages.sendMessage("Limit command uses the following format: `!rsvp limit " + + "<#limit>", event); + } + }).then(); + } + private Mono moduleList(final String[] args, final MessageCreateEvent event, final GuildSettings settings) { return Mono.defer(() -> { @@ -315,6 +370,10 @@ public class RsvpCommand implements Command { spec.setTitle(Messages.getMessage("Embed.RSVP.List.Title", settings)); spec.addField("Event ID", data.getEventId(), false); + if (data.getLimit() > -1) + spec.addField("Max Respondents", data.getCurrentCount() + "/" + data.getLimit(), true); + else + spec.addField("Max Respondents", "Unlimited", true); final StringBuilder onTimeBuilder = new StringBuilder(); for (final Member u : onTime) onTimeBuilder.append(u.getDisplayName()).append(", "); diff --git a/core/src/main/java/org/dreamexposure/discal/core/database/DatabaseManager.java b/core/src/main/java/org/dreamexposure/discal/core/database/DatabaseManager.java index b269e324..bbf278a6 100644 --- a/core/src/main/java/org/dreamexposure/discal/core/database/DatabaseManager.java +++ b/core/src/main/java/org/dreamexposure/discal/core/database/DatabaseManager.java @@ -421,7 +421,8 @@ public class DatabaseManager { + " GOING_ON_TIME = ?," + " GOING_LATE = ?," + " NOT_GOING = ?," - + " UNDECIDED = ?" + + " UNDECIDED = ?," + + " 'LIMIT' = ?" + " WHERE EVENT_ID = ?"; return connect(master, c -> Mono.from(c.createStatement(update) @@ -430,15 +431,16 @@ public class DatabaseManager { .bind(2, data.getGoingLateString()) .bind(3, data.getNotGoingString()) .bind(4, data.getUndecidedString()) - .bind(5, data.getEventId()) + .bind(5, data.getLimit()) + .bind(6, data.getEventId()) .execute()) ).flatMap(res -> Mono.from(res.getRowsUpdated())) .thenReturn(true); } else { final String insertCommand = "INSERT INTO " + table + "(GUILD_ID, EVENT_ID, EVENT_END, GOING_ON_TIME, GOING_LATE, " + - "NOT_GOING, UNDECIDED)" + - " VALUES (?, ?, ?, ?, ?, ?, ?)"; + "NOT_GOING, UNDECIDED, `LIMIT`)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; return connect(master, c -> Mono.from(c.createStatement(insertCommand) .bind(0, data.getGuildId().asString()) @@ -448,6 +450,7 @@ public class DatabaseManager { .bind(4, data.getGoingLateString()) .bind(5, data.getNotGoingString()) .bind(6, data.getUndecidedString()) + .bind(7, data.getLimit()) .execute()) ).flatMap(res -> Mono.from(res.getRowsUpdated())) .thenReturn(true); @@ -699,6 +702,7 @@ public class DatabaseManager { data.setGoingLateFromString(row.get("GOING_LATE", String.class)); data.setNotGoingFromString(row.get("NOT_GOING", String.class)); data.setUndecidedFromString(row.get("UNDECIDED", String.class)); + data.setLimit(row.get("LIMIT", Integer.class)); return data; })) diff --git a/core/src/main/kotlin/org/dreamexposure/discal/core/object/event/RsvpData.kt b/core/src/main/kotlin/org/dreamexposure/discal/core/object/event/RsvpData.kt index 5eacc0fc..76371f01 100644 --- a/core/src/main/kotlin/org/dreamexposure/discal/core/object/event/RsvpData.kt +++ b/core/src/main/kotlin/org/dreamexposure/discal/core/object/event/RsvpData.kt @@ -16,6 +16,8 @@ data class RsvpData( @SerialName("event_end") var eventEnd: Long = 0 + var limit: Int = -1 + @SerialName("on_time") val goingOnTime: MutableList = mutableListOf() @@ -83,6 +85,14 @@ data class RsvpData( this.undecided += strList.split(",") } + fun getCurrentCount() = this.goingOnTime.size + this.goingLate.size + + fun hasRoom(userId: String): Boolean { + return if (limit == -1 || getCurrentCount() + 1 <= limit) true + //Check if they are in a list that counts toward limit, if true, that means they will fit in the event + else goingOnTime.contains(userId) || goingLate.contains(userId) + } + //Functions fun removeCompletely(userId: String) { this.goingOnTime.remove(userId) diff --git a/core/src/main/resources/db/migration/V11__Add_RSVP_Limit.sql b/core/src/main/resources/db/migration/V11__Add_RSVP_Limit.sql new file mode 100644 index 00000000..31c5256a --- /dev/null +++ b/core/src/main/resources/db/migration/V11__Add_RSVP_Limit.sql @@ -0,0 +1,2 @@ +ALTER TABLE ${prefix}rsvp + ADD COLUMN 'limit' INT(11) NOT NULL DEFAULT -1; diff --git a/server/src/main/java/org/dreamexposure/discal/server/api/endpoints/v2/rsvp/UpdateRsvpEndpoint.java b/server/src/main/java/org/dreamexposure/discal/server/api/endpoints/v2/rsvp/UpdateRsvpEndpoint.java index 26c747a3..08252669 100644 --- a/server/src/main/java/org/dreamexposure/discal/server/api/endpoints/v2/rsvp/UpdateRsvpEndpoint.java +++ b/server/src/main/java/org/dreamexposure/discal/server/api/endpoints/v2/rsvp/UpdateRsvpEndpoint.java @@ -50,32 +50,10 @@ public class UpdateRsvpEndpoint { final RsvpData rsvp = DatabaseManager.getRsvpData(Snowflake.of(guildId), eventId).block(); - //Handle additions... - if (requestBody.has("to_add")) { - final JSONObject jToAdd = requestBody.getJSONObject("to_add"); - if (jToAdd.has("on_time")) { - final JSONArray ar = jToAdd.getJSONArray("on_time"); - for (int i = 0; i < jToAdd.length(); i++) - rsvp.getGoingOnTime().add(ar.getString(i)); - } - if (jToAdd.has("late")) { - final JSONArray ar = jToAdd.getJSONArray("late"); - for (int i = 0; i < jToAdd.length(); i++) - rsvp.getGoingLate().add(ar.getString(i)); - } - if (jToAdd.has("not_going")) { - final JSONArray ar = jToAdd.getJSONArray("not_going"); - for (int i = 0; i < jToAdd.length(); i++) - rsvp.getNotGoing().add(ar.getString(i)); - } - if (jToAdd.has("undecided")) { - final JSONArray ar = jToAdd.getJSONArray("undecided"); - for (int i = 0; i < jToAdd.length(); i++) - rsvp.getUndecided().add(ar.getString(i)); - } - } + //Handle limit change + rsvp.setLimit(requestBody.optInt("limit", rsvp.getLimit())); - //handle removals... + //handle removals... (We do this first just in case they are using the limit) if (requestBody.has("to_remove")) { final JSONObject jToRemove = requestBody.getJSONObject("to_remove"); if (jToRemove.has("on_time")) { @@ -100,6 +78,43 @@ public class UpdateRsvpEndpoint { } } + //Handle additions... + if (requestBody.has("to_add")) { + final JSONObject jToAdd = requestBody.getJSONObject("to_add"); + if (jToAdd.has("on_time")) { + final JSONArray ar = jToAdd.getJSONArray("on_time"); + for (int i = 0; i < jToAdd.length(); i++) { + if (rsvp.hasRoom(ar.getString(i))) { + rsvp.removeCompletely(ar.getString(i)); + rsvp.getGoingOnTime().add(ar.getString(i)); + } + } + } + if (jToAdd.has("late")) { + final JSONArray ar = jToAdd.getJSONArray("late"); + for (int i = 0; i < jToAdd.length(); i++) { + if (rsvp.hasRoom(ar.getString(i))) { + rsvp.removeCompletely(ar.getString(i)); + rsvp.getGoingLate().add(ar.getString(i)); + } + } + } + if (jToAdd.has("not_going")) { + final JSONArray ar = jToAdd.getJSONArray("not_going"); + for (int i = 0; i < jToAdd.length(); i++) { + rsvp.removeCompletely(ar.getString(i)); + rsvp.getNotGoing().add(ar.getString(i)); //Limit not needed here + } + } + if (jToAdd.has("undecided")) { + final JSONArray ar = jToAdd.getJSONArray("undecided"); + for (int i = 0; i < jToAdd.length(); i++) { + rsvp.removeCompletely(ar.getString(i)); + rsvp.getUndecided().add(ar.getString(i)); //Limit also not needed here + } + } + } + if (DatabaseManager.updateRsvpData(rsvp).block()) { response.setContentType("application/json"); response.setStatus(GlobalConst.STATUS_SUCCESS); diff --git a/web/src/main/html/templates/docs/api/v2/rsvp.html b/web/src/main/html/templates/docs/api/v2/rsvp.html index 8528d761..03a5a2cc 100644 --- a/web/src/main/html/templates/docs/api/v2/rsvp.html +++ b/web/src/main/html/templates/docs/api/v2/rsvp.html @@ -3,50 +3,50 @@ - - - - - + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - DisCal - API Docs + DisCal - API Docs - - - + + + - - + + @@ -55,382 +55,389 @@
- - + - -
+ +
- -
+ +
- -
+ + - + - - + + - -
-

API v2 Docs - RSVP Endpoint

+ +
+

API v2 Docs - RSVP Endpoint

-

- The RSVP endpoints allows you to get and edit RSVP status for events in a guild. -

+

+ The RSVP endpoints allows you to get and edit RSVP status for events in a guild. +

-
+
-
+
- -

rsvp/get

+ +

rsvp/get

-

- Returns the specified event's RSVP details. -

+

+ Returns the specified event's RSVP details. +

-
+
-
Example Request Body
+
Example Request Body
-

+                

 				{
 				"guild_id": 375357265198317579,
 				"event_id": "divq9ihqhoq9hbm2tncj8set04"
 				}
 				
-
+
-
Example Response
+
Example Response
-
+
-

+                

 				{
 				"on_time": ["130510525770629121"],
 				"late": ["233611560545812480", "142107863307780097"],
 				"undecided": [],
-				"not_going": []
+				"not_going": [],
+                "limit", -1
 				}
 				
-
+
-
Supported Values in Request
+
Supported Values in Request
- - - - - - - - - - - - - - - - - - - - - -
KeyValue TypeInfoRequired
guild_idstringThe Guild IDTrue
event_idStringThe Event IDTrue
+ + + + + + + + + + + + + + + + + + + + + +
KeyValue TypeInfoRequired
guild_idstringThe Guild IDTrue
event_idStringThe Event IDTrue
-
+
-
Returned Values
+
Returned Values
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValue TypeInfo
guild_idstringThe ID of the guild the RSVP data be`1`s to
event_idStringThe ID of the event this RSVP object is for
event_endLongUnix time of when the event ends
on_timeList (of Strings)List of users that RSVPed as "on time"
on_timeList (of Strings)List of users that RSVPed as "on time"
lateList (of Strings)List of users that RSVPed as "late"
undecidedList (of Strings)List of users that RSVPed as "unsure"/"undecided"
not_goingList (of Strings)List of users that RSVPed as "not going"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyValue TypeInfo
guild_idstringThe ID of the guild the RSVP data be`1`s to
event_idStringThe ID of the event this RSVP object is for
event_endLongUnix time of when the event ends
on_timeList (of Strings)List of users that RSVPed as "on time"
on_timeList (of Strings)List of users that RSVPed as "on time"
lateList (of Strings)List of users that RSVPed as "late"
undecidedList (of Strings)List of users that RSVPed as "unsure"/"undecided"
not_goingList (of Strings)List of users that RSVPed as "not going"
limitIntegerAmount of people that can RSVP to the event, `-1` to disable
-
- +
+ -
+
- -

rsvp/update

+ +

rsvp/update

-

- Updates the specified event's RSVP data. -

+

+ Updates the specified event's RSVP data. +

-
+
-
Example Request Body
+
Example Request Body
-

+                

 				{
 				"guild_id": 375357265198317579,
 				"event_id": "divq9ihqhoq9hbm2tncj8set04",
+                "limit": 10
 				"to_add": {
 					"on_time": ["130510525770629121"],
 					"late": ["233611560545812480"]
@@ -442,99 +449,105 @@
 				}
 				
-
+
-
Example Response
+
Example Response
-
+
-

+                

 				{
 				"message": "RSVP successfully updated"
 				}
 				
-
+
-
Supported Values in Request
+
Supported Values in Request
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValue TypeInfoRequired
guild_idstringThe Guild IDTrue
event_idStringThe Event IDTrue
to_add.on_timeList (of Strings)List of users to add that RSVPed as "on time"False
to_add.lateList (of Strings)List of users to add that RSVPed as "late"False
to_add.undecidedList (of Strings)List of users to add that RSVPed as "unsure"/"undecided"False
to_add.not_goingList (of Strings)List of users to remove that RSVPed as "not going"False
to_remove.on_timeList (of Strings)List of users to remove that RSVPed as "on time"False
to_remove.lateList (of Strings)List of users to remove that RSVPed as "late"False
to_remove.undecidedList (of Strings)List of users to remove that RSVPed as "unsure"/"undecided"False
to_remove.not_goingList (of Strings)List of users to remove that RSVPed as "not going"False
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyValue TypeInfoRequired
guild_idstringThe Guild IDTrue
event_idStringThe Event IDTrue
limitIntegerAmount of people that can RSVP to the event, `-1` to disableFalse
to_add.on_timeList (of Strings)List of users to add that RSVPed as "on time"False
to_add.lateList (of Strings)List of users to add that RSVPed as "late"False
to_add.undecidedList (of Strings)List of users to add that RSVPed as "unsure"/"undecided"False
to_add.not_goingList (of Strings)List of users to remove that RSVPed as "not going"False
to_remove.on_timeList (of Strings)List of users to remove that RSVPed as "on time"False
to_remove.lateList (of Strings)List of users to remove that RSVPed as "late"False
to_remove.undecidedList (of Strings)List of users to remove that RSVPed as "unsure"/"undecided"False
to_remove.not_goingList (of Strings)List of users to remove that RSVPed as "not going"False
-
+
-
Returned Values
+
Returned Values
- @@ -558,10 +571,10 @@
- + - -
+ +
-
- +
+ - - + + - +