Skip to content

Commit 906c80c

Browse files
committed
Allow editing submission data
1 parent 3731bf3 commit 906c80c

File tree

13 files changed

+285
-7
lines changed

13 files changed

+285
-7
lines changed

botfest/src/main/kotlin/net/modfest/botfest/App.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ suspend fun main() {
5151
add(::UserCommands)
5252
add(::EventCommands)
5353
add(::RoleManager)
54+
add(::SubmissionCommands)
5455
}
5556

5657
errorResponse { message, type ->

botfest/src/main/kotlin/net/modfest/botfest/Platform.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import net.modfest.platform.pojo.EventData
1919
import net.modfest.platform.pojo.HealthData
2020
import net.modfest.platform.pojo.PlatformErrorResponse
2121
import net.modfest.platform.pojo.SubmissionData
22+
import net.modfest.platform.pojo.SubmissionPatchData
2223
import net.modfest.platform.pojo.SubmitRequest
2324
import net.modfest.platform.pojo.UserCreateData
2425
import net.modfest.platform.pojo.UserData
@@ -106,6 +107,10 @@ class Platform(baseUrl: String) {
106107
return getEvents().map { e -> e.id }
107108
}
108109

110+
suspend fun getUserSubmissions(user: Snowflake): List<SubmissionData> {
111+
return client.get("/user/dc:${user.value}/submissions").unwrapErrors().body()
112+
}
113+
109114
/**
110115
* Retrieve a user by their discord id. Will be null if the user does not exist
111116
*/
@@ -171,6 +176,13 @@ class PlatformAuthenticated(var client: HttpClient, var discordUser: Snowflake)
171176
}.unwrapErrors().body()
172177
}
173178

179+
suspend fun editSubmissionData(eventId: String, subId: String, edit: SubmissionPatchData) {
180+
client.patch("/event/$eventId/submission/$subId") {
181+
addAuth()
182+
setBody(edit)
183+
}
184+
}
185+
174186
suspend fun registerMe(event: EventData) {
175187
client.put("/event/"+event.id+"/registrations/dc:"+discordUser.value) {
176188
addAuth()

botfest/src/main/kotlin/net/modfest/botfest/extensions/DebugCommands.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class DebugCommands : Extension(), KordExKoinComponent {
2828
name = Translations.Commands.Group.Debug.name
2929
description = Translations.Commands.Group.Debug.description
3030

31-
guild(MAIN_GUILD_ID) // Otherwise it will take up to an hour to update
31+
guild(MAIN_GUILD_ID) // Otherwise it will take up to an hour to update
3232

3333
// View health of the bot and platform
3434
ephemeralSubCommand {

botfest/src/main/kotlin/net/modfest/botfest/extensions/EventCommands.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ class EventCommands : Extension(), KordExKoinComponent {
3131

3232
@OptIn(UnsafeAPI::class)
3333
override suspend fun setup() {
34-
val platform = bot.getKoin().get<Platform>()
35-
3634
// Register command
3735
unsafeSlashCommand {
3836
name = Translations.Commands.Register.name
3937
description = Translations.Commands.Register.description
38+
guild(MAIN_GUILD_ID)
4039

4140
// We're using KordEx's unsafe api here, because our modal is optional and has prefilled fields.
4241
// This means we're responsible for initiating the response
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package net.modfest.botfest.extensions
2+
3+
import dev.kord.core.behavior.interaction.response.edit
4+
import dev.kordex.core.commands.Arguments
5+
import dev.kordex.core.commands.converters.impl.string
6+
import dev.kordex.core.components.forms.ModalForm
7+
import dev.kordex.core.extensions.Extension
8+
import dev.kordex.core.extensions.ephemeralSlashCommand
9+
import dev.kordex.core.i18n.toKey
10+
import dev.kordex.core.i18n.types.Key
11+
import dev.kordex.core.i18n.withContext
12+
import dev.kordex.core.koin.KordExKoinComponent
13+
import dev.kordex.core.utils.suggestStringCollection
14+
import dev.kordex.modules.dev.unsafe.annotations.UnsafeAPI
15+
import dev.kordex.modules.dev.unsafe.commands.slash.InitialSlashCommandResponse
16+
import dev.kordex.modules.dev.unsafe.extensions.unsafeSubCommand
17+
import net.modfest.botfest.MAIN_GUILD_ID
18+
import net.modfest.botfest.Platform
19+
import net.modfest.botfest.i18n.Translations
20+
import net.modfest.platform.pojo.SubmissionData.AssociatedData.Other
21+
import net.modfest.platform.pojo.SubmissionPatchData
22+
import org.koin.core.component.inject
23+
import java.util.*
24+
25+
/**
26+
* Provides various debugging commands
27+
*/
28+
class SubmissionCommands : Extension(), KordExKoinComponent {
29+
override val name = "submission"
30+
val platform: Platform by inject()
31+
32+
@OptIn(UnsafeAPI::class)
33+
override suspend fun setup() {
34+
ephemeralSlashCommand {
35+
name = Translations.Commands.Group.Submission.name
36+
description = Translations.Commands.Group.Submission.description
37+
38+
guild(MAIN_GUILD_ID)
39+
40+
unsafeSubCommand(::SubmissionArg) {
41+
name = Translations.Commands.Submission.Edit.name
42+
description = Translations.Commands.Submission.Edit.description
43+
44+
initialResponse = InitialSlashCommandResponse.None
45+
46+
action {
47+
val subId = this.arguments.submission
48+
val curEvent = platform.getCurrentEvent().event
49+
if (curEvent == null) {
50+
ackEphemeral {
51+
content = Translations.Commands.Event.Submit.Response.unavailable
52+
.withContext(this@action)
53+
.translateNamed()
54+
}
55+
return@action
56+
}
57+
58+
val submission = platform.getUserSubmissions(this.user.id).find { it.id == subId }
59+
60+
if (submission == null) {
61+
ackEphemeral {
62+
content = Translations.Commands.Submission.Edit.Response.notfound
63+
.withContext(this@action)
64+
.translateNamed(
65+
"subId" to subId
66+
)
67+
}
68+
return@action
69+
}
70+
71+
val subPlatformData = submission.platform.inner
72+
val modal: SubmissionEditForm
73+
if (subPlatformData is Other) {
74+
modal = SubmissionEditOtherForm()
75+
if (subPlatformData.homepageUrl != null) {
76+
modal.homepage.initialValue = subPlatformData.homepageUrl!!.toKey(Locale.UK)
77+
}
78+
} else {
79+
modal = SubmissionEditForm()
80+
}
81+
modal.name.initialValue = submission.name.toKey(Locale.UK)
82+
modal.description.initialValue = submission.description.toKey(Locale.UK)
83+
if (submission.source != null) {
84+
modal.source.initialValue = submission.source!!.toKey(Locale.UK)
85+
}
86+
87+
val result = modal.sendAndDeferEphemeral(this)
88+
89+
// Result will be null if the user didn't enter anything
90+
// or if the modal timed out
91+
if (result == null) {
92+
return@action
93+
}
94+
95+
platform.withAuth(this.user).editSubmissionData(curEvent, subId, SubmissionPatchData(
96+
modal.name.value,
97+
modal.description.value,
98+
modal.source.value,
99+
if (modal is SubmissionEditOtherForm) { modal.homepage.value } else { null }
100+
))
101+
102+
result.edit {
103+
content = Translations.Commands.Submission.Edit.Response.success
104+
.withContext(this@action)
105+
.translateNamed(
106+
"subId" to subId
107+
)
108+
}
109+
}
110+
}
111+
}
112+
}
113+
114+
inner class SubmissionArg : Arguments() {
115+
val submission by string {
116+
name = Translations.Arguments.Submission.Edit.name
117+
description = Translations.Arguments.Submission.Edit.description
118+
119+
autoComplete {
120+
val curEvent = platform.getCurrentEvent().event ?: return@autoComplete
121+
suggestStringCollection(
122+
platform.getUserSubmissions(this.user.id)
123+
.filter { it.event == curEvent }
124+
.map { it.id }
125+
)
126+
}
127+
}
128+
}
129+
130+
open class SubmissionEditForm : ModalForm() {
131+
override var title: Key = Translations.Modal.Submission.Edit.title
132+
133+
val name = lineText {
134+
label = Translations.Modal.Submission.Name.label
135+
placeholder = Translations.Modal.Submission.Name.placeholder
136+
minLength = 2
137+
maxLength = 128
138+
required = true
139+
translateInitialValue = false
140+
}
141+
142+
val description = lineText {
143+
label = Translations.Modal.Submission.Description.label
144+
placeholder = Translations.Modal.Submission.Description.placeholder
145+
minLength = 1
146+
maxLength = 128
147+
required = true
148+
translateInitialValue = false
149+
}
150+
151+
val source = lineText {
152+
label = Translations.Modal.Submission.Source.label
153+
placeholder = Translations.Modal.Submission.Source.placeholder
154+
minLength = 0
155+
maxLength = 128
156+
required = false
157+
translateInitialValue = false
158+
}
159+
}
160+
161+
class SubmissionEditOtherForm : SubmissionEditForm() {
162+
val homepage = lineText {
163+
label = Translations.Modal.Submission.Homepage.label
164+
placeholder = Translations.Modal.Submission.Homepage.placeholder
165+
minLength = 1
166+
maxLength = 128
167+
required = true
168+
translateInitialValue = false
169+
}
170+
}
171+
}

botfest/src/main/resources/translations/botfest/strings.properties

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ commands.group.user.name=user
66
commands.group.user.description=Manage your ModFest profile
77
commands.group.event.name=event
88
commands.group.event.description=Do things related to the current event
9+
commands.group.submission.name=submission
10+
commands.group.submission.description=Manage your submissions to the current event
911

1012
commands.getcurrentevent.name=GetCurrentEvent
1113
commands.getcurrentevent.description=Gets the current event
@@ -56,6 +58,10 @@ commands.event.submit.description=submit your mod
5658
commands.event.submit.response.invalid=`{url}` is not a valid Modrinth URL
5759
commands.event.submit.response.unavailable=There's no current event happening.
5860
commands.event.submit.response.success=Successfully submitted {mod} to {event} :tada:
61+
commands.submission.edit.name=edit
62+
commands.submission.edit.description=Edit your submission's data
63+
commands.submission.edit.response.notfound=Unknown submission {subId}
64+
commands.submission.edit.response.success=Successfully edited the data for {subId}
5965

6066
commands.fix.name=fixmyroles
6167
commands.fix.description=Corrects any errors with your roles. Use after re-joining the server
@@ -75,6 +81,8 @@ arguments.setuser.pronouns.name=pronouns
7581
arguments.setuser.pronouns.description=Change your pronouns
7682
arguments.setuser.name.name=name
7783
arguments.setuser.name.description=Change your name
84+
arguments.submission.edit.name=submission
85+
arguments.submission.edit.description=The submission you want to edit
7886

7987
modal.register.title=Register for ModFest
8088
modal.register.name.label=What name would you like on your profile?
@@ -86,6 +94,15 @@ modal.register.pronouns.placeholder=they/them
8694
modal.submit.title=Submit a mod
8795
modal.submit.url.label=Enter your project's Modrinth URL
8896
modal.submit.url.placeholder=https://modrinth.com/mod/your-mod
97+
modal.submission.edit.title=Edit your mod's data
98+
modal.submission.name.label=Name
99+
modal.submission.name.placeholder=...
100+
modal.submission.description.label=Description
101+
modal.submission.description.placeholder=...
102+
modal.submission.source.label=Sourcecode link
103+
modal.submission.source.placeholder=...
104+
modal.submission.homepage.label=Link to your project's homepage
105+
modal.submission.homepage.placeholder=https://mymod.com/
89106

90107
apierror.event_no_exists=An event named {n} doesn't exists
91108
apierror.user_no_exists=A user named {n} doesn't exists

common/src/main/java/net/modfest/platform/pojo/SubmissionData.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package net.modfest.platform.pojo;
22

33
import com.google.gson.*;
4+
import lombok.With;
45
import org.jspecify.annotations.NonNull;
56
import org.jspecify.annotations.Nullable;
67

78
import java.lang.reflect.Type;
89
import java.util.Set;
910

11+
@With
1012
public record SubmissionData(@NonNull String id,
1113
@NonNull String event,
1214
@NonNull String name,
@@ -36,6 +38,7 @@ public record Modrinth(String projectId, @Nullable String versionId) {
3638
public static final String KEY = "modrinth";
3739
}
3840

41+
@With
3942
public record Other(@Nullable String homepageUrl, @Nullable String downloadUrl) {
4043
public static final String KEY = "other";
4144
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package net.modfest.platform.pojo;
2+
3+
import org.jspecify.annotations.Nullable;
4+
5+
public record SubmissionPatchData(
6+
@Nullable String name,
7+
@Nullable String description,
8+
@Nullable String sourceUrl,
9+
/**
10+
* Should not be included if the submission isn't of type "other"
11+
*/
12+
@Nullable String homepage
13+
) {
14+
}

platform_api/src/main/java/net/modfest/platform/controller/EventController.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import net.modfest.platform.pojo.EventData;
44
import net.modfest.platform.pojo.SubmissionData;
5+
import net.modfest.platform.pojo.SubmissionPatchData;
56
import net.modfest.platform.pojo.SubmitRequest;
67
import net.modfest.platform.security.PermissionUtils;
78
import net.modfest.platform.security.Permissions;
@@ -112,4 +113,21 @@ public SubmissionData makeSubmission(@PathVariable String eventId, @RequestBody
112113

113114
return service.makeModrinthSubmission(eventId, submission.modrinthProject());
114115
}
116+
117+
@PatchMapping("/event/{eventId}/submission/{subId}")
118+
public void editSubmissionData(@PathVariable String eventId, @PathVariable String subId, @RequestBody SubmissionPatchData editData) {
119+
getEvent(eventId);
120+
var subject = SecurityUtils.getSubject();
121+
var can_others = subject.isPermitted(Permissions.Event.EDIT_OTHER_SUBMISSION);
122+
var submission = service.getSubmission(eventId, subId);
123+
if (submission == null) {
124+
throw new IllegalArgumentException();// TODO
125+
}
126+
if (!PermissionUtils.owns(subject, submission) && !can_others) {
127+
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
128+
"You do not have permissions to edit this data");
129+
}
130+
131+
service.editSubmission(submission, editData);
132+
}
115133
}

platform_api/src/main/java/net/modfest/platform/security/PermissionGroup.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public enum PermissionGroup {
1818
Permissions.Event.REGISTER_OTHERS,
1919
Permissions.Event.SUBMIT_BYPASS,
2020
Permissions.Event.SUBMIT_OTHER,
21-
Permissions.Event.EDIT_SCHEDULE
21+
Permissions.Event.EDIT_SCHEDULE,
22+
Permissions.Event.EDIT_OTHER_SUBMISSION
2223
)),
2324
/**
2425
* Note: BotFest usually performs actions on behalf of a different user.

0 commit comments

Comments
 (0)