Skip to content

Commit

Permalink
Add /addcal slash command
Browse files Browse the repository at this point in the history
This is locked to devs only for right now until I finish the full flow that is planned in #113

I also cleaned up some code in a few places while I was messing with some stuff
  • Loading branch information
NovaFox161 committed Aug 30, 2021
1 parent 66a4f7f commit b9bac2f
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.dreamexposure.discal.client.commands

import discord4j.core.`object`.entity.Guild
import discord4j.core.`object`.entity.Member
import discord4j.core.event.domain.interaction.SlashCommandEvent
import org.dreamexposure.discal.client.message.Responder
import org.dreamexposure.discal.core.`object`.BotSettings
import org.dreamexposure.discal.core.`object`.GuildSettings
import org.dreamexposure.discal.core.extensions.discord4j.canAddCalendar
import org.dreamexposure.discal.core.extensions.discord4j.hasElevatedPermissions
import org.dreamexposure.discal.core.utils.getCommonMsg
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono

@Component
class AddCalCommand : SlashCommand {
override val name = "addcal"
override val ephemeral = true

override fun handle(event: SlashCommandEvent, settings: GuildSettings): Mono<Void> {
//TODO: Remove dev-only and switch to patron-only once this is completed
return if (settings.devGuild) {
Mono.justOrEmpty(event.interaction.member)
.filterWhen(Member::hasElevatedPermissions).flatMap {
//Check if a calendar can be added since non-premium only allows 1 calendar.
event.interaction.guild.filterWhen(Guild::canAddCalendar).flatMap {
Responder.followupEphemeral(event, getMessage("response.start", settings, getLink(settings)))
}.switchIfEmpty(Responder.followupEphemeral(event, getCommonMsg("error.calendar.max", settings)))
}.switchIfEmpty(Responder.followupEphemeral(event, getCommonMsg("error.perms.elevated", settings)))
.then()
} else {
Responder.followupEphemeral(event, getCommonMsg("error.disabled", settings)).then()
}
}

private fun getLink(settings: GuildSettings): String {
return "${BotSettings.BASE_URL.get()}/dashboard/${settings.guildID.asString()}/calendar/new?type=1&step=0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import discord4j.core.`object`.entity.Guild
import org.dreamexposure.discal.core.`object`.BotSettings
import org.dreamexposure.discal.core.`object`.calendar.CalendarData
import org.dreamexposure.discal.core.`object`.web.WebCalendar
import org.dreamexposure.discal.core.entities.google.GoogleCalendar
import org.dreamexposure.discal.core.entities.response.UpdateCalendarResponse
import org.dreamexposure.discal.core.entities.spec.create.CreateEventSpec
import org.dreamexposure.discal.core.entities.spec.update.UpdateCalendarSpec
import org.dreamexposure.discal.core.enums.calendar.CalendarHost
import org.json.JSONObject
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
Expand Down Expand Up @@ -143,7 +145,7 @@ interface Calendar {
* @return A [Flux] of [events][Event] that are happening within the next 24-hour period from the start.
*/
fun getEventsInNext24HourPeriod(start: Instant): Flux<Event> =
getEventsInTimeRange(start, start.plus(1, ChronoUnit.DAYS))
getEventsInTimeRange(start, start.plus(1, ChronoUnit.DAYS))

/**
* Requests to retrieve all [events][Event] within the month starting at the supplied [Instant].
Expand All @@ -152,7 +154,7 @@ interface Calendar {
* @return A [Flux] of [events][Event] that are happening in the supplied 1-month period.
*/
fun getEventsInMonth(start: Instant, daysInMonth: Int): Flux<Event> =
getEventsInTimeRange(start, start.plus(daysInMonth.toLong(), ChronoUnit.DAYS))
getEventsInTimeRange(start, start.plus(daysInMonth.toLong(), ChronoUnit.DAYS))

/**
* Requests to create an event with the supplied information.
Expand All @@ -172,29 +174,46 @@ interface Calendar {
*/
fun toWebCalendar(): WebCalendar {
return WebCalendar(
this.calendarId,
this.calendarAddress,
this.calendarNumber,
this.calendarData.host,
this.link,
this.name,
this.description,
this.timezone.id.replace("/", "___"),
this.external
this.calendarId,
this.calendarAddress,
this.calendarNumber,
this.calendarData.host,
this.link,
this.name,
this.description,
this.timezone.id.replace("/", "___"),
this.external
)
}

fun toJson(): JSONObject {
return JSONObject()
.put("guild_id", guildId.asString())
.put("calendar_id", calendarId)
.put("calendar_address", calendarAddress)
.put("calendar_number", calendarNumber)
.put("host", calendarData.host.name)
.put("external", external)
.put("name", name)
.put("description", description)
.put("timezone", timezone)
.put("link", link)
.put("guild_id", guildId.asString())
.put("calendar_id", calendarId)
.put("calendar_address", calendarAddress)
.put("calendar_number", calendarNumber)
.put("host", calendarData.host.name)
.put("external", external)
.put("name", name)
.put("description", description)
.put("timezone", timezone)
.put("link", link)
}

companion object {
/**
* Requests to retrieve the [Calendar] from the provided [CalendarData]
* If an error occurs, it is emitted through the [Mono]
*
* @param data The data object for the Calendar to be built with
* @return A [Mono] containing the [Calendar], if it does not exist, [empty][Mono.empty] is returned.
*/
fun from(data: CalendarData): Mono<Calendar> {
when (data.host) {
CalendarHost.GOOGLE -> {
return GoogleCalendar.get(data)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,6 @@ interface Event {
.put("rrule", recurrence.toRRule())
.put("image", eventData.imageLink)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class GoogleCalendar internal constructor(
) : Calendar {

override val name: String
get() = baseCalendar.summary ?: ""
get() = baseCalendar.summary.orEmpty()

override val description: String
get() = baseCalendar.description ?: ""
get() = baseCalendar.description.orEmpty()

override val timezone: ZoneId
get() = ZoneId.of(baseCalendar.timeZone)
Expand Down Expand Up @@ -143,7 +143,7 @@ class GoogleCalendar internal constructor(
confirmed.id,
calendarNumber,
spec.end.toEpochMilli(),
spec.image ?: ""
spec.image.orEmpty()
)

return@flatMap DatabaseManager.updateEventData(data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ fun Guild.getSettings(): Mono<GuildSettings> = getRestGuild().getSettings()
*/
fun Guild.hasCalendar(): Mono<Boolean> = getRestGuild().hasCalendar()

fun Guild.canAddCalendar(): Mono<Boolean> = getRestGuild().canAddCalendar()

/**
* Attempts to retrieve this [Guild]'s main [Calendar] (calendar 1, this guild's first/primary calendar)
* If an error occurs, it is emitted through the [Mono]
Expand Down Expand Up @@ -69,7 +71,7 @@ fun Guild.createCalendar(spec: CreateCalendarSpec): Mono<Calendar> = getRestGuil
* If an error occurs, it is emitted through the Mono.
*
* @param id The ID of the announcement to check for
* @return A Mono, where upon successful completion, returns a boolean as to if the announcement exists or not
* @return A Mono, whereupon successful completion, returns a boolean as to if the announcement exists or not
*/
fun Guild.announcementExists(id: UUID): Mono<Boolean> = getRestGuild().announcementExists(id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,25 @@ fun RestGuild.getSettings(): Mono<GuildSettings> = DatabaseManager.getSettings(t

//Calendars
/**
* Attempts to request whether or not this [Guild] has at least one [Calendar].
* Attempts to request whether this [Guild] has at least one [Calendar].
* If an error occurs, it is emitted through the [Mono]
*
* @return A [Mono] containing whether or not this [Guild] has a [Calendar].
* @return A [Mono] containing whether this [Guild] has a [Calendar].
*/
fun RestGuild.hasCalendar(): Mono<Boolean> {
return DatabaseManager.getAllCalendars(this.id).map(List<CalendarData>::isNotEmpty)
}

fun RestGuild.canAddCalendar(): Mono<Boolean> {
return getAllCalendars()
.count()
.map(Long::toInt)
.flatMap { current ->
if (current == 0) Mono.just(true)
else getSettings().map { current < it.maxCalendars }
}
}

/**
* Attempts to retrieve this [Guild]'s main [Calendar] (calendar 1, this guild's first/primary calendar)
* If an error occurs, it is emitted through the [Mono]
Expand All @@ -51,13 +61,8 @@ fun RestGuild.getMainCalendar(): Mono<Calendar> = this.getCalendar(1)
* returned.
*/
fun RestGuild.getCalendar(calNumber: Int): Mono<Calendar> {
return DatabaseManager.getCalendar(this.id, calNumber).flatMap {
when (it.host) {
CalendarHost.GOOGLE -> {
return@flatMap GoogleCalendar.get(it)
}
}
}
return DatabaseManager.getCalendar(this.id, calNumber)
.flatMap(Calendar.Companion::from)
}

/**
Expand All @@ -68,14 +73,8 @@ fun RestGuild.getCalendar(calNumber: Int): Mono<Calendar> {
*/
fun RestGuild.getAllCalendars(): Flux<Calendar> {
return DatabaseManager.getAllCalendars(this.id)
.flatMapMany { Flux.fromIterable(it) }
.flatMap {
when (it.host) {
CalendarHost.GOOGLE -> {
return@flatMap GoogleCalendar.get(it)
}
}
}
.flatMapMany { Flux.fromIterable(it) }
.flatMap(Calendar.Companion::from)
}

/**
Expand All @@ -97,26 +96,26 @@ fun RestGuild.createCalendar(spec: CreateCalendarSpec): Mono<Calendar> {

//Call google to create it
CalendarWrapper.createCalendar(googleCal, credId, this.id)
.timeout(Duration.ofSeconds(30))
.flatMap { confirmed ->
val data = CalendarData(
this.id,
spec.calNumber,
CalendarHost.GOOGLE,
confirmed.id,
confirmed.id,
credId
)

val rule = AclRule()
.setScope(AclRule.Scope().setType("default"))
.setRole("reader")

Mono.`when`(
DatabaseManager.updateCalendar(data),
AclRuleWrapper.insertRule(rule, data)
).thenReturn(GoogleCalendar(data, confirmed))
}
.timeout(Duration.ofSeconds(30))
.flatMap { confirmed ->
val data = CalendarData(
this.id,
spec.calNumber,
CalendarHost.GOOGLE,
confirmed.id,
confirmed.id,
credId
)

val rule = AclRule()
.setScope(AclRule.Scope().setType("default"))
.setRole("reader")

Mono.`when`(
DatabaseManager.updateCalendar(data),
AclRuleWrapper.insertRule(rule, data)
).thenReturn(GoogleCalendar(data, confirmed))
}
}
}
}
Expand All @@ -128,7 +127,7 @@ fun RestGuild.createCalendar(spec: CreateCalendarSpec): Mono<Calendar> {
* If an error occurs, it is emitted through the Mono.
*
* @param id The ID of the announcement to check for
* @return A Mono, where upon successful completion, returns a boolean as to if the announcement exists or not
* @return A Mono, whereupon successful completion, returns a boolean as to if the announcement exists or not
*/
fun RestGuild.announcementExists(id: UUID): Mono<Boolean> = this.getAnnouncement(id).hasElement()

Expand All @@ -149,7 +148,7 @@ fun RestGuild.getAnnouncement(id: UUID): Mono<Announcement> = DatabaseManager.ge
*/
fun RestGuild.getAllAnnouncements(): Flux<Announcement> {
return DatabaseManager.getAnnouncements(this.id)
.flatMapMany { Flux.fromIterable(it) }
.flatMapMany { Flux.fromIterable(it) }
}

/**
Expand All @@ -160,7 +159,7 @@ fun RestGuild.getAllAnnouncements(): Flux<Announcement> {
*/
fun RestGuild.getEnabledAnnouncements(): Flux<Announcement> {
return DatabaseManager.getEnabledAnnouncements(this.id)
.flatMapMany { Flux.fromIterable(it) }
.flatMapMany { Flux.fromIterable(it) }
}

fun RestGuild.createAnnouncement(ann: Announcement): Mono<Boolean> = DatabaseManager.updateAnnouncement(ann)
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/resources/commands/addcal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "addcal",
"description": "Used to add an external (already existing) calendar to DisCal",
"default_permissions": true
}
8 changes: 2 additions & 6 deletions core/src/main/resources/i18n/command/addcal/addcal.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
meta.description=Used to add an external calendar
meta.example=/addcal (calendar-id)
response.started=Instructions on how to authorize DisCal and add your calendar have been DMed to you
success=Successfully linked your calendar! you can start creating events, announcements, and more!
failure.notStarted=Please start the process with `/addcal` before selecting a calendar to connect.
failure.invalid=The supplied ID was not recognized, are you sure it is correct?
failure.badArgs=Invalid arguments were supplied. See command info with `/help addcal`
error.hasCalendar=A calendar already exists. View it with `/linkcal`

response.start=To add an external calendar, please visit this link and follow the provided instructions: {0}
7 changes: 6 additions & 1 deletion core/src/main/resources/i18n/common.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ error.perms.elevated=This feature requires elevated access (admin/manage server
error.perms.privileged=This feature requires privileged access (discal control role). Please contact the server owner\
\ if you believe this is a mistake.

error.disabled=This feature is currently disabled as it is in development.
error.disabled=This feature is currently disabled as it is in development. \n\n\
Please contact the devs in the support server (`/help`) for more information. \n\
Sorry for the inconvenience.
error.patronOnly=This feature is patron-only at this time. Consider supporting at https://www.patreon.com/Novafox to \
gain access to early access/patron-only features.

Expand All @@ -18,3 +20,6 @@ error.format.dateTime=The date/time was not formatted correctly, please try agai
`yyyy/MM/dd-hh:mm:ss`.

error.event.ended=That event has already ended!

error.calendar.max=The max amount of calendars have already been created. Consider supporting at \
https://patreon.com/Novafox to unlock a greater limit.

0 comments on commit b9bac2f

Please sign in to comment.