{"id":13410522,"url":"https://github.com/MinnDevelopment/jda-ktx","last_synced_at":"2025-03-14T16:32:21.094Z","repository":{"id":38360654,"uuid":"259655475","full_name":"MinnDevelopment/jda-ktx","owner":"MinnDevelopment","description":"Collection of useful Kotlin extensions for JDA","archived":false,"fork":false,"pushed_at":"2024-07-14T16:13:17.000Z","size":342,"stargazers_count":120,"open_issues_count":0,"forks_count":12,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-16T11:54:58.320Z","etag":null,"topics":["coroutines","discord","hacktoberfest","jda","kotlin","kotlin-coroutines"],"latest_commit_sha":null,"homepage":"https://minndevelopment.github.io/jda-ktx/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MinnDevelopment.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-28T14:09:40.000Z","updated_at":"2024-09-28T13:25:48.000Z","dependencies_parsed_at":"2024-01-01T17:30:32.631Z","dependency_job_id":"96027fb0-cc89-4916-a3cd-445f08641fc9","html_url":"https://github.com/MinnDevelopment/jda-ktx","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MinnDevelopment%2Fjda-ktx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MinnDevelopment%2Fjda-ktx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MinnDevelopment%2Fjda-ktx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MinnDevelopment%2Fjda-ktx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MinnDevelopment","download_url":"https://codeload.github.com/MinnDevelopment/jda-ktx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243610247,"owners_count":20318931,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coroutines","discord","hacktoberfest","jda","kotlin","kotlin-coroutines"],"created_at":"2024-07-30T20:01:07.460Z","updated_at":"2025-03-14T16:32:20.716Z","avatar_url":"https://github.com/MinnDevelopment.png","language":"Kotlin","readme":"\r\n[1]: https://github.com/discord-jda/jda\r\n[2]: https://github.com/kotlin/kotlinx.coroutines\r\n[3]: https://github.com/MinnDevelopment/jda-reactor\r\n\r\n[4]: https://github.com/MinnDevelopment/jda-ktx/blob/master/src/main/kotlin/dev/minn/jda/ktx/events/CoroutineEventManager.kt\r\n[5]: https://github.com/MinnDevelopment/jda-reactor/tree/master/src/main/java/club/minnced/jda/reactor/ReactiveEventManager.java\r\n[6]: https://github.com/MinnDevelopment/jda-ktx/blob/master/src/main/kotlin/dev/minn/jda/ktx/messages/builder.kt\r\n[7]: https://github.com/MinnDevelopment/strumbot\r\n[8]: https://minndevelopment.github.io/jda-ktx/\r\n\r\n[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-blue.svg?logo=kotlin)](http://kotlinlang.org)\r\n[![kotlinx-coroutines](https://img.shields.io/badge/kotlinx.coroutines-1.8.1-blue.svg?logo=kotlin)][2]\r\n[![JDA](https://img.shields.io/badge/JDA-5.0.0-blue.svg)][1]\r\n[![docs](https://img.shields.io/github/deployments/minndevelopment/jda-ktx/github-pages?label=docs)][8]\r\n[ ![](https://img.shields.io/maven-central/v/club.minnced/jda-ktx) ](https://search.maven.org/artifact/club.minnced/jda-ktx)\r\n\r\n# jda-ktx\r\n\r\nCollection of useful Kotlin extensions for [JDA][1].\r\nGreat in combination with [kotlinx-coroutines][2] and [jda-reactor][3].\r\n\r\n## Required Dependencies\r\n\r\n- Kotlin **2.0.0**\r\n- kotlinx.coroutines **1.8.1**\r\n- JDA **5.0.0**\r\n\r\n## Examples\r\n\r\nYou can look at my own bot ([strumbot][7]) for inspiration, or look at the examples listed here.\r\nThe most useful feature of this library is the [CoroutineEventManager][4] which adds the ability to use\r\nsuspending functions in your event handlers.\r\n\r\n```kotlin\r\n// enableCoroutines (default true) changes the event manager to CoroutineEventManager\r\n// this event manager uses a default scope generated by getDefaultScope() but can be configured to use a custom scope if you set it manually\r\nval jda = light(\"token\", enableCoroutines=true) {\r\n    intents += listOf(GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT)\r\n}\r\n\r\n// This can only be used with the CoroutineEventManager\r\njda.listener\u003cMessageReceivedEvent\u003e {\r\n    val guild = it.guild\r\n    val channel = it.channel\r\n    val message = it.message\r\n    val content = message.contentRaw\r\n\r\n    if (content.startsWith(\"!profile\")) {\r\n        // Send typing indicator and wait for it to arrive\r\n        channel.sendTyping().await()\r\n        val user = message.mentionedUsers.firstOrNull() ?: run {\r\n            // Try loading user through prefix loading\r\n            val matches = guild.retrieveMembersByPrefix(content.substringAfter(\"!profile \"), 1).await()\r\n            // Take first result, or null\r\n            matches.firstOrNull()\r\n        }\r\n\r\n        if (user == null) // unknown user for name\r\n            channel.send(\"${it.author.asMention}, I cannot find a user for your query!\").queue()\r\n        else // load profile and send it as embed\r\n            channel.send(\"${it.author.asMention}, here is the user profile:\", embeds=profile(user).into()).queue()\r\n    }\r\n}\r\n\r\njda.onCommand(\"ban\", timeout=2.minutes) { event -\u003e // 2 minute timeout listener\r\n    val user = event.getOption\u003cUser\u003e(\"user\")!!\r\n    val confirm = danger(\"${user.id}:ban\", \"Confirm\")\r\n    event.reply_(\r\n        \"Are you sure you want to ban **${user.asTag}**?\",\r\n        components=confirm.into(),\r\n        ephemeral=true\r\n    ).queue()\r\n\r\n    withTimeoutOrNull(1.minutes) { // 1 minute scoped timeout\r\n        val pressed = event.user.awaitButton(confirm) // await for user to click button\r\n        pressed.deferEdit().queue() // Acknowledge the button press\r\n        event.guild.ban(user, 0).queue() // the button is pressed -\u003e execute action\r\n    } ?: event.hook.editMessage(/*id=\"@original\" is default */content=\"Timed out.\", components=emptyList()).queue()\r\n}\r\n\r\njda.onButton(\"hello\") { // Button that says hello\r\n    it.reply_(\"Hello :)\").queue()\r\n}\r\n```\r\n\r\n### Coroutine Extensions\r\n\r\nI've added a few suspending extension functions to various JDA components.\r\nNone of these extensions require the `CoroutineEventManager`!\r\n\r\nTo use `await\u003cEvent\u003e` and `awaitMessage` the event manager must support either `EventListener` or `@SubscribeEvent`,\r\nthe [`ReactiveEventManager`][5] and [`CoroutineEventManager`][4] both support this.\r\n\r\n```kotlin\r\n/* Async Operations */\r\n\r\n// Await RestAction result\r\nsuspend fun \u003cT\u003e RestAction\u003cT\u003e.await()\r\n// Await Task result (retrieveMembersByPrefix)\r\nsuspend fun \u003cT\u003e Task\u003cT\u003e.await()\r\n\r\n/* Event Waiter */\r\n\r\n// Await specific event\r\nsuspend fun \u003cT : GenericEvent\u003e JDA.await(filter: (T) -\u003e Boolean = { true })\r\n// Await specific event\r\nsuspend fun \u003cT : GenericEvent\u003e ShardManager.await(filter: (T) -\u003e Boolean = { true })\r\n// Await message from specific channel (filter by user and/or filter function)\r\nsuspend fun MessageChannel.awaitMessage(author: User? = null, filter: (Message) -\u003e Boolean = { true }): Message\r\n\r\n// Flow representation for PaginationAction\r\nfun \u003cT, M: PaginationAction\u003cT, M\u003e\u003e M.asFlow(): Flow\u003cT\u003e\r\n```\r\n\r\n### Delegates\r\n\r\nThis library implements [delegate properties](https://kotlinlang.org/docs/reference/delegated-properties.html) which can be used to safely keep references of JDA entities such as users/channels.\r\nThese delegates can be used with the [`ref()`](https://github.com/MinnDevelopment/jda-ktx/blob/master/src/main/kotlin/dev/minn/jda/ktx/util/proxies.kt) extension function:\r\n\r\n```kotlin\r\nclass Foo(guild: Guild) {\r\n    val guild : Guild by guild.ref()\r\n}\r\n```\r\n\r\nYou can also use the `SLF4J` delegate to initialize loggers.\r\n\r\n```kotlin\r\nobject Listener : ListenerAdapter() {\r\n    private val log by SLF4J \r\n\r\n    override fun onMessageReceived(event: MessageReceivedEvent) {\r\n        log.info(\"[{}] {}: {}\", event.channel.name, event.author.asTag, event.message.contentDisplay)\r\n    }\r\n}\r\n```\r\n\r\n### Embed- and MessageBuilders\r\n\r\nThis library also provides some useful builder alternatives which can be used instead of the default `MessageBuilder` and `EmbedBuilder` from JDA.\r\n\r\nYou can see both builders in [builders.kt][6].\r\n\r\n**Example**\r\n\r\n```kotlin\r\nval embed = Embed(title=\"Hello Friend\", description=\"Goodbye Friend\")\r\n```\r\n\r\nOr the builder function style:\r\n\r\n```kotlin\r\nval embed = Embed { // Builds a MessageEmbed\r\n    title = \"Hello Friend\"\r\n    description = \"Goodbye Friend\"\r\n    field {\r\n        name = \"How good is this example?\"\r\n        value = \"5 :star:\"\r\n        inline = false\r\n    }\r\n    timestamp = Instant.now()\r\n    color = 0xFF0000\r\n}\r\n\r\nval message = MessageCreate { // Builds MessageCreateData\r\n    embeds += Embed(\"Ban Confirmation\")\r\n    components += row(\r\n        success(id=\"approve:ban:$userId\", label=\"Approve\"),\r\n        danger(id=\"deny:ban:$userId\", label=\"Deny\")\r\n    )\r\n}\r\n```\r\n\r\n### Command and SelectMenu Builders\r\n\r\n```kotlin\r\njda.updateCommands {\r\n    slash(\"ban\", \"Ban a user\") {\r\n        restrict(guild=true, Permission.BAN_MEMBERS) // guild only and requires ban permission\r\n        option\u003cUser\u003e(\"user\", \"The user to ban\", true)\r\n        option\u003cString\u003e(\"reason\", \"Why to ban this user\")\r\n        option\u003cInt\u003e(\"duration\", \"For how long to ban this user\") {\r\n            choice(\"1 day\", 1)\r\n            choice(\"1 week\", 7)\r\n            choice(\"1 month\", 31)\r\n        }\r\n    }\r\n\r\n    slash(\"mod\", \"Moderation commands\") {\r\n        restrict(guild=true, Permission.MODERATE_MEMBERS) // you cannot apply this on subcommands due to discord's design!\r\n        subcommand(\"ban\", \"Ban a user\") {\r\n            option\u003cUser\u003e(\"user\", \"The user to ban\", true)\r\n            option\u003cString\u003e(\"reason\", \"Why to ban this user\")\r\n            option\u003cInt\u003e(\"duration\", \"For how long to ban this user\") {\r\n                choice(\"1 day\", 1)\r\n                choice(\"1 week\", 7)\r\n                choice(\"1 month\", 31)\r\n            }\r\n        }\r\n\r\n        subcommand(\"prune\", \"Prune messages\") {\r\n            option\u003cInt\u003e(\"amount\", \"The amount to delete from 2-100, default 50\")\r\n        }\r\n    }\r\n}.queue()\r\n\r\njda.upsertCommand(\"prune\", \"Prune messages\") {\r\n    restrict(guild=true, Permission.MESSAGE_MANAGE) // guild only and requires message manage perms\r\n    option\u003cInt\u003e(\"amount\", \"The amount to delete from 2-100, default 50\")\r\n}.queue()\r\n\r\nval menu = StringSelectMenu(\"menu:class\") {\r\n    option(\"Frost Mage\", \"mage-frost\", emoji=FROST_SPEC, default=true)\r\n    option(\"Fire Mage\", \"mage-fire\", emoji=FIRE_SPEC)\r\n    option(\"Arcane Mage\", \"mage-arcane\", emoji=ARCANE_SPEC)\r\n}\r\n```\r\n\r\n\r\n### Buttons\r\n\r\n```kotlin\r\njda.upsertCommand(\"ban\", \"ban a user\") {\r\n    option\u003cMember\u003e(\"member\", \"The member to ban\", true)\r\n    option\u003cString\u003e(\"reason\", \"The ban reason\")\r\n}.queue()\r\n\r\njda.onCommand(\"ban\") { event -\u003e\r\n    if (event.user.asTag != \"Minn#6688\") return@onCommand\r\n    val guild = event.guild!!\r\n    val member = event.getOption\u003cUser\u003e(\"member\")!!\r\n    val reason = event.getOption\u003cString\u003e(\"reason\")\r\n\r\n    // Buttons will timeout after 15 minutes by default\r\n    val accept = jda.button(label = \"Accept\", style = SUCCESS, user = event.user) {\r\n        guild.ban(member, 0, reason).queue()\r\n        it.editMessage(\"${event.user.asTag} banned ${member.asTag}\")\r\n            .setActionRows() // remove buttons from message\r\n            .queue()\r\n    }\r\n    val deny = jda.button(label = \"Deny\", style = DANGER, user = event.user) { butt -\u003e\r\n        butt.hook.deleteOriginal().queue() // automatically acknowledged if callback does not do it\r\n    }\r\n\r\n    event.reply_(\"Are you sure?\")\r\n        .addActionRow(accept, deny) // send your buttons\r\n        .queue()\r\n}\r\n\r\n// or a global listener\r\njda.onButton(\"accept\") { event -\u003e\r\n    event.reply(\"You accepted :)\").queue()\r\n}\r\n```\r\n\r\n### Sending Messages\r\n\r\nThis library also adds some more kotlin idiomatic message send/edit extensions which rely on named parameters.\r\nThese named parameters also support defaults, which can be modified by `SendDefaults` and `MessageEditDefaults`.\r\n\r\nIn order to avoid overload conflicts with methods from JDA, some functions may use a suffixed `_` such as `reply_` or `editMessage_`.\r\nThis is simply done to prevent you from accidentally calling the wrong overload which doesn't use the defaults of this library.\r\nIf you don't care about that, you can simply add an import alias with `import dev.minn.jda.ktx.message.reply_ as reply`.\r\n\r\nExample:\r\n\r\n```kt\r\nSendDefaults.ephemeral = true // \u003c- all reply_ calls set ephemeral=true by default\r\nMessageEditDefaults.replace = false // \u003c- only apply explicitly set parameters (default behavior)\r\njda.onCommand(\"ban\") { event -\u003e\r\n    if (!event.member.hasPermission(Permission.BAN_MEMBERS))\r\n        return@onCommand event.reply_(\"You can't do that!\").queue()\r\n    \r\n    event.reply_(\"Are you sure?\", components=danger(\"ban\", \"Yes!\").into())\r\n    val interaction = event.user.awaitButton(\"ban\")\r\n    val user = event.getOption\u003cUser\u003e(\"target\")!!\r\n    event.guild!!.ban(user, 0).queue()\r\n    interaction.editMessage_(\"Successfully banned user\", components=emptyList()).queue()\r\n}\r\n```\r\n\r\n\r\n## Download\r\n\r\n[ ![](https://img.shields.io/maven-central/v/net.dv8tion/JDA?color=blue\u0026label=JDA) ](https://search.maven.org/artifact/net.dv8tion/JDA) \r\n[ ![](https://img.shields.io/maven-central/v/club.minnced/jda-ktx?label=jda-ktx) ](https://search.maven.org/artifact/club.minnced/jda-ktx)\r\n\r\nThis project is available on maven central.\r\n\r\n### Gradle\r\n\r\n```gradle\r\nrepositories {\r\n    mavenCentral()\r\n}\r\n\r\ndependencies {\r\n    implementation(\"net.dv8tion:JDA:${JDA_VERSION}\")\r\n    implementation(\"club.minnced:jda-ktx:${VERSION}\")\r\n}\r\n```\r\n\r\n### Maven\r\n\r\n\r\n```xml\r\n\u003cdependency\u003e\r\n  \u003cgroupId\u003enet.dv8tion\u003c/groupId\u003e\r\n  \u003cartifactId\u003eJDA\u003c/artifactId\u003e\r\n  \u003cversion\u003e$JDA_VERSION\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n\u003cdependency\u003e\r\n  \u003cgroupId\u003eclub.minnced\u003c/groupId\u003e\r\n  \u003cartifactId\u003ejda-ktx\u003c/artifactId\u003e\r\n  \u003cversion\u003e$VERSION\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n```\r\n","funding_links":[],"categories":["Libraries"],"sub_categories":["Kotlin"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMinnDevelopment%2Fjda-ktx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMinnDevelopment%2Fjda-ktx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMinnDevelopment%2Fjda-ktx/lists"}