From 0c60bc8f9fab59afe71f59a0262d8f0cece2c61d Mon Sep 17 00:00:00 2001
From: zt64 <31907977+zt64@users.noreply.github.com>
Date: Wed, 15 May 2024 01:11:50 -0400
Subject: [PATCH 1/5] overhaul project setup
---
.editorconfig | 36 +++++++
.../src/main/AndroidManifest.xml | 2 -
.../github/yournamehere/MyFirstCommand.java | 57 -----------
.../java/MyFirstPatch/build.gradle.kts | 19 ----
.../MyFirstPatch/src/main/AndroidManifest.xml | 2 -
.../com/github/yournamehere/MyFirstPatch.java | 80 ---------------
.../kotlin/MyFirstCommand/build.gradle.kts | 19 ----
.../src/main/AndroidManifest.xml | 2 -
.../MyFirstPatch/src/main/AndroidManifest.xml | 2 -
README.md | 11 ++-
build.gradle.kts | 97 ++-----------------
gradle.properties | 8 +-
gradle/libs.versions.toml | 19 ++++
gradle/wrapper/gradle-wrapper.properties | 6 +-
.../MyFirstCommand/build.gradle.kts | 8 +-
.../com/github/yournamehere/MyFirstCommand.kt | 26 +++--
.../MyFirstPatch/build.gradle.kts | 8 +-
.../com/github/yournamehere/MyFirstPatch.kt | 15 ++-
plugin/build.gradle.kts | 76 +++++++++++++++
settings.gradle.kts | 43 ++++----
20 files changed, 217 insertions(+), 319 deletions(-)
create mode 100644 .editorconfig
delete mode 100644 ExamplePlugins/java/MyFirstCommand/src/main/AndroidManifest.xml
delete mode 100644 ExamplePlugins/java/MyFirstCommand/src/main/java/com/github/yournamehere/MyFirstCommand.java
delete mode 100644 ExamplePlugins/java/MyFirstPatch/build.gradle.kts
delete mode 100644 ExamplePlugins/java/MyFirstPatch/src/main/AndroidManifest.xml
delete mode 100644 ExamplePlugins/java/MyFirstPatch/src/main/java/com/github/yournamehere/MyFirstPatch.java
delete mode 100644 ExamplePlugins/kotlin/MyFirstCommand/build.gradle.kts
delete mode 100644 ExamplePlugins/kotlin/MyFirstCommand/src/main/AndroidManifest.xml
delete mode 100644 ExamplePlugins/kotlin/MyFirstPatch/src/main/AndroidManifest.xml
create mode 100644 gradle/libs.versions.toml
rename {ExamplePlugins/java => plugin}/MyFirstCommand/build.gradle.kts (90%)
rename {ExamplePlugins/kotlin => plugin}/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt (73%)
rename {ExamplePlugins/kotlin => plugin}/MyFirstPatch/build.gradle.kts (90%)
rename {ExamplePlugins/kotlin => plugin}/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt (87%)
create mode 100644 plugin/build.gradle.kts
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..2e691bb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,36 @@
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = false
+max_line_length = 100
+tab_width = 4
+ij_continuation_indent_size = 8
+ij_any_line_comment_at_first_column = false
+ij_any_line_comment_add_space = true
+ij_smart_tabs = false
+ij_wrap_on_typing = false
+
+[plugin/Frecents/src/main/java/*.java]
+ij_formatter_enabled = false
+
+[*.{kt,kts}]
+ij_kotlin_allow_trailing_comma = false
+ij_kotlin_allow_trailing_comma_on_call_site = false
+ktlint_code_style = ktlint_official
+ktlint_experimental = enabled
+ktlint_standard_no-wildcard-imports = disabled
+ktlint_standard_string-template-indent = disabled
+ktlint_standard_multiline-expression-wrapping = disabled
+ktlint_standard_function-expression-body = disabled
+ktlint_function_signature_body_expression_wrapping = default
+ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 6
+ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 3
+ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 6
+
+[*.xml]
+ij_xml_continuation_indent_size = 4
+
+[*.yml]
+indent_size = 2
\ No newline at end of file
diff --git a/ExamplePlugins/java/MyFirstCommand/src/main/AndroidManifest.xml b/ExamplePlugins/java/MyFirstCommand/src/main/AndroidManifest.xml
deleted file mode 100644
index a45f28d..0000000
--- a/ExamplePlugins/java/MyFirstCommand/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/ExamplePlugins/java/MyFirstCommand/src/main/java/com/github/yournamehere/MyFirstCommand.java b/ExamplePlugins/java/MyFirstCommand/src/main/java/com/github/yournamehere/MyFirstCommand.java
deleted file mode 100644
index 0b6c233..0000000
--- a/ExamplePlugins/java/MyFirstCommand/src/main/java/com/github/yournamehere/MyFirstCommand.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.github.yournamehere;
-
-import android.content.Context;
-import com.aliucord.Utils;
-import com.aliucord.annotations.AliucordPlugin;
-import com.aliucord.api.CommandsAPI;
-import com.aliucord.entities.Plugin;
-import com.discord.api.commands.ApplicationCommandType;
-
-import java.util.Arrays;
-
-// Aliucord Plugin annotation. Must be present on the main class of your plugin
-@AliucordPlugin(requiresRestart = false /* Whether your plugin requires a restart after being installed/updated */)
-// Plugin class. Must extend Plugin and override start and stop
-// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
-public class MyFirstCommand extends Plugin {
- @Override
- public void start(Context context) {
- // Register a command with the name hello and description "My first command!" and no arguments.
- // Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/2_commands.md
- commands.registerCommand("hello", "My first command!", ctx -> {
- // Just return a command result with hello world as the content
- return new CommandsAPI.CommandResult(
- "Hello World!",
- null, // List of embeds
- false // Whether to send visible for everyone
- );
- });
-
- // A bit more advanced command with arguments
- commands.registerCommand(
- "hellowitharguments",
- "Hello World but with arguments!",
- Arrays.asList(
- Utils.createCommandOption(ApplicationCommandType.STRING, "name", "Person to say hello to"),
- Utils.createCommandOption(ApplicationCommandType.USER, "user", "User to say hello to")
- ),
- ctx -> {
- // Check if a user argument was passed
- if (ctx.containsArg("user")) {
- var user = ctx.getRequiredUser("user");
- return new CommandsAPI.CommandResult("Hello " + user.getUsername() + "!");
- } else {
- // Returns either the argument value if present, or the defaultValue ("World" in this case)
- var name = ctx.getStringOrDefault("name", "World");
- return new CommandsAPI.CommandResult("Hello " + name + "!");
- }
- }
- );
- }
-
- @Override
- public void stop(Context context) {
- // Unregister all commands
- commands.unregisterAll();
- }
-}
diff --git a/ExamplePlugins/java/MyFirstPatch/build.gradle.kts b/ExamplePlugins/java/MyFirstPatch/build.gradle.kts
deleted file mode 100644
index 1ebb20c..0000000
--- a/ExamplePlugins/java/MyFirstPatch/build.gradle.kts
+++ /dev/null
@@ -1,19 +0,0 @@
-version = "1.0.0" // Plugin version. Increment this to trigger the updater
-description = "My first patch!" // Plugin description that will be shown to user
-
-aliucord {
- // Changelog of your plugin
- changelog.set("""
- Some changelog
- """.trimIndent())
- // Image or Gif that will be shown at the top of your changelog page
- // changelogMedia.set("https://cool.png")
-
- // Add additional authors to this plugin
- // author("Name", 0)
- // author("Name", 0)
-
- // Excludes this plugin from the updater, meaning it won't show up for users.
- // Set this if the plugin is unfinished
- excludeFromUpdaterJson.set(true)
-}
diff --git a/ExamplePlugins/java/MyFirstPatch/src/main/AndroidManifest.xml b/ExamplePlugins/java/MyFirstPatch/src/main/AndroidManifest.xml
deleted file mode 100644
index a45f28d..0000000
--- a/ExamplePlugins/java/MyFirstPatch/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/ExamplePlugins/java/MyFirstPatch/src/main/java/com/github/yournamehere/MyFirstPatch.java b/ExamplePlugins/java/MyFirstPatch/src/main/java/com/github/yournamehere/MyFirstPatch.java
deleted file mode 100644
index c7c614f..0000000
--- a/ExamplePlugins/java/MyFirstPatch/src/main/java/com/github/yournamehere/MyFirstPatch.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.github.yournamehere;
-
-import android.content.Context;
-import com.aliucord.CollectionUtils;
-import com.aliucord.annotations.AliucordPlugin;
-import com.aliucord.entities.MessageEmbedBuilder;
-import com.aliucord.entities.Plugin;
-import com.aliucord.patcher.*;
-import com.aliucord.wrappers.embeds.MessageEmbedWrapper;
-import com.discord.models.user.CoreUser;
-import com.discord.stores.StoreUserTyping;
-import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemMessage;
-import com.discord.widgets.chat.list.entries.ChatListEntry;
-import com.discord.widgets.chat.list.entries.MessageEntry;
-
-// Aliucord Plugin annotation. Must be present on the main class of your plugin
-@AliucordPlugin(requiresRestart = false /* Whether your plugin requires a restart after being installed/updated */)
-// Plugin class. Must extend Plugin and override start and stop
-// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
-public class MyFirstPatch extends Plugin {
- @Override
- public void start(Context context) throws Throwable {
- // Patch that adds an embed with message statistics to each message
- // Patched method is WidgetChatListAdapterItemMessage.onConfigure(int type, ChatListEntry entry)
- patcher.patch(
- // see https://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
- WidgetChatListAdapterItemMessage.class.getDeclaredMethod("onConfigure", int.class, ChatListEntry.class),
- new Hook(param -> { // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
- // Obtain the second argument passed to the method, so the ChatListEntry
- // Because this is a Message item, it will always be a MessageEntry, so cast it to that
- var entry = (MessageEntry) param.args[1];
-
- // You need to be careful when messing with messages, because they may be loading
- // (user sent a message, and it is currently sending)
- if (entry.getMessage().isLoading()) return;
-
- // Now add an embed with the statistics
-
- // This method may be called multiple times per message, e.g. if it is edited,
- // so first remove existing embeds
- CollectionUtils.removeIf(entry.getMessage().getEmbeds(), e -> {
- // MessageEmbed is an obfuscated class. However, Aliucord provides wrappers for commonly used
- // obfuscated classes, the MessageEmbedWrapper in this case.
- return "Message Statistics".equals(MessageEmbedWrapper.getTitle(e));
- });
-
- // Creating embeds is a pain, so Aliucord provides a convenient builder
- var embed = new MessageEmbedBuilder().
- setTitle("Message Statistics")
- .addField("Length", entry.getMessage().getContent() != null ? Integer.toString(entry.getMessage().getContent().length()) : "0", false)
- .addField("ID", Long.toString(entry.getMessage().getId()), false).build();
-
- entry.getMessage().getEmbeds().add(embed);
- })
- );
-
- // Patch that renames Juby to JoobJoob
- patcher.patch(
- CoreUser.class.getDeclaredMethod("getUsername"),
- new PreHook(param -> { // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
- if (((CoreUser) param.thisObject).getId() == 925141667688878090L) {
- // setResult() in before patches skips original method invocation
- param.setResult("JoobJoob");
- }
- })
- );
-
- // Patch that hides your typing status by replacing the method and simply doing nothing
- patcher.patch(
- StoreUserTyping.class.getDeclaredMethod("setUserTyping", long.class),
- InsteadHook.DO_NOTHING
- );
- }
-
- @Override
- public void stop(Context context) {
- // Remove all patches
- patcher.unpatchAll();
- }
-}
diff --git a/ExamplePlugins/kotlin/MyFirstCommand/build.gradle.kts b/ExamplePlugins/kotlin/MyFirstCommand/build.gradle.kts
deleted file mode 100644
index 355488d..0000000
--- a/ExamplePlugins/kotlin/MyFirstCommand/build.gradle.kts
+++ /dev/null
@@ -1,19 +0,0 @@
-version = "1.0.0" // Plugin version. Increment this to trigger the updater
-description = "My first commands!" // Plugin description that will be shown to user
-
-aliucord {
- // Changelog of your plugin
- changelog.set("""
- Some changelog
- """.trimIndent())
- // Image or Gif that will be shown at the top of your changelog page
- // changelogMedia.set("https://cool.png")
-
- // Add additional authors to this plugin
- // author("Name", 0)
- // author("Name", 0)
-
- // Excludes this plugin from the updater, meaning it won't show up for users.
- // Set this if the plugin is unfinished
- excludeFromUpdaterJson.set(true)
-}
diff --git a/ExamplePlugins/kotlin/MyFirstCommand/src/main/AndroidManifest.xml b/ExamplePlugins/kotlin/MyFirstCommand/src/main/AndroidManifest.xml
deleted file mode 100644
index a45f28d..0000000
--- a/ExamplePlugins/kotlin/MyFirstCommand/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/ExamplePlugins/kotlin/MyFirstPatch/src/main/AndroidManifest.xml b/ExamplePlugins/kotlin/MyFirstPatch/src/main/AndroidManifest.xml
deleted file mode 100644
index a45f28d..0000000
--- a/ExamplePlugins/kotlin/MyFirstPatch/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/README.md b/README.md
index e00b299..0477804 100644
--- a/README.md
+++ b/README.md
@@ -4,17 +4,18 @@ Template for an [Aliucord](https://github.com/Aliucord) plugin repo
⚠️ Make sure you check "Include all branches" when using this template
-
## Getting started with writing your first plugin
-This template includes 2 example plugins demonstrating commands and patches which you can find in the ExamplePlugins folder.
+This template includes 2 example plugins demonstrating commands and patches which you can find in
+the ExamplePlugins folder.
1. Open the root build.gradle.kts, read the comments and replace all the placeholders
2. Familiarize yourself with the project structure. Most files are commented
3. Build or deploy your first plugin using:
- - Windows: `.\gradlew.bat MyFirstCommand:make` or `.\gradlew.bat MyFirstCommand:deployWithAdb`
- - Linux & Mac: `./gradlew MyFirstCommand:make` or `./gradlew MyFirstCommand:deployWithAdb`
+ - Windows: `.\gradlew.bat MyFirstCommand:make` or `.\gradlew.bat MyFirstCommand:deployWithAdb`
+ - Linux & Mac: `./gradlew MyFirstCommand:make` or `./gradlew MyFirstCommand:deployWithAdb`
## License
-Everything in this repo is released into the public domain. You may use it however you want with no conditions whatsoever
+Everything in this repo is released into the public domain. You may use it however you want with no
+conditions whatsoever
diff --git a/build.gradle.kts b/build.gradle.kts
index 0a34669..ea3b6d2 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,89 +1,8 @@
-import com.aliucord.gradle.AliucordExtension
-import com.android.build.gradle.BaseExtension
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- // Aliucords Maven repo which contains our tools and dependencies
- maven("https://maven.aliucord.com/snapshots")
- // Shitpack which still contains some Aliucord dependencies for now. TODO: Remove
- maven("https://jitpack.io")
- }
-
- dependencies {
- classpath("com.android.tools.build:gradle:7.0.4")
- // Aliucord gradle plugin which makes everything work and builds plugins
- classpath("com.aliucord:gradle:main-SNAPSHOT")
- // Kotlin support. Remove if you want to use Java
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21")
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- maven("https://maven.aliucord.com/snapshots")
- }
-}
-
-fun Project.aliucord(configuration: AliucordExtension.() -> Unit) = extensions.getByName("aliucord").configuration()
-
-fun Project.android(configuration: BaseExtension.() -> Unit) = extensions.getByName("android").configuration()
-
-subprojects {
- apply(plugin = "com.android.library")
- apply(plugin = "com.aliucord.gradle")
- // Remove if using Java
- apply(plugin = "kotlin-android")
-
- // Fill out with your info
- aliucord {
- author("DISCORD USERNAME", 123456789L)
- updateUrl.set("https://raw.githubusercontent.com/USERNAME/REPONAME/builds/updater.json")
- buildUrl.set("https://raw.githubusercontent.com/USERNAME/REPONAME/builds/%s.zip")
- }
-
- android {
- compileSdkVersion(31)
-
- defaultConfig {
- minSdk = 24
- targetSdk = 31
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
-
- tasks.withType {
- kotlinOptions {
- jvmTarget = "11" // Required
- // Disables some unnecessary features
- freeCompilerArgs = freeCompilerArgs +
- "-Xno-call-assertions" +
- "-Xno-param-assertions" +
- "-Xno-receiver-assertions"
- }
- }
- }
-
- dependencies {
- val discord by configurations
- val implementation by configurations
-
- // Stubs for all Discord classes
- discord("com.discord:discord:aliucord-SNAPSHOT")
- implementation("com.aliucord:Aliucord:main-SNAPSHOT")
-
- implementation("androidx.appcompat:appcompat:1.4.0")
- implementation("com.google.android.material:material:1.4.0")
- implementation("androidx.constraintlayout:constraintlayout:2.1.2")
- }
-}
-
-task("clean") {
- delete(rootProject.buildDir)
-}
+@file:Suppress("DSL_SCOPE_VIOLATION")
+
+plugins {
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.aliucord) apply false
+ alias(libs.plugins.ktlint) apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 01b80d7..5a653f2 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,14 +6,14 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options=--illegal-access=permit
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
+org.gradle.parallel=true
+org.gradle.configureondemand=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
+android.suppressUnsupportedCompileSdk=34
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..6dc81ea
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,19 @@
+[versions]
+discord = "aliucord-SNAPSHOT"
+aliucord = "main-SNAPSHOT"
+#noinspection GradleDependency Aliucord uses Kotlin 1.5.21
+kotlin = "1.5.21"
+android = "7.4.2"
+ktlintPlugin = "12.1.1"
+#noinspection GradleDependency Newer versions incompatible with other plugins
+ktlint = "0.50.0"
+
+[libraries]
+discord = { module = "com.discord:discord", version.ref = "discord" }
+aliucord = { module = "com.aliucord:Aliucord", version.ref = "aliucord" }
+
+[plugins]
+android-library = { id = "com.android.library", version.ref = "android" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+aliucord = { id = "com.aliucord.gradle", version.ref = "aliucord" }
+ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintPlugin" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index da0e964..3c39e7f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Jun 09 16:18:24 EST 2021
+#Fri Jul 28 20:21:56 EDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/ExamplePlugins/java/MyFirstCommand/build.gradle.kts b/plugin/MyFirstCommand/build.gradle.kts
similarity index 90%
rename from ExamplePlugins/java/MyFirstCommand/build.gradle.kts
rename to plugin/MyFirstCommand/build.gradle.kts
index 355488d..f952125 100644
--- a/ExamplePlugins/java/MyFirstCommand/build.gradle.kts
+++ b/plugin/MyFirstCommand/build.gradle.kts
@@ -3,9 +3,11 @@ description = "My first commands!" // Plugin description that will be shown to u
aliucord {
// Changelog of your plugin
- changelog.set("""
+ changelog.set(
+ """
Some changelog
- """.trimIndent())
+ """.trimIndent()
+ )
// Image or Gif that will be shown at the top of your changelog page
// changelogMedia.set("https://cool.png")
@@ -16,4 +18,4 @@ aliucord {
// Excludes this plugin from the updater, meaning it won't show up for users.
// Set this if the plugin is unfinished
excludeFromUpdaterJson.set(true)
-}
+}
\ No newline at end of file
diff --git a/ExamplePlugins/kotlin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt b/plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
similarity index 73%
rename from ExamplePlugins/kotlin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
rename to plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
index 59577a4..f794da0 100644
--- a/ExamplePlugins/kotlin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
+++ b/plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
@@ -8,9 +8,11 @@ import com.aliucord.entities.Plugin
import com.discord.api.commands.ApplicationCommandType
// Aliucord Plugin annotation. Must be present on the main class of your plugin
-@AliucordPlugin(requiresRestart = false /* Whether your plugin requires a restart after being installed/updated */)
// Plugin class. Must extend Plugin and override start and stop
// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
+@AliucordPlugin(
+ requiresRestart = false // Whether your plugin requires a restart after being installed/updated
+)
class MyFirstCommand : Plugin() {
override fun start(context: Context) {
// Register a command with the name hello and description "My first command!" and no arguments.
@@ -25,10 +27,22 @@ class MyFirstCommand : Plugin() {
}
// A bit more advanced command with arguments
- commands.registerCommand("hellowitharguments", "Hello World but with arguments!", listOf(
- Utils.createCommandOption(ApplicationCommandType.STRING, "name", "Person to say hello to"),
- Utils.createCommandOption(ApplicationCommandType.USER, "user", "User to say hello to")
- )) { ctx ->
+ commands.registerCommand(
+ "hellowitharguments",
+ "Hello World but with arguments!",
+ listOf(
+ Utils.createCommandOption(
+ ApplicationCommandType.STRING,
+ "name",
+ "Person to say hello to"
+ ),
+ Utils.createCommandOption(
+ ApplicationCommandType.USER,
+ "user",
+ "User to say hello to"
+ )
+ )
+ ) { ctx ->
// Check if a user argument was passed
if (ctx.containsArg("user")) {
val user = ctx.getRequiredUser("user")
@@ -45,4 +59,4 @@ class MyFirstCommand : Plugin() {
// Unregister our commands
commands.unregisterAll()
}
-}
+}
\ No newline at end of file
diff --git a/ExamplePlugins/kotlin/MyFirstPatch/build.gradle.kts b/plugin/MyFirstPatch/build.gradle.kts
similarity index 90%
rename from ExamplePlugins/kotlin/MyFirstPatch/build.gradle.kts
rename to plugin/MyFirstPatch/build.gradle.kts
index 1ebb20c..e83932f 100644
--- a/ExamplePlugins/kotlin/MyFirstPatch/build.gradle.kts
+++ b/plugin/MyFirstPatch/build.gradle.kts
@@ -3,9 +3,11 @@ description = "My first patch!" // Plugin description that will be shown to user
aliucord {
// Changelog of your plugin
- changelog.set("""
+ changelog.set(
+ """
Some changelog
- """.trimIndent())
+ """.trimIndent()
+ )
// Image or Gif that will be shown at the top of your changelog page
// changelogMedia.set("https://cool.png")
@@ -16,4 +18,4 @@ aliucord {
// Excludes this plugin from the updater, meaning it won't show up for users.
// Set this if the plugin is unfinished
excludeFromUpdaterJson.set(true)
-}
+}
\ No newline at end of file
diff --git a/ExamplePlugins/kotlin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt b/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
similarity index 87%
rename from ExamplePlugins/kotlin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
rename to plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
index bab47b9..42faef7 100644
--- a/ExamplePlugins/kotlin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
+++ b/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
@@ -13,9 +13,11 @@ import com.discord.widgets.chat.list.entries.ChatListEntry
import com.discord.widgets.chat.list.entries.MessageEntry
// Aliucord Plugin annotation. Must be present on the main class of your plugin
-@AliucordPlugin(requiresRestart = false /* Whether your plugin requires a restart after being installed/updated */)
// Plugin class. Must extend Plugin and override start and stop
// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
+@AliucordPlugin(
+ requiresRestart = false // Whether your plugin requires a restart after being installed/updated
+)
class MyFirstPatch : Plugin() {
override fun start(context: Context) {
// Patch that adds an embed with message statistics to each message
@@ -26,7 +28,8 @@ class MyFirstPatch : Plugin() {
// and https://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
Int::class.java, // int type
ChatListEntry::class.java // ChatListEntry entry
- ) { param -> // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
+ ) { param ->
+ // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
// Obtain the second argument passed to the method, so the ChatListEntry
// Because this is a Message item, it will always be a MessageEntry, so cast it to that
val entry = param.args[1] as MessageEntry
@@ -56,7 +59,8 @@ class MyFirstPatch : Plugin() {
}
// Patch that renames Juby to JoobJoob
- patcher.before("getUsername") { param -> // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
+ patcher.before("getUsername") { param ->
+ // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
// in before, after and instead patches, `this` refers to the instance of the class
// the patched method is on, so the CoreUser instance here
if (id == 925141667688878090) {
@@ -67,7 +71,8 @@ class MyFirstPatch : Plugin() {
// Patch that hides your typing status by replacing the method and simply doing nothing
patcher.instead(
- "setUserTyping", Long::class.java // long channelId
+ "setUserTyping",
+ Long::class.java // long channelId
) { null }
}
@@ -75,4 +80,4 @@ class MyFirstPatch : Plugin() {
// Remove all patches
patcher.unpatchAll()
}
-}
+}
\ No newline at end of file
diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts
new file mode 100644
index 0000000..2a56ce2
--- /dev/null
+++ b/plugin/build.gradle.kts
@@ -0,0 +1,76 @@
+@file:Suppress("UnstableApiUsage")
+
+import com.aliucord.gradle.AliucordExtension
+import com.android.build.gradle.LibraryExtension
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jlleitschuh.gradle.ktlint.KtlintExtension
+
+// TODO: Change to your repository
+val repo = "Aliucord/plugins-template"
+
+subprojects {
+ val libs = rootProject.libs
+
+ apply {
+ plugin(libs.plugins.android.library.get().pluginId)
+ plugin(libs.plugins.aliucord.get().pluginId)
+ plugin(libs.plugins.kotlin.android.get().pluginId)
+ plugin(libs.plugins.ktlint.get().pluginId)
+ }
+
+ configure {
+ namespace = "com.github.yournamehere"
+
+ compileSdk = 34
+
+ defaultConfig {
+ minSdk = 24
+ }
+
+ buildFeatures {
+ renderScript = false
+ shaders = false
+ buildConfig = true
+ resValues = false
+ aidl = false
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ }
+
+ configure {
+ // TODO: Change to your name and user ID
+ author("yournamehere", 0L)
+
+ updateUrl.set("https://raw.githubusercontent.com/$repo/builds/updater.json")
+ buildUrl.set("https://raw.githubusercontent.com/$repo/builds/%s.zip")
+ }
+
+ configure {
+ version.set(libs.versions.ktlint)
+
+ coloredOutput.set(true)
+ outputColorName.set("RED")
+ ignoreFailures.set(true)
+ }
+
+ dependencies {
+ val discord by configurations
+ val compileOnly by configurations
+ val implementation by configurations
+
+ discord(libs.discord)
+ compileOnly(libs.aliucord)
+ // compileOnly("com.github.Aliucord:Aliucord:unspecified")
+ }
+
+ tasks.withType {
+ kotlinOptions {
+ jvmTarget = "11"
+ freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
+ }
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 54b32ab..5353a27 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,22 +1,29 @@
-rootProject.name = "AliucordPlugins"
+@file:Suppress("UnstableApiUsage")
-// This file sets what projects are included. Every time you add a new project, you must add it
-// to the includes below.
+pluginManagement {
+ repositories {
+ google()
+ gradlePluginPortal()
+ maven("https://maven.aliucord.com/snapshots")
+ maven("https://jitpack.io")
+ }
+}
-// Plugins are included like this
-include(
- "MyFirstCommand",
- "MyFirstPatch"
-)
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://maven.aliucord.com/snapshots")
+ }
-// This is required because plugins are in the ExamplePlugins/kotlin subdirectory.
-//
-// Assuming you put all your plugins into the project root, so on the same
-// level as this file, simply remove everything below.
-//
-// Otherwise, if you want a different structure, for example all plugins in a folder called "plugins",
-// then simply change the path
-rootProject.children.forEach {
- // Change kotlin to java if you'd rather use java
- it.projectDir = file("ExamplePlugins/kotlin/${it.name}")
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
+
+rootProject.name = "aliucord-plugins"
+
+rootDir
+ .resolve("plugin")
+ .listFiles { file ->
+ file.isDirectory && file.resolve("build.gradle.kts").exists()
+ }!!
+ .forEach { include(":plugin:${it.name}") }
\ No newline at end of file
From 8a2ec4191467e15bb9e703ba47e3a6681d6910cf Mon Sep 17 00:00:00 2001
From: Nick <31907977+zt64@users.noreply.github.com>
Date: Thu, 4 Jul 2024 18:57:01 -0400
Subject: [PATCH 2/5] Update
plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
Co-authored-by: rushii <33725716+rushiiMachine@users.noreply.github.com>
---
.../src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt b/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
index 42faef7..00a0884 100644
--- a/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
+++ b/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
@@ -72,7 +72,7 @@ class MyFirstPatch : Plugin() {
// Patch that hides your typing status by replacing the method and simply doing nothing
patcher.instead(
"setUserTyping",
- Long::class.java // long channelId
+ Long::class.java // java.lang.Long channelId
) { null }
}
From 25ee7ba440ce332106671b42e39db1261ef9d4b2 Mon Sep 17 00:00:00 2001
From: zt64 <31907977+zt64@users.noreply.github.com>
Date: Fri, 5 Jul 2024 23:40:13 -0400
Subject: [PATCH 3/5] Update gradle.properties
---
gradle.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle.properties b/gradle.properties
index 5a653f2..8001bb3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options=--illegal-access=permit
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
From f3c86920f5674cfb700cae3fd72db15f6a7f70dd Mon Sep 17 00:00:00 2001
From: zt64 <31907977+zt64@users.noreply.github.com>
Date: Sat, 6 Jul 2024 00:23:36 -0400
Subject: [PATCH 4/5] Combine examples into singular plugin, with Java and
Kotlin
---
.../com/github/yournamehere/MyFirstCommand.kt | 62 ----------
.../build.gradle.kts | 2 +-
.../yournamehere/MyFirstJavaPlugin.java | 117 ++++++++++++++++++
.../build.gradle.kts | 2 +-
.../yournamehere/MyFirstKotlinPlugin.kt} | 58 +++++++--
plugin/build.gradle.kts | 1 +
6 files changed, 171 insertions(+), 71 deletions(-)
delete mode 100644 plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
rename plugin/{MyFirstCommand => MyFirstJavaPlugin}/build.gradle.kts (87%)
create mode 100644 plugin/MyFirstJavaPlugin/src/main/java/com/github/yournamehere/MyFirstJavaPlugin.java
rename plugin/{MyFirstPatch => MyFirstKotlinPlugin}/build.gradle.kts (86%)
rename plugin/{MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt => MyFirstKotlinPlugin/src/main/kotlin/com/github/yournamehere/MyFirstKotlinPlugin.kt} (62%)
diff --git a/plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt b/plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
deleted file mode 100644
index f794da0..0000000
--- a/plugin/MyFirstCommand/src/main/kotlin/com/github/yournamehere/MyFirstCommand.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.github.yournamehere
-
-import android.content.Context
-import com.aliucord.Utils
-import com.aliucord.annotations.AliucordPlugin
-import com.aliucord.api.CommandsAPI
-import com.aliucord.entities.Plugin
-import com.discord.api.commands.ApplicationCommandType
-
-// Aliucord Plugin annotation. Must be present on the main class of your plugin
-// Plugin class. Must extend Plugin and override start and stop
-// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
-@AliucordPlugin(
- requiresRestart = false // Whether your plugin requires a restart after being installed/updated
-)
-class MyFirstCommand : Plugin() {
- override fun start(context: Context) {
- // Register a command with the name hello and description "My first command!" and no arguments.
- // Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/2_commands.md
- commands.registerCommand("hello", "My first command!") {
- // Just return a command result with hello world as the content
- CommandsAPI.CommandResult(
- "Hello World!",
- null, // List of embeds
- false // Whether to send visible for everyone
- )
- }
-
- // A bit more advanced command with arguments
- commands.registerCommand(
- "hellowitharguments",
- "Hello World but with arguments!",
- listOf(
- Utils.createCommandOption(
- ApplicationCommandType.STRING,
- "name",
- "Person to say hello to"
- ),
- Utils.createCommandOption(
- ApplicationCommandType.USER,
- "user",
- "User to say hello to"
- )
- )
- ) { ctx ->
- // Check if a user argument was passed
- if (ctx.containsArg("user")) {
- val user = ctx.getRequiredUser("user")
- CommandsAPI.CommandResult("Hello ${user.username}!")
- } else {
- // Returns either the argument value if present, or the defaultValue ("World" in this case)
- val name = ctx.getStringOrDefault("name", "World")
- CommandsAPI.CommandResult("Hello $name!")
- }
- }
- }
-
- override fun stop(context: Context) {
- // Unregister our commands
- commands.unregisterAll()
- }
-}
\ No newline at end of file
diff --git a/plugin/MyFirstCommand/build.gradle.kts b/plugin/MyFirstJavaPlugin/build.gradle.kts
similarity index 87%
rename from plugin/MyFirstCommand/build.gradle.kts
rename to plugin/MyFirstJavaPlugin/build.gradle.kts
index f952125..27dd107 100644
--- a/plugin/MyFirstCommand/build.gradle.kts
+++ b/plugin/MyFirstJavaPlugin/build.gradle.kts
@@ -1,5 +1,5 @@
version = "1.0.0" // Plugin version. Increment this to trigger the updater
-description = "My first commands!" // Plugin description that will be shown to user
+description = "My first Java plugin!" // Plugin description that will be shown to user
aliucord {
// Changelog of your plugin
diff --git a/plugin/MyFirstJavaPlugin/src/main/java/com/github/yournamehere/MyFirstJavaPlugin.java b/plugin/MyFirstJavaPlugin/src/main/java/com/github/yournamehere/MyFirstJavaPlugin.java
new file mode 100644
index 0000000..525d2f2
--- /dev/null
+++ b/plugin/MyFirstJavaPlugin/src/main/java/com/github/yournamehere/MyFirstJavaPlugin.java
@@ -0,0 +1,117 @@
+package com.github.yournamehere;
+
+import android.content.Context;
+
+import com.aliucord.Utils;
+import com.aliucord.annotations.AliucordPlugin;
+import com.aliucord.api.CommandsAPI;
+import com.aliucord.entities.MessageEmbedBuilder;
+import com.aliucord.entities.Plugin;
+import com.aliucord.patcher.Hook;
+import com.aliucord.patcher.InsteadHook;
+import com.aliucord.patcher.PreHook;
+import com.aliucord.wrappers.embeds.MessageEmbedWrapper;
+import com.discord.api.commands.ApplicationCommandType;
+import com.discord.models.user.CoreUser;
+import com.discord.stores.StoreUserTyping;
+import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemMessage;
+import com.discord.widgets.chat.list.entries.ChatListEntry;
+import com.discord.widgets.chat.list.entries.MessageEntry;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+// Aliucord Plugin annotation. Must be present on the main class of your plugin
+// Plugin class. Must extend Plugin and override start and stop
+// Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/1_introduction.md#basic-plugin-structure
+@AliucordPlugin(
+ requiresRestart = false // Whether your plugin requires a restart after being installed/updated
+)
+class MyFirstJavaPlugin extends Plugin {
+ @Override
+ public void start(Context context) throws Throwable {
+ // Register a command with the name hello and description "My first command!" and no arguments.
+ // Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/2_commands.md
+ commands.registerCommand("hello", "My first command!", ctx -> {
+ // Just return a command result with hello world as the content
+ return new CommandsAPI.CommandResult(
+ "Hello World!",
+ null, // List of embeds
+ false // Whether to send visible for everyone
+ );
+ });
+
+ // A bit more advanced command with arguments
+ commands.registerCommand(
+ "hellowitharguments",
+ "Hello World but with arguments!",
+ Arrays.asList(
+ Utils.createCommandOption(ApplicationCommandType.STRING, "name", "Person to say hello to"),
+ Utils.createCommandOption(ApplicationCommandType.USER, "user", "User to say hello to")
+ ),
+ ctx -> {
+ String username;
+
+ // Check if a user argument was passed
+ if (ctx.containsArg("user")) {
+ username = ctx.getRequiredUser("user").getUsername();
+ } else {
+ // Returns either the argument value if present, or the defaultValue ("World" in this case)
+ username = ctx.getStringOrDefault("name", "World");
+ }
+
+ // Return the final result that will be displayed in chat as a response to the command
+ return new CommandsAPI.CommandResult("Hello " + username + "!");
+ }
+ );
+
+ // Patch that adds an embed with message statistics to each message
+ // Patched method is WidgetChatListAdapterItemMessage.onConfigure(int type, ChatListEntry entry)
+ patcher.patch(WidgetChatListAdapterItemMessage.class.getDeclaredMethod("onConfigure", int.class, ChatListEntry.class), new Hook(param -> {
+ // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
+ // Obtain the second argument passed to the method, so the ChatListEntry
+ // Because this is a Message item, it will always be a MessageEntry, so cast it to that
+ var entry = (MessageEntry) param.args[1];
+ var message = entry.getMessage();
+
+ // You need to be careful when messing with messages, because they may be loading
+ // (user sent a message, and it is currently sending)
+ if (message.isLoading()) return;
+
+ // Now add an embed with the statistics
+
+ // This method may be called multiple times per message, e.g. if it is edited,
+ // so first remove existing embeds
+ message.getEmbeds().removeIf(it -> Objects.equals(new MessageEmbedWrapper(it).getTitle(), "Message Statistics"));
+
+ // Creating embeds is a pain, so Aliucord provides a convenient builder
+ var embed = new MessageEmbedBuilder()
+ .setTitle("Message Statistics")
+ .addField("Length", message.getContent() != null ? Integer.toString(message.getContent().length()) : "0", false)
+ .addField("ID", Long.toString(message.getId()), false).build();
+
+ message.getEmbeds().add(embed);
+ }));
+
+ // Patch that renames Juby to JoobJoob
+ patcher.patch(
+ CoreUser.class.getDeclaredMethod("getUsername"),
+ new PreHook(param -> { // see https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.MethodHookParam.html
+ if (((CoreUser) param.thisObject).getId() == 925141667688878090L) {
+ // setResult() in before patches skips original method invocation
+ param.setResult("JoobJoob");
+ }
+ })
+ );
+
+ // Patch that hides your typing status by replacing the method and simply doing nothing
+ // This patches the method StoreUserTyping.setUserTyping(long channelId)
+ patcher.patch(StoreUserTyping.class.getDeclaredMethod("setUserTyping", long.class), InsteadHook.DO_NOTHING);
+ }
+
+ @Override
+ public void stop(Context context) {
+ // Remove all patches
+ patcher.unpatchAll();
+ }
+}
\ No newline at end of file
diff --git a/plugin/MyFirstPatch/build.gradle.kts b/plugin/MyFirstKotlinPlugin/build.gradle.kts
similarity index 86%
rename from plugin/MyFirstPatch/build.gradle.kts
rename to plugin/MyFirstKotlinPlugin/build.gradle.kts
index e83932f..00cc5ef 100644
--- a/plugin/MyFirstPatch/build.gradle.kts
+++ b/plugin/MyFirstKotlinPlugin/build.gradle.kts
@@ -1,5 +1,5 @@
version = "1.0.0" // Plugin version. Increment this to trigger the updater
-description = "My first patch!" // Plugin description that will be shown to user
+description = "My first Kotlin plugin!" // Plugin description that will be shown to user
aliucord {
// Changelog of your plugin
diff --git a/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt b/plugin/MyFirstKotlinPlugin/src/main/kotlin/com/github/yournamehere/MyFirstKotlinPlugin.kt
similarity index 62%
rename from plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
rename to plugin/MyFirstKotlinPlugin/src/main/kotlin/com/github/yournamehere/MyFirstKotlinPlugin.kt
index 00a0884..e2305d3 100644
--- a/plugin/MyFirstPatch/src/main/kotlin/com/github/yournamehere/MyFirstPatch.kt
+++ b/plugin/MyFirstKotlinPlugin/src/main/kotlin/com/github/yournamehere/MyFirstKotlinPlugin.kt
@@ -1,11 +1,14 @@
package com.github.yournamehere
import android.content.Context
+import com.aliucord.Utils
import com.aliucord.annotations.AliucordPlugin
+import com.aliucord.api.CommandsAPI
import com.aliucord.entities.MessageEmbedBuilder
import com.aliucord.entities.Plugin
import com.aliucord.patcher.*
import com.aliucord.wrappers.embeds.MessageEmbedWrapper.Companion.title
+import com.discord.api.commands.ApplicationCommandType
import com.discord.models.user.CoreUser
import com.discord.stores.StoreUserTyping
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemMessage
@@ -18,11 +21,51 @@ import com.discord.widgets.chat.list.entries.MessageEntry
@AliucordPlugin(
requiresRestart = false // Whether your plugin requires a restart after being installed/updated
)
-class MyFirstPatch : Plugin() {
+class MyFirstKotlinPlugin : Plugin() {
override fun start(context: Context) {
+ // Register a command with the name hello and description "My first command!" and no arguments.
+ // Learn more: https://github.com/Aliucord/documentation/blob/main/plugin-dev/2_commands.md
+ commands.registerCommand("hello", "My first command!") {
+ // Just return a command result with hello world as the content
+ CommandsAPI.CommandResult(
+ "Hello World!",
+ null, // List of embeds
+ false // Whether to send visible for everyone
+ )
+ }
+
+ // A bit more advanced command with arguments
+ commands.registerCommand(
+ "hellowitharguments",
+ "Hello World but with arguments!",
+ listOf(
+ Utils.createCommandOption(
+ ApplicationCommandType.STRING,
+ "name",
+ "Person to say hello to"
+ ),
+ Utils.createCommandOption(
+ ApplicationCommandType.USER,
+ "user",
+ "User to say hello to"
+ )
+ )
+ ) { ctx ->
+ // Check if a user argument was passed
+ val username = if (ctx.containsArg("user")) {
+ ctx.getRequiredUser("user").username
+ } else {
+ // Returns either the argument value if present, or the defaultValue ("World" in this case)
+ ctx.getStringOrDefault("name", "World")
+ }
+
+ // Return the final result that will be displayed in chat as a response to the command
+ CommandsAPI.CommandResult("Hello $username!")
+ }
+
// Patch that adds an embed with message statistics to each message
// Patched method is WidgetChatListAdapterItemMessage.onConfigure(int type, ChatListEntry entry)
- patcher.after /* Class whose method to patch */(
+ patcher.after(
"onConfigure", // Method name
// Refer to https://kotlinlang.org/docs/reflection.html#class-references
// and https://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
@@ -33,16 +76,17 @@ class MyFirstPatch : Plugin() {
// Obtain the second argument passed to the method, so the ChatListEntry
// Because this is a Message item, it will always be a MessageEntry, so cast it to that
val entry = param.args[1] as MessageEntry
+ val message = entry.message
// You need to be careful when messing with messages, because they may be loading
// (user sent a message, and it is currently sending)
- if (entry.message.isLoading) return@after
+ if (message.isLoading) return@after
// Now add an embed with the statistics
// This method may be called multiple times per message, e.g. if it is edited,
// so first remove existing embeds
- entry.message.embeds.removeIf {
+ message.embeds.removeIf {
// MessageEmbed.getTitle() is actually obfuscated, but Aliucord provides extensions for commonly used
// obfuscated Discord classes, so just import the MessageEmbed.title extension and boom goodbye obfuscation!
it.title == "Message Statistics"
@@ -51,10 +95,10 @@ class MyFirstPatch : Plugin() {
// Creating embeds is a pain, so Aliucord provides a convenient builder
MessageEmbedBuilder().run {
setTitle("Message Statistics")
- addField("Length", (entry.message.content?.length ?: 0).toString(), false)
- addField("ID", entry.message.id.toString(), false)
+ addField("Length", "${message.content?.length ?: 0}", false)
+ addField("ID", message.id.toString(), false)
- entry.message.embeds.add(build())
+ message.embeds.add(build())
}
}
diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts
index 2a56ce2..57a03a2 100644
--- a/plugin/build.gradle.kts
+++ b/plugin/build.gradle.kts
@@ -19,6 +19,7 @@ subprojects {
}
configure {
+ // TODO: Change to your package name
namespace = "com.github.yournamehere"
compileSdk = 34
From 0aba41dbfb1b72f9958d7ee7010c6ec502c6b9a5 Mon Sep 17 00:00:00 2001
From: zt64 <31907977+zt64@users.noreply.github.com>
Date: Sat, 6 Jul 2024 00:25:33 -0400
Subject: [PATCH 5/5] Update README.md
---
README.md | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 0477804..5e46207 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,36 @@
-# `Aliucord Plugin Repo Template`
+# Aliucord Plugin Repo Template
+
+---
Template for an [Aliucord](https://github.com/Aliucord) plugin repo
-⚠️ Make sure you check "Include all branches" when using this template
+⚠️ Make sure you check "Include all branches" when using this template \
+⚠️ Consider getting familiar with Java and/or Kotlin and Gradle before starting
+
+## Pre-requisites
+
+- Java JDK 11 or newer. OpenJDK recommended
+- [Android Studio](https://developer.android.com/studio)
## Getting started with writing your first plugin
-This template includes 2 example plugins demonstrating commands and patches which you can find in
-the ExamplePlugins folder.
+This template includes an example plugin written in Kotlin and Java, demonstrating how to implement
+a command and patches.
+
+To set up your development environment:
+
+1. Clone this repository to your local machine.
+2. Open the cloned repository in Android Studio.
+3. Open the gradle build script at [plugin/build.gradle.kts](plugin/build.gradle.kts), read the
+ comments and replace all the placeholders
+4. Familiarize yourself with the project structure. Most files are commented
+
+To build and deploy your plugin:
-1. Open the root build.gradle.kts, read the comments and replace all the placeholders
-2. Familiarize yourself with the project structure. Most files are commented
-3. Build or deploy your first plugin using:
- - Windows: `.\gradlew.bat MyFirstCommand:make` or `.\gradlew.bat MyFirstCommand:deployWithAdb`
- - Linux & Mac: `./gradlew MyFirstCommand:make` or `./gradlew MyFirstCommand:deployWithAdb`
+- On Linux & Mac, run `./gradlew MyFirstKotlinPlugin:make` to build the plugin.
+ Use `./gradlew MyFirstKotlinPlugin:deployWithAdb` to deploy directly to a connected device.
+- On Windows, use `.\gradlew.bat MyFirstKotlinPlugin:make`
+ and `.\gradlew.bat MyFirstKotlinPlugin:deployWithAdb` for building and deploying, respectively.
## License