Skip to content
This repository was archived by the owner on May 19, 2024. It is now read-only.

Commit ca6a5df

Browse files
committed
Data Import / Export
1 parent e8107d2 commit ca6a5df

File tree

8 files changed

+119
-9
lines changed

8 files changed

+119
-9
lines changed

.github/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ Aggregator uses the Gradle build system, and as such building the project is eas
1010

1111
The bot is written in Kotlin and uses MongoDB for data storage. There are not many contribution guidelines, just keep code clean.
1212

13-
For simplicity, instead of using a configuration file, Aggregator gets its credentials from environment variables:
13+
For simplicity, instead of using a configuration file, Aggregator gets its credentials and configuration from environment variables:
1414

15-
| Name | Description | Example Value |
16-
|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
17-
| DISCORD_TOKEN | A bot authentication token provided by Discord. [See Discord Documentation](https://discord.com/developers/docs/getting-started#creating-an-app) | *Lots of letters, numbers, and symbols.* |
18-
| MONGO_URI | A MongoDB Connection URI. [See MongoDB Documentation](https://www.mongodb.com/docs/manual/reference/connection-string) | `mongo://username:password@host:27017` |
19-
| MONGO_DATABASE | The name of the MongoDB Database. | `aggregator` |
15+
| Name | Description | Example Value |
16+
|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
17+
| DISCORD_TOKEN | A bot authentication token provided by Discord. [See Discord Documentation](https://discord.com/developers/docs/getting-started#creating-an-app) | *Lots of letters, numbers, and symbols.* |
18+
| MONGO_URI | A MongoDB Connection URI. [See MongoDB Documentation](https://www.mongodb.com/docs/manual/reference/connection-string) | `mongo://username:password@host:27017` |
19+
| MONGO_DATABASE | The name of the MongoDB Database. | `aggregator` |
20+
| OWNER_SNOWFLAKE | Optional, required for /data. Discord ID of the owner of the bot. [See Discord Documentation](https://support.discord.com/hc/en-us/articles/206346498) | `521031433972744193` |

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
@Suppress("DSL_SCOPE_VIOLATION") // TODO: https://youtrack.jetbrains.com/issue/KTIJ-19369
22
plugins {
3+
alias(libs.plugins.serialization)
34
alias(libs.plugins.kotlin)
45
alias(libs.plugins.shadow)
56
application
67
}
78

8-
version = "1.3.0"
9+
version = "1.4.0"
910

1011
repositories {
1112
mavenCentral()
@@ -15,6 +16,7 @@ dependencies {
1516
implementation(libs.jda) { exclude("opus-java") }
1617
implementation(libs.mongo)
1718
implementation(libs.webhooks)
19+
implementation(libs.json)
1820
}
1921

2022
kotlin.jvmToolchain(17)

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ kotlin = "1.8.10"
44
mongo = "4.8.0"
55
shadow = "8.1.0"
66
webhooks = "0.8.2"
7+
json = "1.4.1"
78

89
[libraries]
910
jda = { module = "net.dv8tion:JDA", version.ref = "jda" }
1011
mongo = { module = "org.litote.kmongo:kmongo", version.ref = "mongo"}
1112
webhooks = { module = "club.minnced:discord-webhooks", version.ref = "webhooks" }
13+
json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "json" }
1214

1315
[plugins]
1416
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
1517
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
18+
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

src/main/kotlin/io/github/petercrawley/aggregator/Aggregator.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import club.minnced.discord.webhook.send.WebhookEmbed
55
import club.minnced.discord.webhook.send.WebhookMessageBuilder
66
import com.mongodb.ConnectionString
77
import com.mongodb.MongoClientSettings
8+
import io.github.petercrawley.aggregator.commands.DataCommand
89
import io.github.petercrawley.aggregator.commands.HelpCommand
910
import io.github.petercrawley.aggregator.commands.RedirectCommand
1011
import io.github.petercrawley.aggregator.commands.UptimeCommand
@@ -39,6 +40,7 @@ object Aggregator : ListenerAdapter() {
3940
private val token = getVariable("DISCORD_TOKEN")
4041
private val connectionString = getVariable("MONGO_URI")
4142
private val databaseName = getVariable("MONGO_DATABASE")
43+
val botOwnerSnowflake = System.getenv()["OWNER_SNOWFLAKE"]
4244

4345
private val jda = JDABuilder.createLight(token)
4446
.setEnabledIntents(listOf(
@@ -74,6 +76,7 @@ object Aggregator : ListenerAdapter() {
7476
commandDataList.add(commandData)
7577
}
7678

79+
registerCommand(DataCommand())
7780
registerCommand(HelpCommand())
7881
registerCommand(RedirectCommand())
7982
registerCommand(UptimeCommand())

src/main/kotlin/io/github/petercrawley/aggregator/CommandClass.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import io.github.petercrawley.aggregator.annotations.Default
55
import io.github.petercrawley.aggregator.annotations.EnabledFor
66
import io.github.petercrawley.aggregator.annotations.Parameter
77
import io.github.petercrawley.aggregator.annotations.Subcommand
8+
import net.dv8tion.jda.api.entities.Message.Attachment
89
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel
910
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
1011
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions
@@ -91,11 +92,15 @@ abstract class CommandClass {
9192
event.getOption(parameterName)?.asChannel as? TextChannel
9293
?: throw NullPointerException("Unable to get TextChannel for $parameterName.")
9394
}
95+
Attachment::class -> Pair(OptionType.ATTACHMENT) { event: SlashCommandInteractionEvent ->
96+
event.getOption(parameterName)?.asAttachment
97+
?: throw NullPointerException("Unable to get Attachment for $parameterName")
98+
}
9499
else -> throw Exception("Unsupported type ${jvmErasure.jvmName}")
95100
}
96101

97-
val optionData = OptionData(type, parameterName, parameterMeta.description, parameter.isOptional)
98-
.setChannelTypes(*parameterMeta.channelTypes)
102+
var optionData = OptionData(type, parameterName, parameterMeta.description, parameter.isOptional)
103+
if (type == OptionType.CHANNEL) optionData = optionData.setChannelTypes(*parameterMeta.channelTypes)
99104

100105
Pair(optionData, accessor)
101106
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package io.github.petercrawley.aggregator.commands
2+
3+
import io.github.petercrawley.aggregator.Aggregator
4+
import io.github.petercrawley.aggregator.CommandClass
5+
import io.github.petercrawley.aggregator.annotations.Command
6+
import io.github.petercrawley.aggregator.annotations.Parameter
7+
import io.github.petercrawley.aggregator.annotations.Subcommand
8+
import io.github.petercrawley.aggregator.data.Data
9+
import io.github.petercrawley.aggregator.messageEmbed
10+
import kotlinx.serialization.ExperimentalSerializationApi
11+
import kotlinx.serialization.encodeToString
12+
import kotlinx.serialization.json.Json
13+
import kotlinx.serialization.json.decodeFromStream
14+
import net.dv8tion.jda.api.entities.Message.Attachment
15+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
16+
import net.dv8tion.jda.api.utils.FileUpload
17+
import org.litote.kmongo.findOneById
18+
import kotlin.system.measureTimeMillis
19+
20+
@Command("data", "Import or export Aggregator data (Bot Owner Only)")
21+
class DataCommand : CommandClass() {
22+
@Suppress("Unused")
23+
@Subcommand("export", "Exports all Aggregator data")
24+
fun onDataExportAllCommand(event: SlashCommandInteractionEvent) {
25+
if (event.user.id != Aggregator.botOwnerSnowflake) {
26+
event.replyEmbeds(messageEmbed(description = "You do not have permission to use this command."))
27+
.setEphemeral(true)
28+
.queue()
29+
return
30+
}
31+
32+
val data = Data(Aggregator.targetConfiguration.find().toList().toTypedArray())
33+
34+
event.replyFiles(FileUpload.fromData(Json.encodeToString(data).toByteArray(), "data.json"))
35+
.setEphemeral(true)
36+
.queue()
37+
}
38+
39+
@Suppress("Unused")
40+
@OptIn(ExperimentalSerializationApi::class)
41+
@Subcommand("import", "Imports all Aggregator data")
42+
fun onDataImportAllCommand(event: SlashCommandInteractionEvent, @Parameter("data", "Data file exported from Aggregator") dataFile: Attachment) {
43+
if (event.user.id != Aggregator.botOwnerSnowflake) {
44+
event.replyEmbeds(messageEmbed(description = "You do not have permission to use this command."))
45+
.setEphemeral(true)
46+
.queue()
47+
return
48+
}
49+
50+
dataFile.proxy.download().thenAcceptAsync {
51+
var response = ""
52+
53+
val time = measureTimeMillis {
54+
val data = Json.decodeFromStream<Data>(it)
55+
56+
for (configuration in data.servers) {
57+
if (Aggregator.targetConfiguration.findOneById(configuration.targetChannel) != null) {
58+
response += "- *${configuration.targetChannel} already exists.*\n"
59+
continue
60+
}
61+
62+
Aggregator.targetConfiguration.insertOne(configuration)
63+
response += "- Added configuration for ${configuration.targetChannel}\n"
64+
}
65+
}
66+
67+
response += "Done in ${time}ms."
68+
event.reply(response).setEphemeral(true).queue()
69+
}
70+
}
71+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.github.petercrawley.aggregator.data
2+
3+
import io.github.petercrawley.aggregator.database.TargetConfiguration
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class Data(
8+
val servers: Array<TargetConfiguration> = arrayOf(),
9+
) {
10+
// Auto generated IntelliJ because it asked me too
11+
override fun equals(other: Any?): Boolean {
12+
if (this === other) return true
13+
if (javaClass != other?.javaClass) return false
14+
15+
other as Data
16+
17+
if (!servers.contentEquals(other.servers)) return false
18+
19+
return true
20+
}
21+
22+
override fun hashCode(): Int = servers.contentHashCode()
23+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.github.petercrawley.aggregator.database
22

3+
import kotlinx.serialization.Serializable
34
import org.bson.codecs.pojo.annotations.BsonId
45

6+
@Serializable
57
data class TargetConfiguration(@BsonId val targetChannel: Long, val sourceChannels: MutableList<Long>, val server: Long)

0 commit comments

Comments
 (0)