feat(canary/SlashCommandsFix): init @ jedenastka/Aliucord@47a1c8e43f
This commit is contained in:
parent
50ba1acc23
commit
a087010fbd
21 changed files with 955 additions and 1 deletions
42
canary/SlashCommandsFix/build.gradle.kts
Normal file
42
canary/SlashCommandsFix/build.gradle.kts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
version = "7.16.0"
|
||||||
|
description = "Beta backport of SlashCommandsFix"
|
||||||
|
|
||||||
|
aliucord {
|
||||||
|
changelog.set("""
|
||||||
|
# 7.16.0
|
||||||
|
* Initial port >w< thanks @jedenastka
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
excludeFromUpdaterJson.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply(plugin = "com.gradleup.shadow")
|
||||||
|
apply(plugin = "com.github.johnrengelman.shadow") // remove when gradle 8
|
||||||
|
|
||||||
|
val shadowDir = File(buildDir, "intermediates/shadowed")
|
||||||
|
|
||||||
|
tasks.register<ShadowJar>("relocateJar") {
|
||||||
|
val javaTask = tasks.findByName("compileDebugJavaWithJavac")!!
|
||||||
|
val kotlinTask = tasks.findByName("compileDebugKotlin")!!
|
||||||
|
from(javaTask.outputs, kotlinTask.outputs)
|
||||||
|
relocate("com.aliucord.coreplugins.slashcommandsfix", "moe.lava.corenary.slashcommandsfix")
|
||||||
|
archiveClassifier.set("shadowed")
|
||||||
|
destinationDirectory.set(File(buildDir, "intermediates"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Sync>("copyShadowed") {
|
||||||
|
val reloc = tasks.findByName("relocateJar")!! as ShadowJar
|
||||||
|
dependsOn(reloc)
|
||||||
|
from(zipTree(reloc.archiveFile))
|
||||||
|
into(shadowDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
project.afterEvaluate {
|
||||||
|
tasks.compileDex {
|
||||||
|
val copyShadowed = tasks.findByName("copyShadowed")!! as Sync
|
||||||
|
dependsOn(copyShadowed)
|
||||||
|
input.setFrom(shadowDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
2
canary/SlashCommandsFix/src/main/AndroidManifest.xml
Normal file
2
canary/SlashCommandsFix/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="moe.lava.corenary.slashcommandsfix" />
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import com.discord.models.user.User;
|
||||||
|
import com.discord.stores.StoreStream;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApiApplication {
|
||||||
|
public final long id;
|
||||||
|
public final String name;
|
||||||
|
public final String icon;
|
||||||
|
public final ApiPermissions permissions;
|
||||||
|
public final Long botId;
|
||||||
|
|
||||||
|
public ApiApplication() {
|
||||||
|
this.id = 0;
|
||||||
|
this.name = null;
|
||||||
|
this.icon = null;
|
||||||
|
this.permissions = null;
|
||||||
|
this.botId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Application toModel() {
|
||||||
|
Permissions permissions = null;
|
||||||
|
if (this.permissions != null) {
|
||||||
|
permissions = this.permissions.toModel(Optional.empty());
|
||||||
|
} else {
|
||||||
|
permissions = new Permissions(null, null, null, null);
|
||||||
|
}
|
||||||
|
var usersStore = StoreStream.getUsers();
|
||||||
|
Optional<User> botUser = Optional.ofNullable(this.botId).map(userId -> usersStore.getUsers().get(userId));
|
||||||
|
return new Application(this.id, this.name, this.icon, permissions, botUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import com.discord.models.commands.ApplicationCommand;
|
||||||
|
import com.discord.stores.StoreApplicationCommandsKt;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class ApiApplicationCommand {
|
||||||
|
public final long id;
|
||||||
|
public final long applicationId;
|
||||||
|
public final String name;
|
||||||
|
public final String description;
|
||||||
|
public final List<com.discord.api.commands.ApplicationCommandOption> options;
|
||||||
|
public final ApiPermissions permissions;
|
||||||
|
public final Long defaultMemberPermissions;
|
||||||
|
public final Long guildId;
|
||||||
|
public final String version;
|
||||||
|
public final int type;
|
||||||
|
|
||||||
|
public ApiApplicationCommand() {
|
||||||
|
this.id = 0;
|
||||||
|
this.applicationId = 0;
|
||||||
|
this.name = null;
|
||||||
|
this.description = null;
|
||||||
|
this.options = null;
|
||||||
|
this.permissions = null;
|
||||||
|
this.defaultMemberPermissions = null;
|
||||||
|
this.guildId = null;
|
||||||
|
this.version = null;
|
||||||
|
this.type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteApplicationCommand toModel() {
|
||||||
|
var apiOptions = this.options;
|
||||||
|
if (apiOptions == null) {
|
||||||
|
apiOptions = new ArrayList<>();
|
||||||
|
}
|
||||||
|
var options = apiOptions
|
||||||
|
.stream()
|
||||||
|
.map(option -> StoreApplicationCommandsKt.toSlashCommandOption(option))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Permissions permissions = null;
|
||||||
|
var defaultMemberPermissions = Optional.ofNullable(this.defaultMemberPermissions);
|
||||||
|
if (this.permissions != null) {
|
||||||
|
permissions = this.permissions.toModel(defaultMemberPermissions);
|
||||||
|
} else {
|
||||||
|
permissions = new Permissions(null, null, null, defaultMemberPermissions);
|
||||||
|
}
|
||||||
|
return new RemoteApplicationCommand(String.valueOf(this.id), this.applicationId, this.name, this.description, options, permissions, this.guildId, this.version, this.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class ApiApplicationIndex {
|
||||||
|
public List<ApiApplication> applications;
|
||||||
|
public List<ApiApplicationCommand> applicationCommands;
|
||||||
|
|
||||||
|
public ApiApplicationIndex() {
|
||||||
|
this.applications = null;
|
||||||
|
this.applicationCommands = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationIndex toModel() {
|
||||||
|
var applications = new HashMap<Long, Application>();
|
||||||
|
for (var application: this.applications) {
|
||||||
|
applications.put(application.id, application.toModel());
|
||||||
|
}
|
||||||
|
var applicationCommands = new HashMap<Long, RemoteApplicationCommand>();
|
||||||
|
for (var applicationCommand: this.applicationCommands) {
|
||||||
|
applicationCommands.put(applicationCommand.id, applicationCommand.toModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ApplicationIndex(applications, applicationCommands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
class ApiGuildApplicationCommandIndexUpdate {
|
||||||
|
public long guildId;
|
||||||
|
|
||||||
|
public ApiGuildApplicationCommandIndexUpdate() {
|
||||||
|
this.guildId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApiPermissions {
|
||||||
|
public Boolean user;
|
||||||
|
public Map<Long, Boolean> roles;
|
||||||
|
public Map<Long, Boolean> channels;
|
||||||
|
|
||||||
|
public ApiPermissions() {
|
||||||
|
this.user = null;
|
||||||
|
this.roles = null;
|
||||||
|
this.channels = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Permissions toModel(Optional<Long> defaultMemberPermissions) {
|
||||||
|
return new Permissions(Optional.ofNullable(user), roles, channels, defaultMemberPermissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import com.aliucord.Logger;
|
||||||
|
import com.discord.models.user.User;
|
||||||
|
import com.discord.utilities.user.UserUtils;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class Application extends com.discord.models.commands.Application {
|
||||||
|
public Permissions permissions_;
|
||||||
|
|
||||||
|
public Application(long id, String name, String icon, Permissions permissions, Optional<User> botUser) {
|
||||||
|
super(id, name, icon, null, -1, botUser.map(user -> UserUtils.INSTANCE.synthesizeApiUser(user)).orElse(null), false);
|
||||||
|
this.permissions_ = permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.lang.IllegalAccessException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class ApplicationIndex {
|
||||||
|
public Map<Long, Application> applications;
|
||||||
|
public Map<Long, RemoteApplicationCommand> applicationCommands;
|
||||||
|
|
||||||
|
public ApplicationIndex(Map<Long, Application> applications, Map<Long, RemoteApplicationCommand> applicationCommands) {
|
||||||
|
this.applications = applications;
|
||||||
|
this.applicationCommands = applicationCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationIndex(List<ApplicationIndex> applicationIndexes) {
|
||||||
|
this.applications = new HashMap();
|
||||||
|
this.applicationCommands = new HashMap();
|
||||||
|
for (var applicationIndex: applicationIndexes) {
|
||||||
|
this.applications.putAll(applicationIndex.applications);
|
||||||
|
this.applicationCommands.putAll(applicationIndex.applicationCommands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void populateCommandCounts(Field applicationCommandCountField) throws IllegalAccessException {
|
||||||
|
var applicationCommandCounts = new HashMap<Long, Integer>();
|
||||||
|
for (var applicationCommand: this.applicationCommands.values()) {
|
||||||
|
var count = applicationCommandCounts.getOrDefault(applicationCommand.getApplicationId(), 0);
|
||||||
|
count += 1;
|
||||||
|
applicationCommandCounts.put(applicationCommand.getApplicationId(), count);
|
||||||
|
}
|
||||||
|
for (var application: this.applications.values()) {
|
||||||
|
applicationCommandCountField.setInt(application, applicationCommandCounts.getOrDefault(application.getId(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApplicationIndexCache {
|
||||||
|
public Map<Long, ApplicationIndex> guild;
|
||||||
|
public Map<Long, ApplicationIndex> dm;
|
||||||
|
public Optional<ApplicationIndex> user;
|
||||||
|
|
||||||
|
public ApplicationIndexCache() {
|
||||||
|
this.guild = new HashMap<>();
|
||||||
|
this.dm = new HashMap<>();
|
||||||
|
this.user = Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
interface ApplicationIndexSource {
|
||||||
|
String getEndpoint();
|
||||||
|
Optional<ApplicationIndex> getFromCache(ApplicationIndexCache cache);
|
||||||
|
void insertIntoCache(ApplicationIndexCache cache, ApplicationIndex index);
|
||||||
|
void removeFromCache(ApplicationIndexCache cache);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApplicationIndexSourceDm implements ApplicationIndexSource {
|
||||||
|
long channelId;
|
||||||
|
|
||||||
|
public ApplicationIndexSourceDm(long channelId) {
|
||||||
|
this.channelId = channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEndpoint() {
|
||||||
|
return String.format("/channels/%d/application-command-index", this.channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ApplicationIndex> getFromCache(ApplicationIndexCache cache) {
|
||||||
|
return Optional.ofNullable(
|
||||||
|
cache.dm.get(this.channelId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertIntoCache(ApplicationIndexCache cache, ApplicationIndex index) {
|
||||||
|
cache.dm.put(this.channelId, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeFromCache(ApplicationIndexCache cache) {
|
||||||
|
cache.dm.remove(this.channelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApplicationIndexSourceGuild implements ApplicationIndexSource {
|
||||||
|
long guildId;
|
||||||
|
|
||||||
|
public ApplicationIndexSourceGuild(long guildId) {
|
||||||
|
this.guildId = guildId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEndpoint() {
|
||||||
|
return String.format("/guilds/%d/application-command-index", this.guildId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ApplicationIndex> getFromCache(ApplicationIndexCache cache) {
|
||||||
|
return Optional.ofNullable(
|
||||||
|
cache.guild.get(this.guildId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertIntoCache(ApplicationIndexCache cache, ApplicationIndex index) {
|
||||||
|
cache.guild.put(this.guildId, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeFromCache(ApplicationIndexCache cache) {
|
||||||
|
cache.guild.remove(this.guildId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class ApplicationIndexSourceUser implements ApplicationIndexSource {
|
||||||
|
public ApplicationIndexSourceUser() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEndpoint() {
|
||||||
|
return "/users/@me/application-command-index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ApplicationIndex> getFromCache(ApplicationIndexCache cache) {
|
||||||
|
return cache.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertIntoCache(ApplicationIndexCache cache, ApplicationIndex index) {
|
||||||
|
cache.user = Optional.of(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeFromCache(ApplicationIndexCache cache) {
|
||||||
|
cache.user = Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.aliucord.*
|
||||||
|
import com.aliucord.fragments.ConfirmDialog
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
object ConflictCheck {
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
@JvmStatic
|
||||||
|
fun run(context: Context): Boolean {
|
||||||
|
val hasFix = PluginManager.plugins.containsKey("SlashCommandsFix")
|
||||||
|
val hasForcedFix = PluginManager.plugins.containsKey("ForceSlashCommandsFixNOW")
|
||||||
|
val fromStorage = Main.settings.getBool("AC_from_storage", false)
|
||||||
|
|
||||||
|
if (hasFix) {
|
||||||
|
Logger("SlashCommandsFixBeta").warn("conflict detected")
|
||||||
|
if (hasForcedFix || fromStorage) {
|
||||||
|
Utils.threadPool.execute {
|
||||||
|
Thread.sleep(5000) // wait for app to load guh
|
||||||
|
Utils.mainThread.post {
|
||||||
|
val dialog = ConfirmDialog()
|
||||||
|
dialog
|
||||||
|
.setTitle("SlashCommandsFix Conflict")
|
||||||
|
.setDescription("You have another variant of SlashCommandsFix installed. Do you want to disable it?")
|
||||||
|
.setIsDangerous(true)
|
||||||
|
.setOnOkListener {
|
||||||
|
File(context.codeCacheDir, "Aliucord.zip").delete()
|
||||||
|
if (fromStorage)
|
||||||
|
Main.settings.setBool("AC_from_storage", false)
|
||||||
|
if (hasForcedFix)
|
||||||
|
PluginManager.disablePlugin("ForceSlashCommandsFixNOW")
|
||||||
|
val ctx = it.context
|
||||||
|
val intent = ctx.packageManager.getLaunchIntentForPackage(ctx.packageName)
|
||||||
|
Utils.appActivity.startActivity(Intent.makeRestartActivityTask(intent!!.component))
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
.apply { isCancelable = false }
|
||||||
|
.show(Utils.appActivity.supportFragmentManager, "SlashCommandsFix conflict")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger("SlashCommandsFixBeta").warn("removing myself... bye!")
|
||||||
|
File("${Constants.PLUGINS_PATH}/SlashCommandsFixBeta.zip").delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasFix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.aliucord.api.GatewayAPI;
|
||||||
|
import com.aliucord.Http;
|
||||||
|
import com.aliucord.Logger;
|
||||||
|
import com.aliucord.patcher.InsteadHook;
|
||||||
|
import com.aliucord.patcher.Patcher;
|
||||||
|
import com.aliucord.patcher.PreHook;
|
||||||
|
import com.aliucord.Utils;
|
||||||
|
import com.aliucord.utils.GsonUtils;
|
||||||
|
import com.discord.api.channel.Channel;
|
||||||
|
import com.discord.models.commands.Application;
|
||||||
|
import com.discord.models.commands.ApplicationCommand;
|
||||||
|
import com.discord.models.commands.ApplicationCommandKt;
|
||||||
|
import com.discord.models.commands.ApplicationCommandLocalSendData;
|
||||||
|
import com.discord.stores.BuiltInCommandsProvider;
|
||||||
|
import com.discord.stores.StoreApplicationCommands;
|
||||||
|
import com.discord.stores.StoreApplicationCommands$requestApplicationCommands$1;
|
||||||
|
import com.discord.stores.StoreApplicationCommands$requestApplicationCommandsQuery$1;
|
||||||
|
import com.discord.stores.StoreApplicationInteractions;
|
||||||
|
import com.discord.stores.StoreChannelsSelected;
|
||||||
|
import com.discord.stores.StoreStream;
|
||||||
|
import com.discord.utilities.error.Error;
|
||||||
|
import com.discord.utilities.messagesend.MessageResult;
|
||||||
|
import com.discord.utilities.permissions.PermissionUtils;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import kotlin.jvm.functions.Function0;
|
||||||
|
import kotlin.jvm.functions.Function1;
|
||||||
|
|
||||||
|
final class Patches {
|
||||||
|
private ApplicationIndexCache applicationIndexCache;
|
||||||
|
private Logger logger;
|
||||||
|
private Method handleGuildApplicationsUpdateMethod;
|
||||||
|
private Method handleDiscoverCommandsUpdateMethod;
|
||||||
|
private Method handleQueryCommandsUpdateMethod;
|
||||||
|
private Field applicationCommandCountField;
|
||||||
|
private Field storeApplicationCommandsQueryField;
|
||||||
|
private Field errorResponseErrorField;
|
||||||
|
private Field skemaErrorSubErrorsField;
|
||||||
|
private Field skemaErrorErrorsField;
|
||||||
|
private Field skemaErrorItemCodeField;
|
||||||
|
private Field skemaErrorItemMessageField;
|
||||||
|
private Field storeApplicationCommandsBuiltInCommandsProviderField;
|
||||||
|
|
||||||
|
Patches(Logger logger) throws Throwable {
|
||||||
|
this.logger = logger;
|
||||||
|
this.applicationIndexCache = new ApplicationIndexCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void loadPatches(Context context) throws Throwable {
|
||||||
|
this.handleGuildApplicationsUpdateMethod = StoreApplicationCommands.class.getDeclaredMethod("handleGuildApplicationsUpdate", List.class);
|
||||||
|
this.handleGuildApplicationsUpdateMethod.setAccessible(true);
|
||||||
|
this.handleDiscoverCommandsUpdateMethod = StoreApplicationCommands.class.getDeclaredMethod("handleDiscoverCommandsUpdate", List.class);
|
||||||
|
this.handleDiscoverCommandsUpdateMethod.setAccessible(true);
|
||||||
|
this.handleQueryCommandsUpdateMethod = StoreApplicationCommands.class.getDeclaredMethod("handleQueryCommandsUpdate", List.class);
|
||||||
|
this.handleQueryCommandsUpdateMethod.setAccessible(true);
|
||||||
|
this.applicationCommandCountField = Application.class.getDeclaredField("commandCount");
|
||||||
|
this.applicationCommandCountField.setAccessible(true);
|
||||||
|
this.storeApplicationCommandsQueryField = StoreApplicationCommands.class.getDeclaredField("query");
|
||||||
|
this.storeApplicationCommandsQueryField.setAccessible(true);
|
||||||
|
this.errorResponseErrorField = Error.Response.class.getDeclaredField("skemaError");
|
||||||
|
this.errorResponseErrorField.setAccessible(true);
|
||||||
|
this.skemaErrorSubErrorsField = Error.SkemaError.class.getDeclaredField("subErrors");
|
||||||
|
this.skemaErrorSubErrorsField.setAccessible(true);
|
||||||
|
this.skemaErrorErrorsField = Error.SkemaError.class.getDeclaredField("errors");
|
||||||
|
this.skemaErrorErrorsField.setAccessible(true);
|
||||||
|
this.skemaErrorItemCodeField = Error.SkemaErrorItem.class.getDeclaredField("code");
|
||||||
|
this.skemaErrorItemCodeField.setAccessible(true);
|
||||||
|
this.skemaErrorItemMessageField = Error.SkemaErrorItem.class.getDeclaredField("message");
|
||||||
|
this.skemaErrorItemMessageField.setAccessible(true);
|
||||||
|
this.storeApplicationCommandsBuiltInCommandsProviderField = StoreApplicationCommands.class.getDeclaredField("builtInCommandsProvider");
|
||||||
|
this.storeApplicationCommandsBuiltInCommandsProviderField.setAccessible(true);
|
||||||
|
|
||||||
|
var storeApplicationCommands = StoreStream.getApplicationCommands();
|
||||||
|
var storeChannelsSelected = StoreStream.getChannelsSelected();
|
||||||
|
var storeUsers = StoreStream.getUsers();
|
||||||
|
var storePermissions = StoreStream.getPermissions();
|
||||||
|
var storeGuilds = StoreStream.getGuilds();
|
||||||
|
|
||||||
|
// Browsing commands (when just a '/' is typed)
|
||||||
|
Patcher.addPatch(
|
||||||
|
StoreApplicationCommands$requestApplicationCommands$1.class.getDeclaredMethod("invoke"),
|
||||||
|
new PreHook(param -> {
|
||||||
|
var this_ = (StoreApplicationCommands$requestApplicationCommands$1) param.thisObject;
|
||||||
|
|
||||||
|
if (this_.$guildId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationIndexSource = Patches.applicationIndexSourceFromContext(this_.$guildId, storeChannelsSelected);
|
||||||
|
try {
|
||||||
|
this.passCommandData(this_.this$0, applicationIndexSource, RequestSource.BROWSE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
param.setResult(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Completing commands
|
||||||
|
Patcher.addPatch(
|
||||||
|
StoreApplicationCommands$requestApplicationCommandsQuery$1.class.getDeclaredMethod("invoke"),
|
||||||
|
new PreHook(param -> {
|
||||||
|
var this_ = (StoreApplicationCommands$requestApplicationCommandsQuery$1) param.thisObject;
|
||||||
|
|
||||||
|
if (this_.$guildId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationIndexSource = Patches.applicationIndexSourceFromContext(this_.$guildId, storeChannelsSelected);
|
||||||
|
try {
|
||||||
|
storeApplicationCommandsQueryField.set(this_.this$0, this_.$query);
|
||||||
|
this.passCommandData(this_.this$0, applicationIndexSource, RequestSource.QUERY);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
param.setResult(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Command permission check
|
||||||
|
Patcher.addPatch(
|
||||||
|
ApplicationCommandKt.class.getDeclaredMethod("hasPermission", ApplicationCommand.class, long.class, List.class),
|
||||||
|
new InsteadHook(param -> {
|
||||||
|
var applicationCommand = (ApplicationCommand) param.args[0];
|
||||||
|
var roleIds = (List<Long>) param.args[2];
|
||||||
|
|
||||||
|
if (!(applicationCommand instanceof RemoteApplicationCommand)) {
|
||||||
|
// Allow all builtin commands
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var remoteApplicationCommand = (RemoteApplicationCommand) applicationCommand;
|
||||||
|
|
||||||
|
var channel = storeChannelsSelected.getSelectedChannel();
|
||||||
|
var guildId = channel.i();
|
||||||
|
|
||||||
|
if (guildId == 0) {
|
||||||
|
// Allow all commands in DMs
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationId = remoteApplicationCommand.getApplicationId();
|
||||||
|
var isUser = this.requestApplicationIndex(new ApplicationIndexSourceUser())
|
||||||
|
.applications
|
||||||
|
.containsKey(applicationId);
|
||||||
|
if (isUser) {
|
||||||
|
// Allow all user application commands
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var application = this.requestApplicationIndex(new ApplicationIndexSourceGuild(guildId))
|
||||||
|
.applications
|
||||||
|
.get(applicationId);
|
||||||
|
if (application == null) {
|
||||||
|
// Discord requested checking a command from the previous guild - ignore
|
||||||
|
// Some such requests are still processed (if the command exists in both guilds), but it's not an issue as the result doesn't matter for them anyways.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var user = storeUsers.getMe();
|
||||||
|
var memberPermissions = storePermissions.getGuildPermissions()
|
||||||
|
.get(guildId);
|
||||||
|
var guild = storeGuilds.getGuild(guildId);
|
||||||
|
|
||||||
|
var applicationPermission = application.permissions_.checkFor(roleIds, channel, guild, memberPermissions, user, true);
|
||||||
|
var commandPermission = remoteApplicationCommand.permissions_.checkFor(roleIds, channel, guild, memberPermissions, user, applicationPermission);
|
||||||
|
|
||||||
|
return commandPermission;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Command error handling
|
||||||
|
Patcher.addPatch(
|
||||||
|
StoreApplicationInteractions.class.getDeclaredMethod("handleApplicationCommandResult", MessageResult.class, ApplicationCommandLocalSendData.class, Function0.class, Function1.class),
|
||||||
|
new PreHook(param -> {
|
||||||
|
var result = (MessageResult) param.args[0];
|
||||||
|
var localSendData = (ApplicationCommandLocalSendData) param.args[1];
|
||||||
|
|
||||||
|
if (result instanceof MessageResult.UnknownFailure) {
|
||||||
|
boolean invalidCommandVersion = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var errorResponse = ((MessageResult.UnknownFailure) result)
|
||||||
|
.getError()
|
||||||
|
.getResponse();
|
||||||
|
var error = this.errorResponseErrorField.get(errorResponse);
|
||||||
|
var subErrors = ((Map<String, Error.SkemaError>) skemaErrorSubErrorsField.get(error));
|
||||||
|
var dataErrors = (List<Error.SkemaErrorItem>) skemaErrorErrorsField.get(subErrors.get("data"));
|
||||||
|
|
||||||
|
for (var dataError: dataErrors) {
|
||||||
|
var errorCode = (String) this.skemaErrorItemCodeField.get(dataError);
|
||||||
|
if (errorCode.equals("INTERACTION_APPLICATION_COMMAND_INVALID_VERSION")) {
|
||||||
|
ApplicationIndexSource applicationIndexSource = null;
|
||||||
|
var guildId = localSendData.component3();
|
||||||
|
if (guildId != null) {
|
||||||
|
applicationIndexSource = new ApplicationIndexSourceGuild(guildId);
|
||||||
|
} else {
|
||||||
|
var channelId = localSendData.component2();
|
||||||
|
applicationIndexSource = new ApplicationIndexSourceDm(channelId);
|
||||||
|
}
|
||||||
|
this.cleanApplicationIndexCache(applicationIndexSource);
|
||||||
|
|
||||||
|
var errorMessage = (String) this.skemaErrorItemMessageField.get(dataError);
|
||||||
|
Utils.showToast(errorMessage);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
GatewayAPI.onEvent("GUILD_APPLICATION_COMMAND_INDEX_UPDATE", ApiGuildApplicationCommandIndexUpdate.class, guildApplicationCommandIndexUpdate -> {
|
||||||
|
this.cleanApplicationIndexCache(new ApplicationIndexSourceGuild(guildApplicationCommandIndexUpdate.guildId));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void passCommandData(StoreApplicationCommands storeApplicationCommands, Optional<ApplicationIndexSource> applicationIndexSource, RequestSource requestSource) throws Exception {
|
||||||
|
var applicationIndexes = new ArrayList();
|
||||||
|
if (applicationIndexSource.isPresent()) {
|
||||||
|
applicationIndexes.add(this.requestApplicationIndex(applicationIndexSource.get()));
|
||||||
|
}
|
||||||
|
applicationIndexes.add(this.requestApplicationIndex(new ApplicationIndexSourceUser()));
|
||||||
|
var applicationIndex = new ApplicationIndex(applicationIndexes);
|
||||||
|
applicationIndex
|
||||||
|
.applicationCommands
|
||||||
|
.entrySet()
|
||||||
|
.removeIf(applicationCommand -> applicationCommand.getValue().type != RemoteApplicationCommand.TYPE_CHAT_INPUT);
|
||||||
|
applicationIndex.populateCommandCounts(this.applicationCommandCountField);
|
||||||
|
|
||||||
|
var applications = new ArrayList<Application>(applicationIndex.applications.values());
|
||||||
|
Collections.sort(applications, (left, right) -> left.getName().compareTo(right.getName()));
|
||||||
|
applications.add(((BuiltInCommandsProvider) this.storeApplicationCommandsBuiltInCommandsProviderField.get(storeApplicationCommands)).getBuiltInApplication());
|
||||||
|
this.handleGuildApplicationsUpdateMethod.invoke(storeApplicationCommands, applications);
|
||||||
|
|
||||||
|
switch (requestSource) {
|
||||||
|
case BROWSE:
|
||||||
|
this.handleDiscoverCommandsUpdateMethod.invoke(storeApplicationCommands, new ArrayList(applicationIndex.applicationCommands.values()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QUERY:
|
||||||
|
this.handleQueryCommandsUpdateMethod.invoke(storeApplicationCommands, new ArrayList(applicationIndex.applicationCommands.values()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationIndex requestApplicationIndex(ApplicationIndexSource source) {
|
||||||
|
// Reuse application index from cache
|
||||||
|
var applicationIndex = source.getFromCache(applicationIndexCache);
|
||||||
|
if (!applicationIndex.isPresent()) {
|
||||||
|
try {
|
||||||
|
// Request application index from API
|
||||||
|
applicationIndex = Optional.of(
|
||||||
|
Http.Request.newDiscordRNRequest(source.getEndpoint())
|
||||||
|
.execute()
|
||||||
|
.json(GsonUtils.getGsonRestApi(), ApiApplicationIndex.class)
|
||||||
|
.toModel()
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.insertIntoCache(applicationIndexCache, applicationIndex.get());
|
||||||
|
}
|
||||||
|
return applicationIndex.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanApplicationIndexCache(ApplicationIndexSource source) {
|
||||||
|
source.removeFromCache(applicationIndexCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<ApplicationIndexSource> applicationIndexSourceFromContext(long guildId, StoreChannelsSelected storeChannelsSelected) {
|
||||||
|
Optional<ApplicationIndexSource> applicationIndexSource = Optional.empty();
|
||||||
|
// guildId being 0 means this is a DM or a DM group
|
||||||
|
if (guildId != 0) {
|
||||||
|
applicationIndexSource = Optional.of(new ApplicationIndexSourceGuild(guildId));
|
||||||
|
} else {
|
||||||
|
// Only create a DM index source for bots
|
||||||
|
var channel = storeChannelsSelected.getSelectedChannel();
|
||||||
|
var channelType = channel.D();
|
||||||
|
if (channelType == Channel.DM) {
|
||||||
|
var user = channel.z().get(0);
|
||||||
|
var userIsBot = Optional.ofNullable(user.e())
|
||||||
|
.orElse(false);
|
||||||
|
if (userIsBot) {
|
||||||
|
var channelId = channel.k();
|
||||||
|
applicationIndexSource = Optional.of(new ApplicationIndexSourceDm(channelId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return applicationIndexSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import com.discord.api.channel.Channel;
|
||||||
|
import com.discord.api.permission.Permission;
|
||||||
|
import com.discord.models.guild.Guild;
|
||||||
|
import com.discord.models.user.MeUser;
|
||||||
|
import com.discord.utilities.permissions.PermissionUtils;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class Permissions {
|
||||||
|
public Optional<Boolean> user;
|
||||||
|
public Map<Long, Boolean> roles;
|
||||||
|
public Map<Long, Boolean> channels;
|
||||||
|
public Optional<Long> defaultMemberPermissions;
|
||||||
|
|
||||||
|
public Permissions(Optional<Boolean> user, Map<Long, Boolean> roles, Map<Long, Boolean> channels, Optional<Long> defaultMemberPermissions) {
|
||||||
|
this.user = Optional.ofNullable(user).orElse(Optional.empty());
|
||||||
|
this.roles = Optional.ofNullable(roles).orElse(new HashMap<>());
|
||||||
|
this.channels = Optional.ofNullable(channels).orElse(new HashMap<>());
|
||||||
|
this.defaultMemberPermissions = Optional.ofNullable(defaultMemberPermissions).orElse(Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkFor(List<Long> roleIds, Channel channel, Guild guild, long memberPermissions, MeUser user, boolean defaultPermission) {
|
||||||
|
var guildId = guild.component7();
|
||||||
|
var defaultChannelPermissionId = guildId - 1;
|
||||||
|
var defaultChannelPermission = this.channels.getOrDefault(defaultChannelPermissionId, defaultPermission);
|
||||||
|
var channelType = channel.D();
|
||||||
|
var channelId = channel.k();
|
||||||
|
var permissionChannelId = channelId;
|
||||||
|
// Threads inherit permissions from their parent channels
|
||||||
|
if (channelType == Channel.ANNOUNCEMENT_THREAD || channelType == Channel.PUBLIC_THREAD || channelType == Channel.PRIVATE_THREAD) {
|
||||||
|
var channelParentId = channel.u();
|
||||||
|
permissionChannelId = channelParentId;
|
||||||
|
}
|
||||||
|
var channelPermission = Optional.ofNullable(this.channels.get(permissionChannelId))
|
||||||
|
.orElse(defaultChannelPermission);
|
||||||
|
var defaultMemberPermission = this.defaultMemberPermissions
|
||||||
|
.map(
|
||||||
|
defaultMemberPermissions -> defaultMemberPermissions != 0
|
||||||
|
&& PermissionUtils.canAndIsElevated(
|
||||||
|
defaultMemberPermissions,
|
||||||
|
memberPermissions,
|
||||||
|
user.getMfaEnabled(),
|
||||||
|
guild.getMfaLevel()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orElse(defaultPermission);
|
||||||
|
var everyoneRoleId = guildId;
|
||||||
|
var defaultRolePermission = this.roles.getOrDefault(everyoneRoleId, defaultMemberPermission);
|
||||||
|
var rolePermission = this.calculateRolePermission(roleIds, defaultRolePermission);
|
||||||
|
var userPermission = this.user.orElse(defaultMemberPermission);
|
||||||
|
var administratorPermission = PermissionUtils.canAndIsElevated(
|
||||||
|
Permission.ADMINISTRATOR,
|
||||||
|
memberPermissions,
|
||||||
|
user.getMfaEnabled(),
|
||||||
|
guild.getMfaLevel()
|
||||||
|
);
|
||||||
|
|
||||||
|
return administratorPermission || (channelPermission && (userPermission || rolePermission));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean calculateRolePermission(List<Long> roleIds, boolean defaultPermission) {
|
||||||
|
var calculatedRolePermission = defaultPermission;
|
||||||
|
for (var roleId: roleIds) {
|
||||||
|
var rolePermission = this.roles.get(roleId);
|
||||||
|
if (rolePermission != null) {
|
||||||
|
calculatedRolePermission = rolePermission;
|
||||||
|
if (rolePermission) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return calculatedRolePermission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import com.discord.models.commands.ApplicationCommandOption;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class RemoteApplicationCommand extends com.discord.models.commands.RemoteApplicationCommand {
|
||||||
|
public Permissions permissions_;
|
||||||
|
public int type;
|
||||||
|
|
||||||
|
public static final int TYPE_CHAT_INPUT = 1;
|
||||||
|
|
||||||
|
public RemoteApplicationCommand(String id, long applicationId, String name, String description, List<ApplicationCommandOption> options, Permissions permissions, Long guildId, String version, int type) {
|
||||||
|
super(id, applicationId, name, description, options, guildId, version, null, null, null);
|
||||||
|
this.permissions_ = permissions;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
enum RequestSource {
|
||||||
|
BROWSE,
|
||||||
|
QUERY;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Aliucord, an Android Discord client mod.
|
||||||
|
* Copyright (c) 2021 Juby210 & Vendicated
|
||||||
|
* Licensed under the Open Software License version 3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aliucord.coreplugins.slashcommandsfix;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.aliucord.annotations.AliucordPlugin;
|
||||||
|
import com.aliucord.entities.Plugin;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
@AliucordPlugin
|
||||||
|
public final class SlashCommandsFix extends Plugin {
|
||||||
|
public SlashCommandsFix() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Context context) throws Throwable {
|
||||||
|
if (ConflictCheck.run(context)) return;
|
||||||
|
|
||||||
|
XposedBridge.makeClassInheritable(com.discord.models.commands.Application.class);
|
||||||
|
XposedBridge.makeClassInheritable(com.discord.models.commands.RemoteApplicationCommand.class);
|
||||||
|
|
||||||
|
new Patches(this.logger).loadPatches(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop(Context context) {}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
rootProject.name = "Awoocord"
|
rootProject.name = "Awoocord"
|
||||||
|
|
||||||
val canaryPlugins = arrayOf("ComponentsV2")
|
val canaryPlugins = arrayOf("ComponentsV2", "SlashCommandsFix")
|
||||||
|
|
||||||
include(
|
include(
|
||||||
"AlignThreads",
|
"AlignThreads",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue