Skip to content

Commit 41103d8

Browse files
committed
feat: begin kotlin
simple migration heavily leveraging automatic tools, much more work to do with integrating jda-ktx and kotlin code styles
1 parent 1541fb4 commit 41103d8

File tree

109 files changed

+4913
-5813
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+4913
-5813
lines changed

build.gradle.kts

+8-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ plugins {
88
`java-library`
99
`maven-publish`
1010
id("io.freefair.lombok") version "8.11"
11+
kotlin("jvm")
1112
}
1213

1314
repositories {
14-
mavenLocal()
15+
mavenCentral()
1516
maven {
1617
url = uri("https://repo.maven.apache.org/maven2/")
1718
}
19+
mavenLocal()
1820
}
1921

2022
dependencies {
@@ -32,13 +34,12 @@ dependencies {
3234
testImplementation(libs.org.junit.jupiter.junit.jupiter)
3335
testImplementation(libs.ch.qos.logback.logback.classic)
3436
compileOnly(libs.org.jetbrains.annotations)
37+
implementation(kotlin("stdlib-jdk8"))
3538
}
3639

3740
group = "dev.qixils.quasicolon"
3841
version = "1.0.0-SNAPSHOT"
3942
description = "quasicord"
40-
java.sourceCompatibility = JavaVersion.VERSION_21
41-
java.targetCompatibility = JavaVersion.VERSION_21
4243

4344
java {
4445
withSourcesJar()
@@ -57,3 +58,7 @@ tasks.withType<JavaCompile>() {
5758
tasks.withType<Javadoc>() {
5859
options.encoding = "UTF8"
5960
}
61+
62+
kotlin {
63+
jvmToolchain(21)
64+
}

settings.gradle.kts

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
pluginManagement {
2+
plugins {
3+
kotlin("jvm") version "2.1.0"
4+
}
5+
}
6+
plugins {
7+
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
8+
}
19
/*
210
* This file was generated by the Gradle 'init' task.
311
*

src/main/java/dev/qixils/quasicord/Builder.kt

+10-15
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,18 @@
33
* License, v. 2.0. If a copy of the MPL was not distributed with this
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
6-
7-
package dev.qixils.quasicord;
8-
9-
import org.checkerframework.checker.nullness.qual.NonNull;
6+
package dev.qixils.quasicord
107

118
/**
129
* An object which builds a certain immutable object.
13-
*
14-
* @param <R> the type of object being built
1510
*/
16-
public interface Builder<R> { // , ErrorType extends Throwable
17-
18-
/**
19-
* Builds the object.
20-
*
21-
* @return the built object
22-
* @throws IllegalStateException a required field is missing
23-
*/
24-
@NonNull R build() throws IllegalStateException; // throws ErrorType
11+
interface Builder<R> {
12+
/**
13+
* Builds the object.
14+
*
15+
* @return the built object
16+
* @throws IllegalStateException a required field is missing
17+
*/
18+
@Throws(IllegalStateException::class)
19+
fun build(): R
2520
}

src/main/java/dev/qixils/quasicord/CommandManager.kt

+133-147
Original file line numberDiff line numberDiff line change
@@ -3,152 +3,138 @@
33
* License, v. 2.0. If a copy of the MPL was not distributed with this
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
6-
7-
package dev.qixils.quasicord;
8-
9-
import dev.qixils.quasicord.cogs.Command;
10-
import dev.qixils.quasicord.cogs.SlashCommand;
11-
import dev.qixils.quasicord.decorators.AnnotationParser;
12-
import dev.qixils.quasicord.error.UserError;
13-
import dev.qixils.quasicord.text.Text;
14-
import lombok.Getter;
15-
import net.dv8tion.jda.api.JDA;
16-
import net.dv8tion.jda.api.entities.Guild;
17-
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
18-
import net.dv8tion.jda.api.hooks.SubscribeEvent;
19-
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
20-
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
21-
import net.dv8tion.jda.api.requests.RestAction;
22-
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
23-
import org.checkerframework.checker.nullness.qual.NonNull;
24-
import org.checkerframework.checker.nullness.qual.Nullable;
25-
import org.slf4j.Logger;
26-
import org.slf4j.LoggerFactory;
27-
28-
import java.util.HashMap;
29-
import java.util.HashSet;
30-
import java.util.Map;
31-
import java.util.Set;
32-
33-
import static dev.qixils.quasicord.Key.library;
34-
import static dev.qixils.quasicord.locale.Context.fromInteraction;
35-
import static dev.qixils.quasicord.text.Text.single;
36-
37-
public class CommandManager {
38-
@Getter
39-
private final @NonNull Quasicord library;
40-
private final Logger logger = LoggerFactory.getLogger(getClass());
41-
protected final @NonNull Map<@Nullable String, Map<String, Command<?>>> commands = new HashMap<>();
42-
private final AnnotationParser parser;
43-
private boolean initialUpsertDone = false;
44-
45-
public CommandManager(@NonNull Quasicord library) {
46-
commands.put(null, new HashMap<>());
47-
this.library = library;
48-
this.parser = new AnnotationParser(this);
49-
}
50-
51-
private static void sendEphemeral(@NonNull IReplyCallback event, @NonNull Text text) {
52-
text.asString(fromInteraction(event)).subscribe(string -> event.reply(string).setEphemeral(true).queue());
53-
}
54-
55-
@Nullable
56-
public Command<?> getCommand(String discordName, @Nullable String guildId) {
57-
if (guildId != null && commands.containsKey(guildId)) {
58-
Command<?> command = commands.get(guildId).get(discordName);
59-
if (command != null) return command;
60-
}
61-
return commands.get(null).get(discordName);
62-
}
63-
64-
public void upsertCommands(JDA jda) {
65-
if (initialUpsertDone) return;
66-
initialUpsertDone = true;
67-
logger.info("Upserting commands");
68-
for (Map.Entry<String, Map<String, Command<?>>> entry : commands.entrySet()) {
69-
String guildId = entry.getKey();
70-
Map<String, Command<?>> guildCommands = entry.getValue();
71-
72-
CommandListUpdateAction updater;
73-
if (guildId == null) {
74-
updater = jda.updateCommands();
75-
} else {
76-
Guild guild = jda.getGuildById(guildId);
77-
if (guild == null)
78-
continue;
79-
updater = guild.updateCommands();
80-
}
81-
82-
Set<String> rootSlashCommands = new HashSet<>();
83-
84-
for (Command<?> command : guildCommands.values()) {
85-
CommandData commandData;
86-
if (command instanceof SlashCommand slashCommand) {
87-
commandData = slashCommand.getBranch().root();
88-
if (rootSlashCommands.contains(commandData.getName()))
89-
continue;
90-
rootSlashCommands.add(commandData.getName());
91-
} else {
92-
commandData = command.getCommandData();
93-
}
94-
95-
if (commandData == null)
96-
continue; // i don't think this should happen but just in case
97-
logger.debug("Upserting command {} to guild {}", commandData.getName(), guildId);
98-
//noinspection ResultOfMethodCallIgnored
99-
updater.addCommands(commandData);
100-
}
101-
102-
updater.queue();
103-
}
104-
}
105-
106-
public void registerCommand(@NonNull Command<?> command) {
107-
String guildId = command.getGuildId();
108-
commands.computeIfAbsent(guildId, $ -> new HashMap<>()).put(command.getDiscordName(), command);
109-
if (!initialUpsertDone)
110-
return;
111-
112-
CommandData cmd = command.getCommandData();
113-
if (cmd == null)
114-
return;
115-
116-
JDA jda = library.getJDA();
117-
RestAction<net.dv8tion.jda.api.interactions.commands.Command> upsert;
118-
119-
if (guildId == null) {
120-
upsert = jda.upsertCommand(cmd);
121-
} else {
122-
Guild guild = jda.getGuildById(guildId);
123-
if (guild == null)
124-
return;
125-
upsert = guild.upsertCommand(cmd);
126-
}
127-
128-
upsert.queue();
129-
}
130-
131-
public void discoverCommands(@NonNull Object object) {
132-
parser.parse(object).forEach(this::registerCommand);
133-
}
134-
135-
@SuppressWarnings({"unchecked", "rawtypes"})
136-
@SubscribeEvent
137-
public void onCommandInteraction(@NonNull GenericCommandInteractionEvent event) {
138-
String guildId = event.getGuild() == null ? event.getGuild().getId() : null;
139-
Command command = getCommand(event.getFullCommandName(), guildId);
140-
if (command == null) {
141-
library.getLogger().error("Could not find an executor for command " + event.getFullCommandName());
142-
sendEphemeral(event, single(library("exception.command_error")));
143-
return;
144-
}
145-
try {
146-
command.accept(event);
147-
} catch (UserError e) {
148-
sendEphemeral(event, e);
149-
} catch (Exception e) {
150-
library.getLogger().error("Failed to execute command " + event.getFullCommandName(), e);
151-
sendEphemeral(event, single(library("exception.command_error")));
6+
package dev.qixils.quasicord
7+
8+
import dev.qixils.quasicord.cogs.Command
9+
import dev.qixils.quasicord.cogs.SlashCommand
10+
import dev.qixils.quasicord.decorators.AnnotationParser
11+
import dev.qixils.quasicord.error.UserError
12+
import dev.qixils.quasicord.locale.Context
13+
import dev.qixils.quasicord.text.Text
14+
import net.dv8tion.jda.api.JDA
15+
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent
16+
import net.dv8tion.jda.api.hooks.SubscribeEvent
17+
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback
18+
import net.dv8tion.jda.api.interactions.commands.build.CommandData
19+
import net.dv8tion.jda.api.requests.RestAction
20+
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction
21+
import org.slf4j.Logger
22+
import org.slf4j.LoggerFactory
23+
import java.util.function.Consumer
24+
25+
class CommandManager(library: Quasicord) {
26+
val library: Quasicord
27+
private val logger: Logger = LoggerFactory.getLogger(javaClass)
28+
protected val commands: MutableMap<String?, MutableMap<String, Command<*>>> = HashMap()
29+
private val parser: AnnotationParser
30+
private var initialUpsertDone = false
31+
32+
init {
33+
commands[null] = HashMap()
34+
this.library = library
35+
this.parser = AnnotationParser(this)
36+
}
37+
38+
fun getCommand(discordName: String, guildId: String?): Command<*>? {
39+
if (guildId != null && commands.containsKey(guildId)) {
40+
val command = commands[guildId]!![discordName]
41+
if (command != null) return command
42+
}
43+
return commands[null]!![discordName]
44+
}
45+
46+
fun upsertCommands(jda: JDA) {
47+
if (initialUpsertDone) return
48+
initialUpsertDone = true
49+
logger.info("Upserting commands")
50+
for ((guildId, guildCommands) in commands) {
51+
var updater: CommandListUpdateAction
52+
if (guildId == null) {
53+
updater = jda.updateCommands()
54+
} else {
55+
val guild = jda.getGuildById(guildId) ?: continue
56+
updater = guild.updateCommands()
57+
}
58+
59+
val rootSlashCommands: MutableSet<String> = HashSet()
60+
61+
for (command in guildCommands.values) {
62+
var commandData: CommandData?
63+
if (command is SlashCommand) {
64+
commandData = command.branch.root
65+
if (rootSlashCommands.contains(commandData.name)) continue
66+
rootSlashCommands.add(commandData.name)
67+
} else {
68+
commandData = command.commandData
69+
}
70+
71+
if (commandData == null) continue // i don't think this should happen but just in case
72+
73+
logger.debug("Upserting command {} to guild {}", commandData.name, guildId)
74+
updater.addCommands(commandData)
75+
}
76+
77+
updater.queue()
78+
}
79+
}
80+
81+
fun registerCommand(command: Command<*>) {
82+
val guildId = command.guildId
83+
commands.computeIfAbsent(guildId) { HashMap() }[command.discordName] =
84+
command
85+
if (!initialUpsertDone) return
86+
87+
val cmd = command.commandData ?: return
88+
89+
val jda = library.jda
90+
val upsert: RestAction<net.dv8tion.jda.api.interactions.commands.Command>
91+
92+
if (guildId == null) {
93+
upsert = jda.upsertCommand(cmd)
94+
} else {
95+
val guild = jda.getGuildById(guildId) ?: return
96+
upsert = guild.upsertCommand(cmd)
97+
}
98+
99+
upsert.queue()
100+
}
101+
102+
fun discoverCommands(`object`: Any) {
103+
parser.parse(`object`).forEach(Consumer { command: Command<*> -> this.registerCommand(command) })
104+
}
105+
106+
@SubscribeEvent
107+
fun onCommandInteraction(event: GenericCommandInteractionEvent) {
108+
val guildId = if (event.guild == null) event.guild!!.id else null
109+
val command = getCommand(event.fullCommandName, guildId)
110+
if (command == null) {
111+
library.logger.error("Could not find an executor for command {}", event.fullCommandName)
112+
sendEphemeral(event, Text.single(Key.library("exception.command_error")))
113+
return
114+
}
115+
if (!(command.interactionClass.isAssignableFrom(event.javaClass))) {
116+
library.logger.error("Invalid event type {} for command {}", event.javaClass.name, event.fullCommandName)
117+
sendEphemeral(event, Text.single(Key.library("exception.command_error")))
118+
return
152119
}
153-
}
120+
try {
121+
@Suppress("UNCHECKED_CAST")
122+
(command as Command<Any>).accept(event)
123+
} catch (e: UserError) {
124+
sendEphemeral(event, e)
125+
} catch (e: Exception) {
126+
library.logger.error("Failed to execute command " + event.fullCommandName, e)
127+
sendEphemeral(event, Text.single(Key.library("exception.command_error")))
128+
}
129+
}
130+
131+
companion object {
132+
private fun sendEphemeral(event: IReplyCallback, text: Text) {
133+
text.asString(Context.fromInteraction(event)).subscribe { string: String? ->
134+
event.reply(
135+
string!!
136+
).setEphemeral(true).queue()
137+
}
138+
}
139+
}
154140
}

src/main/java/dev/qixils/quasicord/Environment.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
* License, v. 2.0. If a copy of the MPL was not distributed with this
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
6+
package dev.qixils.quasicord
67

7-
package dev.qixils.quasicord;
8-
9-
public enum Environment {
10-
PRODUCTION,
11-
TEST,
8+
enum class Environment {
9+
PRODUCTION,
10+
TEST,
1211
}

0 commit comments

Comments
 (0)