commit bcdb9e8766b00354e67517815a313cc7d08d7e8a Author: clienthax Date: Mon Dec 21 22:29:15 2020 +0000 Initial work on porting to dexpatcher. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b9bf75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild + diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..4d0ca22 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +/build +/apk/* \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..6b9249c --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,176 @@ +// DexPatcher Patched Application plugin: + +apply plugin: 'com.github.lanchon.dexpatcher.patched-application' + +// The DexPatcher Patched Application plugin produces a modified Android application by +// applying changes to code, manifest and resources of a source application provided as +// an Android APK or a DexPatcher APK library. + +dependencies { + // The source application is defined as a Gradle dependency. + + // It is defined either as an Android APK dependency: + sourceApk fileTree(dir: 'apk', include: ['*.apk', '*.jar', '*.zip']) + + // Or as a DexPatcher APK library dependency: + //sourceApkLibrary fileTree(dir: 'apklib', include: ['*.apklib']) + + // The contents of the 'apk' and 'apklib' project subdirectories are included in + // these configurations by default. Exactly one source application dependency + // must be available to the build. +} + +// General Plugin Configuration +dexpatcherConfig { + // Plugin-Specific Configuration + patchedApplication { + // Display package and version information of the source app on every build. + // Note that this info can also be queried by invoking the 'sourceAppInfo' task. + // Defaults to false. + printAppInfo = true; + + // Enable import of Java symbols from the source app. Defaults to true. + importSymbols = true; + + // Disable detection of invalid resources and attempt to process them normally. + // Defaults to false. + disableResourceValidation = true + + // Build the patched app using a custom AAPT2 binary bundled with the version of + // Apktool currently in use. These AAPT2 binaries are modified to allow rebuilding + // of slightly out-of-spec apps, such as apps that contain invalid resources. + // Defaults to false. + useAapt2BundledWithApktool = true + + // Speed up debug builds by enabling multi-threaded multi-dex processing for them, + // but only if multi-dex mode is already enabled for them. Defaults to true. + //multiDexThreadedForMultiDexDebugBuilds = true + + // Speed up debug builds by enabling multi-threaded multi-dex processing for them, + // even if multi-dex mode is not already enabled for them. Note that apps patched + // in multi-dex mode are only compatible with devices running Android 5.0 (API 21) + // and later. Defaults to false. + //multiDexThreadedForAllDebugBuilds = true + + //dexpatcherAnnotationClasspath.setFrom project.files(...) + } + + // Apktool Task Defaults + apktool { + // Use a framework provided in a project subdirectory: + //frameworkInDir = layout.projectDirectory.dir('framework') + //frameworkTag = 'tag' + + //apiLevel = 14 + //keepBrokenResources = true + //stripDebugInfo = true + + //classpath.setFrom project.files(...) + //extraArgs.addAll ... + } + + // Dex2jar Task Defaults + dex2jar { + //translateCode = false + //translateDebugInfo = true + //optimizeSynchronized = true + //reuseRegisters = true + + //classpath.setFrom project.files(...) + //extraArgs.addAll ... + } + + // DexPatcher Task Defaults + dexpatcher { + // The Android api level of dex files. The api level is auto-detected by DexPatcher + // v1.2.0 and later, so this setting is no longer needed for normal operation. + //apiLevel = 14 + + // DexPatcher multi-dex patching mode is automatically enabled if multi-dex is + // enabled in the Android build system. However, you can also enable multi-dex + // patching manually: + //multiDex = true + // Note that multi-dex need not be enabled in the Android build system unless the + // patch dex itself is large and requires it. The dex sizes of the source app and + // the patched app are not relevant. + + // Enable multi-threaded multi-dex processing, if multi-dex mode is already enabled. + // Multi-threaded processing is faster but produces more dex files than the bare + // minimum required by the contents of the patched app. This is recommended for + // debug builds but not for releases. + //multiDexThreaded = true + + // Set the number of multi-dex processing threads, if multi-threaded multi-dex mode + // is already enabled. Defaults to the number of available processors up to 4. + //multiDexJobs = 8 + + // The maximum number of items in the pools of the output dex files. This limits, + // for example, the number of method references in each dex file. Defaults to 65536. + //maxDexPoolSize = 65536 + + // The package name of the used DexPatcher annotations. Useful to handle rogue apps + // that want to disallow patching by intentionally clashing with standard DexPatcher + // annotation names. Defaults to 'lanchon.dexpatcher.annotation'. + //annotationPackage = 'lanchon.dexpatcher.annotation' + + // Enable implicit ignore of trivial default constructors. Defaults to true. + //constructorAutoIgnore = true + + // The verbosity of the DexPatcher tool. One of QUIET, NORMAL, VERBOSE or DEBUG. + // Defaults to NORMAL, but VERBOSE is recommended. + verbosity = VERBOSE + + // Enable logging of relative paths of source files instead of just their filename. + //logSourcePath = true + + // Enable logging of paths of source files prepending the specified root. + //logSourcePathRoot = file('src/main/java') + + // Enable logging of timing statistics. + //logStats = true + + //classpath.setFrom project.files(...) + //extraArgs.addAll ... + } +} + +android { + compileSdkVersion 29 + //buildToolsVersion '28.0.3' + + defaultConfig { + // The 'package' attribute in the patched app manifest must be different from the + // source app ID to support class generation (R and BuildConfig), but here you can + // override the exported patched app ID to match the source. + applicationId 'com.discord' + + // You must set these fields to match or be compatible with the corresponding data + // of the source app. (Data is displayed when the source apk library is built.) + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1367 + versionName '53.8' + + } + + lintOptions { + disableLintTasks true + //checkReleaseBuilds false + } +} + +dependencies { + //implementation "androidx.exifinterface:exifinterface:1.3.2" + //implementation 'androidx.appcompat:appcompat:1.2.0' + //provided "com.google.android.material:material:1.2.1" + + +} + +void disableLintTasks(boolean disableTasks) { + if (disableTasks) { + tasks.withType(com.android.build.gradle.tasks.LintBaseTask).configureEach { + enabled = false + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ab9aee3 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/PatchConfig.java b/app/src/main/java/com/PatchConfig.java new file mode 100644 index 0000000..f9787db --- /dev/null +++ b/app/src/main/java/com/PatchConfig.java @@ -0,0 +1,30 @@ +package com; + +import lanchon.dexpatcher.annotation.DexAdd; + +@DexAdd +public class PatchConfig { + + // Patch toggles + + public static final boolean NOZLIB_ENABLED = false; + public static final boolean HIDE_UNUSABLE_EMOJIS_ENABLED = true; + public static final boolean SLASH_COMMANDS_ENABLED = true; + public static final boolean CUSTOM_VERSION_ENABLED = true; + public static final boolean EMBEDLINKS_ENABLED = true; + public static final boolean SHOWTAG_ENABLED = true; + public static final boolean NONEARBY_ENABLED = true; + public static final boolean NOSPOILER_ENABLED = false; + public static final boolean DISABLE_MOBILE_INDICATOR_ENABLED = true; + public static final boolean HQAVATARS_ENABLED = true; + + public static final boolean PROFILEMENTION_ENABLED = true; + public static final boolean TOKENLOGIN_ENABLED = true; + + + // Can't be changed at runtime + public static final boolean PSEUDONITRO_VIEWER_ENABLED = true; + public static final boolean LITECORD_ENABLED = false; // TODO + + +} diff --git a/app/src/main/java/com/discord/app/App.java b/app/src/main/java/com/discord/app/App.java new file mode 100644 index 0000000..680b7e8 --- /dev/null +++ b/app/src/main/java/com/discord/app/App.java @@ -0,0 +1,10 @@ +package com.discord.app; + +import lanchon.dexpatcher.annotation.DexEdit; + +@DexEdit +public class App { + + + +} diff --git a/app/src/main/java/com/discord/gateway/io/OutgoingPayload.java b/app/src/main/java/com/discord/gateway/io/OutgoingPayload.java new file mode 100644 index 0000000..0a62da1 --- /dev/null +++ b/app/src/main/java/com/discord/gateway/io/OutgoingPayload.java @@ -0,0 +1,37 @@ +package com.discord.gateway.io; + +import com.PatchConfig; + +import java.util.Map; + +import lanchon.dexpatcher.annotation.DexAdd; +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexEdit +public abstract class OutgoingPayload { + + + // Change compress to false, part of nozlib patch + @DexEdit + public static final class Identify extends OutgoingPayload { + + // add argument to original constructor so we can chain to this below + @DexEdit + public Identify(String token, int large_threshold, boolean compress, long capabilities, Map properties, IdentifyClientState identifyClientState, @DexIgnore Void tag) { + } + + // Replace call to constructor with chain + @DexAdd + public Identify(String token, int large_threshold, boolean compress, long capabilities, Map properties, IdentifyClientState identifyClientState) { + // Set compress = false + //noinspection RedundantConditionalExpression + this(token, large_threshold, PatchConfig.NOZLIB_ENABLED ? false : true, capabilities, properties, identifyClientState, (Void) null); + } + } + + @DexIgnore + public static final class IdentifyClientState { + } + +} diff --git a/app/src/main/java/com/discord/models/domain/ModelAllowedMentions.java b/app/src/main/java/com/discord/models/domain/ModelAllowedMentions.java new file mode 100644 index 0000000..dfeef09 --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelAllowedMentions.java @@ -0,0 +1,7 @@ +package com.discord.models.domain; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelAllowedMentions { +} diff --git a/app/src/main/java/com/discord/models/domain/ModelApplication.java b/app/src/main/java/com/discord/models/domain/ModelApplication.java new file mode 100644 index 0000000..f584542 --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelApplication.java @@ -0,0 +1,7 @@ +package com.discord.models.domain; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelApplication { +} diff --git a/app/src/main/java/com/discord/models/domain/ModelChannel.java b/app/src/main/java/com/discord/models/domain/ModelChannel.java new file mode 100644 index 0000000..eaee0fc --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelChannel.java @@ -0,0 +1,13 @@ +package com.discord.models.domain; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelChannel { + + @DexIgnore + public static class RecipientNick { + + } + +} diff --git a/app/src/main/java/com/discord/models/domain/ModelGuildMember.java b/app/src/main/java/com/discord/models/domain/ModelGuildMember.java new file mode 100644 index 0000000..43e37e4 --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelGuildMember.java @@ -0,0 +1,13 @@ +package com.discord.models.domain; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelGuildMember { + + @DexIgnore + public static class Computed { + + } + +} diff --git a/app/src/main/java/com/discord/models/domain/ModelMessage.java b/app/src/main/java/com/discord/models/domain/ModelMessage.java new file mode 100644 index 0000000..e61ec9b --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelMessage.java @@ -0,0 +1,16 @@ +package com.discord.models.domain; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelMessage { + + @DexIgnore + public static class MessageReference { + + } + + @DexIgnore + public static class Activity { + } +} diff --git a/app/src/main/java/com/discord/models/domain/ModelMessageEmbed.java b/app/src/main/java/com/discord/models/domain/ModelMessageEmbed.java new file mode 100644 index 0000000..0e0e337 --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelMessageEmbed.java @@ -0,0 +1,24 @@ +package com.discord.models.domain; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class ModelMessageEmbed { + + // nospoiler patch + + @DexWrap + public boolean isSpoilerAttachment() { + if (!PatchConfig.NOSPOILER_ENABLED) { + // Patch not enabled + return isSpoilerAttachment(); + } + + return false; + } + +} diff --git a/app/src/main/java/com/discord/models/domain/ModelUser.java b/app/src/main/java/com/discord/models/domain/ModelUser.java new file mode 100644 index 0000000..f8bf1ad --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/ModelUser.java @@ -0,0 +1,34 @@ +package com.discord.models.domain; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import java.util.Map; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class ModelUser { + + // showtag patch + + @DexWrap + public String getNickOrUsername(ModelGuildMember.Computed computed, ModelChannel modelChannel, Map map) { + String orig = getNickOrUsername(computed, modelChannel, map); + + if (!PatchConfig.SHOWTAG_ENABLED) { + // Patch not enabled + return orig; + } + + return orig + " (" + getUserNameWithDiscriminator() + ")"; + } + + @DexIgnore + public String getUserNameWithDiscriminator() { + return null; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/discord/models/domain/activity/ModelActivity.java b/app/src/main/java/com/discord/models/domain/activity/ModelActivity.java new file mode 100644 index 0000000..17384be --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/activity/ModelActivity.java @@ -0,0 +1,7 @@ +package com.discord.models.domain.activity; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelActivity { +} diff --git a/app/src/main/java/com/discord/models/domain/emoji/Emoji.java b/app/src/main/java/com/discord/models/domain/emoji/Emoji.java new file mode 100644 index 0000000..c2972b4 --- /dev/null +++ b/app/src/main/java/com/discord/models/domain/emoji/Emoji.java @@ -0,0 +1,47 @@ +package com.discord.models.domain.emoji; + +import android.content.Context; +import android.os.Parcelable; +import androidx.annotation.Nullable; +import java.util.List; +import java.util.regex.Pattern; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public interface Emoji extends Parcelable { + + @DexIgnore + String getChatInputText(); + + @DexIgnore + String getCommand(@Nullable String str); + + @DexIgnore + String getFirstName(); + + @DexIgnore + String getImageUri(boolean z, int i, Context context); + + @DexIgnore + String getMessageContentReplacement(); + + @DexIgnore + List getNames(); + + @DexIgnore + String getReactionKey(); + + @DexIgnore + Pattern getRegex(@Nullable String str); + + @DexIgnore + String getUniqueId(); + + @DexIgnore + boolean isAvailable(); + + @DexIgnore + boolean isUsable(); + +} diff --git a/app/src/main/java/com/discord/models/sticker/dto/ModelSticker.java b/app/src/main/java/com/discord/models/sticker/dto/ModelSticker.java new file mode 100644 index 0000000..58f93c4 --- /dev/null +++ b/app/src/main/java/com/discord/models/sticker/dto/ModelSticker.java @@ -0,0 +1,7 @@ +package com.discord.models.sticker.dto; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class ModelSticker { +} diff --git a/app/src/main/java/com/discord/stores/Store.java b/app/src/main/java/com/discord/stores/Store.java new file mode 100644 index 0000000..d1956cf --- /dev/null +++ b/app/src/main/java/com/discord/stores/Store.java @@ -0,0 +1,13 @@ +package com.discord.stores; + +import android.content.SharedPreferences; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class Store { + + @DexIgnore + public SharedPreferences prefs; + +} diff --git a/app/src/main/java/com/discord/stores/StoreAuthentication.java b/app/src/main/java/com/discord/stores/StoreAuthentication.java new file mode 100644 index 0000000..29734fa --- /dev/null +++ b/app/src/main/java/com/discord/stores/StoreAuthentication.java @@ -0,0 +1,7 @@ +package com.discord.stores; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class StoreAuthentication extends Store { +} diff --git a/app/src/main/java/com/discord/stores/StoreMessages.java b/app/src/main/java/com/discord/stores/StoreMessages.java new file mode 100644 index 0000000..24c16ec --- /dev/null +++ b/app/src/main/java/com/discord/stores/StoreMessages.java @@ -0,0 +1,480 @@ +package com.discord.stores; + +import com.PatchConfig; +import com.discord.models.domain.ModelAllowedMentions; +import com.discord.models.domain.ModelApplication; +import com.discord.models.domain.ModelMessage; +import com.discord.models.domain.ModelUser; +import com.discord.models.domain.activity.ModelActivity; +import com.discord.models.sticker.dto.ModelSticker; +import com.discord.utilities.messagesend.MessageResult; +import com.lytefast.flexinput.model.Attachment; + +import java.util.List; + +import lanchon.dexpatcher.annotation.DexAdd; +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; +import rx.Observable; + + +@DexEdit +public final class StoreMessages { + + // slashmessages patch + + // Wrap edit function to check for commands + @DexWrap + public final void editMessage(long j, long j2, String str) { + if (!PatchConfig.SLASH_COMMANDS_ENABLED) { + // Patch not enabled + editMessage(j, j2, str); + return; + } + + String interceptEditMessage = interceptEditMessage(str); + editMessage(j, j2, interceptEditMessage); + } + + // Wrap send function to check for commands + @DexWrap + public final Observable sendMessage(long j, + ModelUser modelUser, + String message, + List list, + List> list2, + List list3, + ModelMessage.MessageReference messageReference, + ModelAllowedMentions modelAllowedMentions, + ModelApplication modelApplication, + ModelActivity modelActivity, + ModelMessage.Activity activity, + Long l, + Long l2, + Integer num) { + if (!PatchConfig.SLASH_COMMANDS_ENABLED) { + // Patch not enabled + return sendMessage(j, modelUser, message, list, list2, list3, messageReference, modelAllowedMentions, modelApplication, modelActivity, activity, l, l2, num); + } + + String interceptEditMessage = interceptSendMessage(message); + return sendMessage(j, modelUser, interceptEditMessage, list, list2, list3, messageReference, modelAllowedMentions, modelApplication, modelActivity, activity, l, l2, num); + } + + // Add in helper functions + + @DexAdd + public static final String interceptEditMessage(String str) { + return str.startsWith("/") ? slashCommands(str) : str; + } + + @DexAdd + public static final String interceptSendMessage(String str) { + StoreStream.getUserSettings().setImageSpoiler(false); + return str.startsWith("/") ? slashCommands(str) : str; + } + + @DexAdd + + public static final String slashCommands(String str) { + String trim = str.trim(); + if (trim.startsWith("/upper ")) { + trim = slashUpper(trim); + } else if (trim.startsWith("/lower ")) { + trim = slashLower(trim); + } else if (trim.startsWith("/bold ")) { + trim = slashBold(trim); + } else if (trim.startsWith("/spoiler ")) { + trim = slashSpoiler(trim); + } else if (trim.startsWith("/me ")) { + trim = slashMe(trim); + } else if (trim.startsWith("/st ")) { + trim = slashSt(trim); + } else if (trim.startsWith("/lenny")) { + trim = slashLenny(trim); + } else if (trim.startsWith("/fw ")) { + trim = slashFw(trim); + } else if (trim.startsWith("/small ")) { + trim = slashSmall(trim); + } else if (trim.startsWith("/smaller ")) { + trim = slashSmaller(trim); + } else if (trim.startsWith("/flip ")) { + trim = slashFlip(trim); + } else if (trim.startsWith("/clap ")) { + trim = slashClap(trim); + } else if (trim.startsWith("/owo ")) { + trim = slashOwo(trim); + } else if (trim.startsWith("/morse ")) { + trim = slashMorse(trim); + } else if (trim.startsWith("/spoilerimg")) { + trim = slashSpoilerImg(trim); + } else if (trim.startsWith("/gordon ")) { + trim = slashGordon(trim); + } + return trim.trim(); + } + + @DexAdd + public static final String slashBold(String str) { + String substring = str.substring(6); + return "**" + substring + "**"; + } + + @DexAdd + public static final String slashClap(String str) { + return str.substring(6).replace(" ", " :clap: "); + } + + @DexAdd + public static final String slashCtc(String str) { + String substring = str.substring(5); + if (substring.startsWith("channelleak ")) { + StoreStream.getUserSettings().setLeakChannels(substring.substring(12).startsWith("false")); + return "CTC: Successfully set channelleak state."; + } else if (substring.startsWith("showtyping ")) { + StoreStream.getUserSettings().setShowTyping(substring.substring(11).startsWith("true")); + return "CTC: Successfully set showtyping state."; + } else if (substring.startsWith("token ")) { + StoreStream.getUserSettings().setStoredToken(substring.substring(6)); + return "CTC: Successfully changed token. Please restart application."; + } else if (substring.startsWith("token")) { + return StoreStream.getUserSettings().getStoredToken(); + } else { + if (substring.startsWith("account ")) { + String trim = substring.substring(8).trim(); + StoreUserSettings userSettings = StoreStream.getUserSettings(); + String accountToken = userSettings.getAccountToken(trim); + if (accountToken.startsWith("none")) { + return "CTC: No such account found."; + } + userSettings.setStoredToken(accountToken); + return "CTC: Successfully changed accounts. Please restart application."; + } else if (substring.startsWith("addaccount ")) { + String substring2 = substring.substring(11); + String substring3 = substring2.substring(substring2.indexOf(" ")); + String trim2 = substring2.replace(substring3, "").trim(); + String trim3 = substring3.trim(); + StoreUserSettings userSettings2 = StoreStream.getUserSettings(); + if (trim2.startsWith("current")) { + trim2 = userSettings2.getStoredToken(); + } + userSettings2.setAccountToken(trim2, trim3); + return "CTC: Added account."; + } else if (substring.startsWith("nodelete ")) { + StoreStream.getUserSettings().setNoDelete(substring.substring(9).startsWith("true")); + return "CTC: Successfully set nodelete state."; + } else if (!substring.startsWith("gifautoplay ")) { + return "CTC: No known command supplied. (available: token, showtyping, channelleak, addaccount, account, nodelete, gifautoplay. Everything except specified token needs to be lowercase)"; + } else { + StoreStream.getUserSettings().setAutoplayGifs(substring.substring(12).startsWith("true")); + return "CTC: Successfully set gifautoplay state."; + } + } + } + + @DexAdd + public static String slashFlip(String str) { + StringBuilder sb = new StringBuilder(str.substring(6).toLowerCase()); + sb.reverse(); + return sb.toString().replace("a", "ɐ") + .replace("b", "q") + .replace("c", "ɔ") + .replace("d", "p") + .replace("e", "ǝ") + .replace("f", "ɟ") + .replace("g", "ƃ") + .replace("h", "ɥ") + .replace("i", "ı") + .replace("j", "ɾ") + .replace("k", "ʞ") + .replace("l", "ן") + .replace("m", "ɯ") + .replace("n", "u") + .replace("o", "o") + .replace("p", "d") + .replace("q", "b") + .replace("r", "ɹ") + .replace("s", "s") + .replace("t", "ʇ") + .replace("u", "n") + .replace("v", "ʌ") + .replace("w", "ʍ") + .replace("x", "x") + .replace("y", "ʎ") + .replace("z", "z"); + } + + @DexAdd + public static String slashFw(String str) { + return str.substring(4).replace(" ", " ") + .replace("!", "!") + .replace("#", "#") + .replace("$", "$") + .replace("%", "%") + .replace("&", "&") + .replace("'", "'") + .replace("(", "(") + .replace(")", ")") + .replace("*", "*") + .replace("+", "+") + .replace(",", ",") + .replace("-", "-") + .replace(".", ".") + .replace("/", "/") + .replace("0", "0") + .replace("1", "1") + .replace("2", "2") + .replace("3", "3") + .replace("4", "4") + .replace("5", "5") + .replace("6", "6") + .replace("7", "7") + .replace("8", "8") + .replace("9", "9") + .replace(":", ":") + .replace(";", ";") + .replace("<", "<") + .replace("=", "=") + .replace(">", ">") + .replace("?", "?") + .replace("@", "@") + .replace("A", "A") + .replace("B", "B") + .replace("C", "C") + .replace("D", "D") + .replace("E", "E") + .replace("F", "F") + .replace("G", "G") + .replace("H", "H") + .replace("I", "I") + .replace("J", "J") + .replace("K", "K") + .replace("L", "L") + .replace("M", "M") + .replace("N", "N") + .replace("O", "O") + .replace("P", "P") + .replace("Q", "Q") + .replace("R", "R") + .replace("S", "S") + .replace("T", "T") + .replace("U", "U") + .replace("V", "V") + .replace("W", "W") + .replace("X", "X") + .replace("Y", "Y") + .replace("Z", "Z") + .replace("[", "[") + .replace("]", "]") + .replace("^", "^") + .replace("_", "_") + .replace("`", "`") + .replace("a", "a") + .replace("b", "b") + .replace("c", "c") + .replace("d", "d") + .replace("e", "e") + .replace("f", "f") + .replace("g", "g") + .replace("h", "h") + .replace("i", "i") + .replace("j", "j") + .replace("k", "k") + .replace("l", "l") + .replace("m", "m") + .replace("n", "n") + .replace("o", "o") + .replace("p", "p") + .replace("q", "q") + .replace("r", "r") + .replace("s", "s") + .replace("t", "t") + .replace("u", "u") + .replace("v", "v") + .replace("w", "w") + .replace("x", "x") + .replace("y", "y") + .replace("z", "z") + .replace("{", "{") + .replace("|", "|") + .replace("}", "}") + .replace("~", "~"); + } + + @DexAdd + public static String slashGordon(String str) { + return str.substring(8).replace("a", "𝗮") + .replace("b", "𝗯") + .replace("c", "𝗰") + .replace("d", "𝗱") + .replace("e", "𝗲") + .replace("f", "𝗳") + .replace("g", "𝗴") + .replace("h", "𝗵") + .replace("i", "𝗶") + .replace("j", "𝗷") + .replace("k", "𝗸") + .replace("l", "𝗹") + .replace("m", "𝗺") + .replace("n", "𝗻") + .replace("o", "𝗼") + .replace("p", "𝗽") + .replace("q", "𝗾") + .replace("r", "𝗿") + .replace("s", "𝘀") + .replace("t", "𝘁") + .replace("u", "𝘂") + .replace("v", "𝘃") + .replace("w", "𝘄") + .replace("x", "𝘅") + .replace("y", "𝘆") + .replace("z", "𝘇") + .replace("A", "𝗔") + .replace("B", "𝗕") + .replace("C", "𝗖") + .replace("D", "𝗗") + .replace("E", "𝗘") + .replace("F", "𝗙") + .replace("G", "𝗚") + .replace("H", "𝗛") + .replace("I", "𝗜") + .replace("J", "𝗝") + .replace("K", "𝗞") + .replace("L", "𝗟") + .replace("M", "𝗠") + .replace("N", "𝗡") + .replace("O", "𝗢") + .replace("P", "𝗣") + .replace("Q", "𝗤") + .replace("R", "𝗥") + .replace("S", "𝗦") + .replace("T", "𝗧") + .replace("U", "𝗨") + .replace("V", "𝗩") + .replace("W", "𝗪") + .replace("X", "𝗫") + .replace("Y", "𝗬") + .replace("Z", "𝗭") + .replace("0", "𝟬") + .replace("1", "𝟭") + .replace("2", "𝟮") + .replace("3", "𝟯") + .replace("4", "𝟰") + .replace("5", "𝟱") + .replace("6", "𝟲") + .replace("7", "𝟳") + .replace("8", "𝟴") + .replace("9", "𝟵"); + } + + @DexAdd + public static String slashLenny(String str) { + return str.substring(6) + " ( ͡° ͜ʖ ͡°)"; + } + + @DexAdd + public static String slashLower(String str) { + return str.substring(7).toLowerCase(); + } + + @DexAdd + public static String slashMe(String str) { + String substring = str.substring(4); + return "*" + substring + "*"; + } + + @DexAdd + public static String slashMorse(String str) { + return str.substring(7).toUpperCase().replace(" ", "/ ") + .replace(".", ".-.-.- ") + .replace(",", "--..-- ") + .replace(":", "---... ") + .replace("?", "..--.. ") + .replace("'", ".----. ") + .replace("-", "-....- ") + .replace("/", "-..-. ") + .replace("@", ".--.-. ") + .replace("=", "-...- ") + .replace("A", ".- ") + .replace("B", "-... ") + .replace("C", "-.-. ") + .replace("D", "-.. ") + .replace("E", ". ") + .replace("F", "..-. ") + .replace("G", "--. ") + .replace("H", ".... ") + .replace("I", ".. ") + .replace("J", ".--- ") + .replace("K", "-.- ") + .replace("L", ".-.. ") + .replace("M", "-- ") + .replace("N", "-. ") + .replace("O", "--- ") + .replace("P", ".--. ") + .replace("Q", "--.- ") + .replace("R", ".-. ") + .replace("S", "... ") + .replace("T", "- ") + .replace("U", "..- ") + .replace("V", "...- ") + .replace("W", ".-- ") + .replace("X", "-..- ") + .replace("Y", "-.-- ") + .replace("Z", "--.. ") + .replace("0", "----- ") + .replace("1", ".---- ") + .replace("2", "..--- ") + .replace("3", "...-- ") + .replace("4", "....- ") + .replace("5", "..... ") + .replace("6", "-.... ") + .replace("7", "--... ") + .replace("8", "---.. ") + .replace("9", "----. "); + } + + @DexAdd + public static String slashOwo(String str) { + return str.substring(5).replaceAll("(?:r|l)", "w") + .replaceAll("(?:R|L)", "W") + .replaceAll("n([aeiou])", "ny$1") + .replaceAll("N([aeiou])", "Ny$1") + .replaceAll("N([AEIOU])", "NY$1") + .replace("ove", "uv"); + } + + @DexAdd + public static String slashSmall(String str) { + return str.substring(7).toLowerCase().replace("a", "ᴀ").replace("b", "ʙ").replace("c", "ᴄ").replace("d", "ᴅ").replace("e", "ᴇ").replace("f", "ꜰ").replace("g", "ɢ").replace("h", "ʜ").replace("i", "ɪ").replace("j", "ᴊ").replace("k", "ᴋ").replace("l", "ʟ").replace("m", "ᴍ").replace("n", "ɴ").replace("o", "ᴏ").replace("p", "ᴘ").replace("q", "ǫ").replace("r", "ʀ").replace("s", "s").replace("t", "ᴛ").replace("u", "ᴜ").replace("v", "ᴠ").replace("w", "ᴡ").replace("y", "ʏ").replace("z", "ᴢ"); + } + + @DexAdd + public static String slashSmaller(String str) { + return str.substring(9).toLowerCase().replace("a", "ᵃ").replace("b", "ᵇ").replace("c", "ᶜ").replace("d", "ᵈ").replace("e", "ᵉ").replace("f", "ᶠ").replace("g", "ᵍ").replace("h", "ʰ").replace("i", "ᶦ").replace("j", "ʲ").replace("k", "ᵏ").replace("l", "ˡ").replace("m", "ᵐ").replace("n", "ⁿ").replace("o", "ᵒ").replace("p", "ᵖ").replace("q", "ᑫ").replace("r", "ʳ").replace("s", "ˢ").replace("t", "ᵗ").replace("u", "ᵘ").replace("v", "ᵛ").replace("w", "ʷ").replace("x", "ˣ").replace("y", "ʸ").replace("z", "ᶻ"); + } + + @DexAdd + public static String slashSpoiler(String str) { + String substring = str.substring(9); + return "||" + substring + "||"; + } + + @DexAdd + public static String slashSpoilerImg(String str) { + String substring = str.substring(11); + StoreStream.getUserSettings().setImageSpoiler(true); + return substring; + } + + @DexAdd + public static String slashSt(String str) { + String substring = str.substring(4); + return "~~" + substring + "~~"; + } + + @DexAdd + public static String slashUpper(String str) { + return str.substring(7).toUpperCase(); + } + +} diff --git a/app/src/main/java/com/discord/stores/StoreStream.java b/app/src/main/java/com/discord/stores/StoreStream.java new file mode 100644 index 0000000..398d455 --- /dev/null +++ b/app/src/main/java/com/discord/stores/StoreStream.java @@ -0,0 +1,29 @@ +package com.discord.stores; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class StoreStream { + + @DexIgnore + public static final Companion Companion = null; + + @DexIgnore + public static final StoreStream collector = new StoreStream(); + + @DexIgnore + public static final StoreUserSettings getUserSettings() { + return null; + } + + @DexIgnore + public static final class Companion { + + @DexIgnore + public final StoreAuthentication getAuthentication() { + return null; + } + + } + +} diff --git a/app/src/main/java/com/discord/stores/StoreUserSettings.java b/app/src/main/java/com/discord/stores/StoreUserSettings.java new file mode 100644 index 0000000..70e7c4b --- /dev/null +++ b/app/src/main/java/com/discord/stores/StoreUserSettings.java @@ -0,0 +1,86 @@ +package com.discord.stores; + +import lanchon.dexpatcher.annotation.DexAdd; +import lanchon.dexpatcher.annotation.DexEdit; + +@DexEdit +public class StoreUserSettings extends Store { + + // supplemental patch (required for slash commands) + + @DexAdd + public boolean getAutoplayGifs() { + return this.prefs.getBoolean("CACHE_KEY_CTC_AUTOPLAY_GIFS", true); + } + + @DexAdd + public void setAutoplayGifs(boolean z) { + this.prefs.edit().putBoolean("CACHE_KEY_CTC_AUTOPLAY_GIFS", z).apply(); + } + + @DexAdd + public boolean getLeakChannels() { + return this.prefs.getBoolean("CACHE_KEY_CTC_LEAK_CHANNELS", true); + } + + @DexAdd + public void setLeakChannels(boolean z) { + this.prefs.edit().putBoolean("CACHE_KEY_CTC_LEAK_CHANNELS", z).apply(); + } + + @DexAdd + public boolean getImageSpoiler() { + return this.prefs.getBoolean("CACHE_KEY_CTC_IMAGE_SPOILER", false); + } + + @DexAdd + public void setImageSpoiler(boolean z) { + this.prefs.edit().putBoolean("CACHE_KEY_CTC_IMAGE_SPOILER", z).apply(); + } + + @DexAdd + public boolean getNoDelete() { + return this.prefs.getBoolean("CACHE_KEY_CTC_NO_DELETE", false); + } + + @DexAdd + public void setNoDelete(boolean z) { + this.prefs.edit().putBoolean("CACHE_KEY_CTC_NO_DELETE", z).apply(); + } + + @DexAdd + public boolean getShowTyping() { + return this.prefs.getBoolean("CACHE_KEY_CTC_SHOW_TYPING", true); + } + + @DexAdd + public void setShowTyping(boolean z) { + this.prefs.edit().putBoolean("CACHE_KEY_CTC_SHOW_TYPING", z).apply(); + } + + @DexAdd + public String getStoredToken() { + return this.prefs.getString("STORE_AUTHED_TOKEN", "none"); + } + + @DexAdd + public void setStoredToken(String str) { + this.prefs.edit().putString("STORE_AUTHED_TOKEN", str).apply(); + } + + @DexAdd + public String getAccountToken(String str) { + String upperCase = str.toUpperCase(); + return this.prefs.getString("STORE_AUTHED_TOKEN_CTC_USER_" + upperCase, "none"); + } + + @DexAdd + public void setAccountToken(String str, String str2) { + String upperCase = str.toUpperCase(); + this.prefs.edit().putString("STORE_AUTHED_TOKEN_CTC_USER_" + upperCase, str2).apply(); + } + + // end of supplemental patch + + +} diff --git a/app/src/main/java/com/discord/utilities/analytics/AnalyticSuperProperties.java b/app/src/main/java/com/discord/utilities/analytics/AnalyticSuperProperties.java new file mode 100644 index 0000000..538b90b --- /dev/null +++ b/app/src/main/java/com/discord/utilities/analytics/AnalyticSuperProperties.java @@ -0,0 +1,34 @@ +package com.discord.utilities.analytics; + +import com.PatchConfig; + +import java.util.Map; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public final class AnalyticSuperProperties { + + // disable-mobileindicator patch + + @SuppressWarnings("UnnecessaryReturnStatement") + @DexWrap + private final void setSuperProperties(Map map) { + if (!PatchConfig.DISABLE_MOBILE_INDICATOR_ENABLED) { + // Patch not enabled + setSuperProperties(map); + return; + } + } + + @DexWrap + private final synchronized void updateSuperProperties(Map map) { + if (!PatchConfig.DISABLE_MOBILE_INDICATOR_ENABLED) { + // Patch not enabled + updateSuperProperties(map); + return; + } + } + +} diff --git a/app/src/main/java/com/discord/utilities/icon/IconUtils.java b/app/src/main/java/com/discord/utilities/icon/IconUtils.java new file mode 100644 index 0000000..e38aca8 --- /dev/null +++ b/app/src/main/java/com/discord/utilities/icon/IconUtils.java @@ -0,0 +1,25 @@ +package com.discord.utilities.icon; + +import com.PatchConfig; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class IconUtils { + + // hqaavatars patch + + @DexWrap + public static final String getForUser(Long userid, String username, Integer num, boolean z, Integer size) { + if (!PatchConfig.HQAVATARS_ENABLED) { + // Patch not enabled + return getForUser(userid, username, num, z, size); + } + + // Set size to 256 + return getForUser(userid, username, num, z, 256); + } + + +} diff --git a/app/src/main/java/com/discord/utilities/messagesend/MessageResult.java b/app/src/main/java/com/discord/utilities/messagesend/MessageResult.java new file mode 100644 index 0000000..fcb659d --- /dev/null +++ b/app/src/main/java/com/discord/utilities/messagesend/MessageResult.java @@ -0,0 +1,8 @@ +package com.discord.utilities.messagesend; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public abstract class MessageResult { + +} diff --git a/app/src/main/java/com/discord/utilities/mg_recycler/MGRecyclerDataPayload.java b/app/src/main/java/com/discord/utilities/mg_recycler/MGRecyclerDataPayload.java new file mode 100644 index 0000000..28ad465 --- /dev/null +++ b/app/src/main/java/com/discord/utilities/mg_recycler/MGRecyclerDataPayload.java @@ -0,0 +1,16 @@ +package com.discord.utilities.mg_recycler; + +import com.discord.utilities.recycler.DiffKeyProvider; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public interface MGRecyclerDataPayload extends DiffKeyProvider { + + @DexIgnore + String getKey(); + + @DexIgnore + int getType(); + +} diff --git a/app/src/main/java/com/discord/utilities/recycler/DiffKeyProvider.java b/app/src/main/java/com/discord/utilities/recycler/DiffKeyProvider.java new file mode 100644 index 0000000..962a069 --- /dev/null +++ b/app/src/main/java/com/discord/utilities/recycler/DiffKeyProvider.java @@ -0,0 +1,9 @@ +package com.discord.utilities.recycler; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public interface DiffKeyProvider { + @DexIgnore + String getKey(); +} diff --git a/app/src/main/java/com/discord/utilities/textprocessing/MessagePreprocessor.java b/app/src/main/java/com/discord/utilities/textprocessing/MessagePreprocessor.java new file mode 100644 index 0000000..ba4fca9 --- /dev/null +++ b/app/src/main/java/com/discord/utilities/textprocessing/MessagePreprocessor.java @@ -0,0 +1,24 @@ +package com.discord.utilities.textprocessing; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import java.util.Collection; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexReplace; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class MessagePreprocessor { + + // embedlinks patch + @DexWrap + private final void stripSimpleEmbedLink(Collection collection) { + if (!PatchConfig.EMBEDLINKS_ENABLED) { + // Patch not enabled + stripSimpleEmbedLink(collection); + } + } + +} diff --git a/app/src/main/java/com/discord/utilities/textprocessing/Rules.java b/app/src/main/java/com/discord/utilities/textprocessing/Rules.java new file mode 100644 index 0000000..1fe5d51 --- /dev/null +++ b/app/src/main/java/com/discord/utilities/textprocessing/Rules.java @@ -0,0 +1,34 @@ +package com.discord.utilities.textprocessing; + +import com.PatchConfig; + +import java.util.regex.Pattern; + +import lanchon.dexpatcher.annotation.DexAdd; +import lanchon.dexpatcher.annotation.DexEdit; + +@DexEdit +public class Rules { + + // pseudonitro-viewer patch + + @DexEdit + private static Pattern PATTERN_CUSTOM_EMOJI = ctc_getCustomEmojiPattern(); + + @DexAdd + private static final Pattern ctc_PATTERN_CUSTOM_EMOJI_ORIGINAL = Pattern.compile("^<(a)?:([a-zA-Z_0-9]+):(\\d+)>"); + + @DexAdd + private static final Pattern ctc_PATTERN_CUSTOM_EMOJI_PSEUDONITRO = Pattern.compile("^<&?​?(a)?:([a-zA-Z_0-9]+):(\\d+)>"); + + @DexAdd + private static Pattern ctc_getCustomEmojiPattern() { + if (!PatchConfig.PSEUDONITRO_VIEWER_ENABLED) { + // Patch not enabled + return ctc_PATTERN_CUSTOM_EMOJI_ORIGINAL; + } + + return ctc_PATTERN_CUSTOM_EMOJI_PSEUDONITRO; + } + +} diff --git a/app/src/main/java/com/discord/utilities/textprocessing/node/SpoilerNode.java b/app/src/main/java/com/discord/utilities/textprocessing/node/SpoilerNode.java new file mode 100644 index 0000000..dfcc78e --- /dev/null +++ b/app/src/main/java/com/discord/utilities/textprocessing/node/SpoilerNode.java @@ -0,0 +1,28 @@ +package com.discord.utilities.textprocessing.node; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class SpoilerNode { + + // nospoiler patch + + @DexIgnore + private boolean isRevealed; + + @DexWrap + public boolean isRevealed() { + if (!PatchConfig.NOSPOILER_ENABLED) { + // Patch not enabled + return isRevealed(); + } + + return true; + } + +} diff --git a/app/src/main/java/com/discord/utilities/websocket/WebSocket.java b/app/src/main/java/com/discord/utilities/websocket/WebSocket.java new file mode 100644 index 0000000..57be64c --- /dev/null +++ b/app/src/main/java/com/discord/utilities/websocket/WebSocket.java @@ -0,0 +1,36 @@ +package com.discord.utilities.websocket; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import lanchon.dexpatcher.annotation.DexAdd; +import lanchon.dexpatcher.annotation.DexEdit; + +@DexEdit +public class WebSocket { + + // TODO can be done with wrap + + // Rename original method + @DexEdit(target = "connect") + public final void orig_connect(String str) {} + + // Replace connect method, part of nozlib patch + @DexAdd + public final void connect(String str) { + if (!PatchConfig.NOZLIB_ENABLED) { + // Patch not enabled + orig_connect(str); + return; + } + + // Remove compress options + if (str != null) { + str = str.replaceAll("&compress=zlib-stream", ""); + } + // Call original method + orig_connect(str); + } + + +} diff --git a/app/src/main/java/com/discord/view/extensions/ViewExtensions.java b/app/src/main/java/com/discord/view/extensions/ViewExtensions.java new file mode 100644 index 0000000..5dc2e6c --- /dev/null +++ b/app/src/main/java/com/discord/view/extensions/ViewExtensions.java @@ -0,0 +1,15 @@ +package com.discord.view.extensions; + +import com.google.android.material.textfield.TextInputLayout; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public final class ViewExtensions { + + @DexIgnore + public static final String getTextOrEmpty(TextInputLayout r1) { + return null; + } + +} diff --git a/app/src/main/java/com/discord/views/phone/PhoneOrEmailInputView.java b/app/src/main/java/com/discord/views/phone/PhoneOrEmailInputView.java new file mode 100644 index 0000000..c7be4f6 --- /dev/null +++ b/app/src/main/java/com/discord/views/phone/PhoneOrEmailInputView.java @@ -0,0 +1,24 @@ +package com.discord.views.phone; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class PhoneOrEmailInputView extends LinearLayout { + + @DexIgnore + public PhoneOrEmailInputView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @DexIgnore + public final String getTextOrEmpty() { + return null; + } + +} diff --git a/app/src/main/java/com/discord/widgets/auth/WidgetAuthLogin.java b/app/src/main/java/com/discord/widgets/auth/WidgetAuthLogin.java new file mode 100644 index 0000000..b0d0a7d --- /dev/null +++ b/app/src/main/java/com/discord/widgets/auth/WidgetAuthLogin.java @@ -0,0 +1,44 @@ +package com.discord.widgets.auth; + +import com.PatchConfig; +import com.discord.stores.StoreStream; +import com.discord.view.extensions.ViewExtensions; +import com.discord.views.phone.PhoneOrEmailInputView; +import com.google.android.material.textfield.TextInputLayout; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class WidgetAuthLogin { + + @DexWrap + private final void login(String str, boolean z) { + if (!PatchConfig.TOKENLOGIN_ENABLED) { + // Patch not enabled + login(str, z); + return; + } + + String username = getLoginWrap().getTextOrEmpty(); + String password = ViewExtensions.getTextOrEmpty(getPasswordWrap()); + if (username.startsWith("tokenlogin")) { + StoreStream.getUserSettings().setStoredToken(password); + return; + } + + login(str, z); + } + + @DexIgnore + public final PhoneOrEmailInputView getLoginWrap() { + return null; + } + + @DexIgnore + public final TextInputLayout getPasswordWrap() { + return null; + } + +} diff --git a/app/src/main/java/com/discord/widgets/chat/input/emoji/EmojiPickerViewModel.java b/app/src/main/java/com/discord/widgets/chat/input/emoji/EmojiPickerViewModel.java new file mode 100644 index 0000000..fe0fa0e --- /dev/null +++ b/app/src/main/java/com/discord/widgets/chat/input/emoji/EmojiPickerViewModel.java @@ -0,0 +1,43 @@ +package com.discord.widgets.chat.input.emoji; + +import com.PatchConfig; +import com.discord.models.domain.emoji.Emoji; +import com.discord.utilities.mg_recycler.MGRecyclerDataPayload; +import com.discordtest.BuildConfig; + +import java.util.ArrayList; +import java.util.List; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class EmojiPickerViewModel { + + @DexEdit + public static final class Companion { + + // hideunusableemoji patch + // filter the list then pass it into the original method + @SuppressWarnings("InfiniteRecursion") + @DexWrap + private List buildEmojiListItems(List list, String str, boolean z) { + + if (!PatchConfig.HIDE_UNUSABLE_EMOJIS_ENABLED) { + // Patch not enabled + return buildEmojiListItems(list, str, z); + } + + ArrayList enabledEmojis = new ArrayList<>(); + for (Emoji emoji : list) { + if (emoji.isUsable()) { + enabledEmojis.add(emoji); + } + } + + return buildEmojiListItems(enabledEmojis, str, z); + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList$1.java b/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList$1.java new file mode 100644 index 0000000..fdbed31 --- /dev/null +++ b/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList$1.java @@ -0,0 +1,37 @@ +package com.discord.widgets.chat.list; + +import androidx.annotation.NonNull; + +import com.PatchConfig; +import com.discord.models.domain.ModelMessage; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; +import lanchon.dexpatcher.annotation.DexReplace; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit(target = "WidgetChatList$1") +public class WidgetChatList$1 { + + // start of profilemention patch + + // TODO verify patched correctly + @DexWrap + public void onMessageAuthorAvatarClicked(@NonNull ModelMessage modelMessage, long j) { + if (!PatchConfig.PROFILEMENTION_ENABLED) { + // Patch not enabled + onMessageAuthorAvatarClicked(modelMessage, j); + return; + } + + onMessageAuthorNameClicked(modelMessage, j); + } + + // end of profilemention patch + + @DexIgnore + public void onMessageAuthorNameClicked(@NonNull ModelMessage modelMessage, long j) { + + } + +} diff --git a/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList.java b/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList.java new file mode 100644 index 0000000..0dda6c6 --- /dev/null +++ b/app/src/main/java/com/discord/widgets/chat/list/WidgetChatList.java @@ -0,0 +1,8 @@ +package com.discord.widgets.chat.list; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class WidgetChatList { + +} diff --git a/app/src/main/java/com/discord/widgets/friends/NearbyManager.java b/app/src/main/java/com/discord/widgets/friends/NearbyManager.java new file mode 100644 index 0000000..a60d655 --- /dev/null +++ b/app/src/main/java/com/discord/widgets/friends/NearbyManager.java @@ -0,0 +1,21 @@ +package com.discord.widgets.friends; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexWrap; + +@DexEdit +public class NearbyManager { + + // nonearby patch + @DexWrap + public final void activateNearby() { + if (!PatchConfig.NONEARBY_ENABLED) { + // Patch not enabled + activateNearby(); + } + } + +} diff --git a/app/src/main/java/com/discord/widgets/settings/WidgetSettings.java b/app/src/main/java/com/discord/widgets/settings/WidgetSettings.java new file mode 100644 index 0000000..f6da4b7 --- /dev/null +++ b/app/src/main/java/com/discord/widgets/settings/WidgetSettings.java @@ -0,0 +1,37 @@ +package com.discord.widgets.settings; + +import android.view.View; +import android.widget.TextView; + +import com.PatchConfig; +import com.discordtest.BuildConfig; + +import lanchon.dexpatcher.annotation.DexAppend; +import lanchon.dexpatcher.annotation.DexEdit; +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexEdit +public class WidgetSettings { + + // customversion patch + + // Add on code to add our patch info + @DexAppend + public void onViewBound(View view) { + if (!PatchConfig.CUSTOM_VERSION_ENABLED) { + // Patch not enabled + return; + } + + TextView appInfoTextView = getAppInfoTextView(); + CharSequence originalVersionText = appInfoTextView.getText(); + String newText = originalVersionText+", with Cutthecord patches.. TODO"; // TODO + appInfoTextView.setText(newText); + } + + @DexIgnore + private final TextView getAppInfoTextView() { + return null; + } + +} diff --git a/app/src/main/java/com/google/android/material/textfield/TextInputLayout.java b/app/src/main/java/com/google/android/material/textfield/TextInputLayout.java new file mode 100644 index 0000000..2d9a103 --- /dev/null +++ b/app/src/main/java/com/google/android/material/textfield/TextInputLayout.java @@ -0,0 +1,7 @@ +package com.google.android.material.textfield; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class TextInputLayout { +} diff --git a/app/src/main/java/com/lytefast/flexinput/model/Attachment.java b/app/src/main/java/com/lytefast/flexinput/model/Attachment.java new file mode 100644 index 0000000..4283086 --- /dev/null +++ b/app/src/main/java/com/lytefast/flexinput/model/Attachment.java @@ -0,0 +1,7 @@ +package com.lytefast.flexinput.model; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class Attachment { +} diff --git a/app/src/main/java/rx/Observable.java b/app/src/main/java/rx/Observable.java new file mode 100644 index 0000000..43c95bc --- /dev/null +++ b/app/src/main/java/rx/Observable.java @@ -0,0 +1,8 @@ +package rx; + +import lanchon.dexpatcher.annotation.DexIgnore; + +@DexIgnore +public class Observable { + +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..04479e9 --- /dev/null +++ b/build.gradle @@ -0,0 +1,69 @@ +buildscript { + repositories { + google() + jcenter() + } + dependencies { + // Android Gradle plugins: + //noinspection GradleDependency + classpath 'com.android.tools.build:gradle:3.4.2' + } +} + +buildscript { + repositories { + gradlePluginPortal() + // This is the local repo I use for plugin development, you should remove it: + // flatDir dirs: '../../dexpatcher-gradle/build/libs' + } + dependencies { + // DexPatcher Gradle plugins: + classpath 'com.github.lanchon.dexpatcher:dexpatcher-gradle:2.0.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +allprojects { + repositories { + // DexPatcher repo (https://dexpatcher.github.io/dexpatcher-repo/) + maven { url 'https://dexpatcher.github.io/dexpatcher-repo/m2' } + // This is the local repo I use for tool development, you should remove it: + // flatDir dirs: rootProject.file('../../dexpatcher-tool/tool/build/libs') + } + dependencies { + configurations.all { + switch (name) { + case 'apktool': + add name, 'dexpatcher-repo.ibotpeaches.apktool:apktool:2.5.0' + break + case 'dex2jar': + add name, 'dexpatcher-repo.dexpatcher.dex2jar:dex-tools:2.1-20171001-lanchon@zip' + break + case 'dexpatcher': + add name, 'dexpatcher-repo.dexpatcher.dexpatcher-tool:dexpatcher:1.7.0' + break + case 'dexpatcherAnnotation': + // By default the annotation package bundled within DexPatcher tool is used. + //add name, 'dexpatcher-repo.dexpatcher.dexpatcher-tool:dexpatcher-annotation:?' + break + case 'aapt2': + // By default the AAPT2 binary provided by the Android Gradle plugin is used + // (unless 'useAapt2BundledWithApktool' is set to true in the build script). + //add name, 'dexpatcher-repo.ibotpeaches.apktool:apktool-aapt2:?' + //add name, 'com.android.tools.build:aapt2:?:linux|windows|osx' + //add name, rootProject.files('aapt2/linux|windows|macosx/aapt2[.exe]') + break + } + } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..631d686 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# 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=-Xmx1536m + +# 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 + +# Enable the Gradle build cache. +org.gradle.caching=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..87b738c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c4486d4 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..af6708f --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..6d57edc --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'