Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Rebrand to Revenge #2

Merged
merged 7 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,29 @@ jobs:

- name: "Setup directories"
run: |
mkdir -p /tmp/pyonbuild/apks
mkdir -p /tmp/pyonbuild/tools
mkdir -p /tmp/build/apks
mkdir -p /tmp/build/tools

- name: "Move debug APK"
run: mv ./app/build/outputs/apk/debug/app-debug.apk /tmp/pyonbuild/apks
run: mv ./app/build/outputs/apk/debug/app-debug.apk /tmp/build/apks

- name: "Download uber-apk-signer"
run: wget -nv "https://github.com/patrickfav/uber-apk-signer/releases/download/v1.2.1/uber-apk-signer-1.2.1.jar" -O /tmp/pyonbuild/tools/uber-apk-signer.jar
run: wget -nv "https://github.com/patrickfav/uber-apk-signer/releases/download/v1.2.1/uber-apk-signer-1.2.1.jar" -O /tmp/build/tools/uber-apk-signer.jar

- name: "Sign release APK"
run: java -jar /tmp/pyonbuild/tools/uber-apk-signer.jar --apks ./app/build/outputs/apk/release/app-release-unsigned.apk --out /tmp/pyonbuild/apks/
run: java -jar /tmp/build/tools/uber-apk-signer.jar --apks ./app/build/outputs/apk/release/app-release-unsigned.apk --out /tmp/build/apks/

- name: "Rename release APK"
run: mv /tmp/pyonbuild/apks/app-release-aligned-debugSigned.apk /tmp/pyonbuild/apks/app-release.apk
run: mv /tmp/build/apks/app-release-aligned-debugSigned.apk /tmp/build/apks/app-release.apk

- name: "Upload debug APK"
uses: actions/upload-artifact@v3
with:
name: app-debug
path: /tmp/pyonbuild/apks/app-debug.apk
path: /tmp/build/apks/app-debug.apk

- name: "Upload release APK"
uses: actions/upload-artifact@v3
with:
name: app-release
path: /tmp/pyonbuild/apks/app-release.apk
path: /tmp/build/apks/app-release.apk
2 changes: 1 addition & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ plugins {
}

android {
namespace = "io.github.pyoncord.xposed"
namespace = "io.github.revenge.xposed"
compileSdk = 33

defaultConfig {
applicationId = "io.github.pyoncord.xposed"
applicationId = "io.github.revenge.xposed"
minSdk = 24
targetSdk = 33
versionCode = 203
versionName = "0.2.3"
versionCode = 204
versionName = "0.2.4"
}

buildTypes {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="An Xposed module to inject Bunny, a mod for Discord's mobile apps." />
android:value="An Xposed module to inject Revenge, a mod for Discord's mobile apps." />
<meta-data
android:name="xposedminversion"
android:value="82" />
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/assets/xposed_init
Original file line number Diff line number Diff line change
@@ -1 +1 @@
io.github.pyoncord.xposed.Main
io.github.revenge.xposed.Main
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
// credits to janisslsm from his PR: https://github.com/vendetta-mod/VendettaXposed/pull/17
// hooks are modified function from RN codebase

package io.github.pyoncord.xposed
package io.github.revenge.xposed

import android.content.res.AssetManager
import android.os.Build
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.Typeface.CustomFallbackBuilder
import android.graphics.fonts.Font
import android.graphics.fonts.FontFamily
import android.util.Log
import android.webkit.URLUtil
import de.robv.android.xposed.XC_MethodReplacement
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.*
import java.io.IOException
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.coroutines.*

import io.ktor.client.*
Expand All @@ -41,7 +36,7 @@ data class FontDefinition(
val main: Map<String, String>,
)

class FontsModule: PyonModule() {
class FontsModule: Module() {
private val EXTENSIONS = arrayOf("", "_bold", "_italic", "_bold_italic")
private val FILE_EXTENSIONS = arrayOf(".ttf", ".otf")
private val FONTS_ASSET_PATH = "fonts/"
Expand All @@ -67,7 +62,7 @@ class FontsModule: PyonModule() {
val assetManager: AssetManager = param.args[2] as AssetManager
return createAssetTypeface(fontFamilyName, style, assetManager)
}
});
})

val fontDefFile = File(appInfo.dataDir, "files/pyoncord/fonts.json")
if (!fontDefFile.exists()) return@with
Expand All @@ -85,7 +80,7 @@ class FontsModule: PyonModule() {
if (!fileName.startsWith(".")) {
val fontName = fileName.split('.')[0]
if (fontDef.main.keys.none { it == fontName }) {
Log.i("Bunny", "Deleting font file: $fileName")
Log.i("Revenge", "Deleting font file: $fileName")
file.delete()
}
}
Expand All @@ -97,12 +92,12 @@ class FontsModule: PyonModule() {
async {
val url = fontDef.main.getValue(name)
try {
Log.i("Bunny", "Downloading $name from $url")
Log.i("Revenge", "Downloading $name from $url")
val file = File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}")
if (file.exists()) return@async

val client = HttpClient(CIO) {
install(UserAgent) { agent = "BunnyXposed" }
install(UserAgent) { agent = "RevengeXposed" }
}

val response: HttpResponse = client.get(url)
Expand All @@ -113,7 +108,7 @@ class FontsModule: PyonModule() {

return@async
} catch (e: Throwable) {
Log.e("Bunny", "Failed to download fonts ($name from $url)", e)
Log.e("Revenge", "Failed to download fonts ($name from $url)", e)
}
}
}.awaitAll()
Expand Down Expand Up @@ -144,7 +139,7 @@ class FontsModule: PyonModule() {
// ignore
}

for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filter { it != null }) {
for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filterNotNull()) {
for (fileExtension in FILE_EXTENSIONS) {
val fileName = java.lang.StringBuilder()
.append(fontRootPath)
Expand Down Expand Up @@ -220,7 +215,7 @@ class FontsModule: PyonModule() {

// Lastly, after all those checks above, this is the original RN logic for
// getting the typeface.
for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filter { it != null }) {
for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filterNotNull()) {
for (fileExtension in FILE_EXTENSIONS) {
val fileName = java.lang.StringBuilder()
.append(fontRootPath)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.github.pyoncord.xposed
package io.github.revenge.xposed

import android.app.Activity
import android.app.AndroidAppHelper
import android.content.Context
import android.app.Activity
import android.content.res.AssetManager
import android.content.res.Resources
import android.util.Log
Expand All @@ -12,7 +10,7 @@ import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage
import io.github.pyoncord.xposed.BuildConfig
import io.github.revenge.xposed.BuildConfig
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
Expand All @@ -36,7 +34,7 @@ data class LoaderConfig(
)

class Main : IXposedHookLoadPackage {
private val pyonModules: Array<PyonModule> = arrayOf(
private val modules: Array<Module> = arrayOf(
ThemeModule(),
SysColorsModule(),
FontsModule(),
Expand All @@ -47,7 +45,7 @@ class Main : IXposedHookLoadPackage {
put("loaderName", "BunnyXposed")
put("loaderVersion", BuildConfig.VERSION_NAME)

for (module in pyonModules) {
for (module in modules) {
module.buildJson(this)
}
}
Expand All @@ -56,24 +54,34 @@ class Main : IXposedHookLoadPackage {
}

override fun handleLoadPackage(param: XC_LoadPackage.LoadPackageParam) = with (param) {
// val reactActivity = runCatching {
// lpparam.classLoader.loadClass("com.discord.react_activities.ReactActivity")
// }.getOrElse { return } // Package is not our the target app, return
val reactActivity = runCatching {
classLoader.loadClass("com.discord.react_activities.ReactActivity")
}.getOrElse { return@with } // Package is not our the target app, return

// XposedBridge.hookMethod(reactActivity.getDeclaredMethod("onCreate", Bundle::class.java), object : XC_MethodHook() {
// override fun beforeHookedMethod(param: MethodHookParam) {
// init(lpparam, param.thisObject as Activity)
// }
// })
// }
var activity: Activity? = null;
val onActivityCreateCallback = mutableSetOf<(activity: Activity) -> Unit>()

// fun init(param: XC_LoadPackage.LoadPackageParam, activity: Activity) = with (param) {
// val catalystInstanceImpl = classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")
val catalystInstanceImpl = runCatching {
classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")
}.getOrElse { return@with }
XposedBridge.hookMethod(reactActivity.getDeclaredMethod("onCreate", Bundle::class.java), object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam) {
activity = param.thisObject as Activity;
onActivityCreateCallback.forEach { cb -> cb(activity!!) }
onActivityCreateCallback.clear()
}
})

init(param) { cb ->
if (activity != null) cb(activity!!)
else onActivityCreateCallback.add(cb)
}
}

private fun init(
param: XC_LoadPackage.LoadPackageParam,
onActivityCreate: ((activity: Activity) -> Unit) -> Unit
) = with (param) {
val catalystInstanceImpl = classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")

for (module in pyonModules) module.onInit(param)
for (module in modules) module.onInit(param)

val loadScriptFromAssets = catalystInstanceImpl.getDeclaredMethod(
"loadScriptFromAssets",
Expand Down Expand Up @@ -124,14 +132,14 @@ class Main : IXposedHookLoadPackage {
install(HttpTimeout) {
requestTimeoutMillis = if (bundle.exists()) 3000 else 10000
}
install(UserAgent) { agent = "BunnyXposed" }
install(UserAgent) { agent = "RevengeXposed" }
}

val url =
if (config.customLoadUrl.enabled) config.customLoadUrl.url
else "https://github.com/revenge-mod/Revenge/releases/latest/download/revenge.js"
else "https://github.com/revenge-mod/revenge-bundle/releases/latest/download/revenge.min.js"

Log.e("Bunny", "Fetching JS bundle from $url")
Log.e("Revenge", "Fetching JS bundle from $url")

val response: HttpResponse = client.get(url) {
headers {
Expand All @@ -153,13 +161,19 @@ class Main : IXposedHookLoadPackage {
return@async
} catch (e: RedirectResponseException) {
if (e.response.status != HttpStatusCode.NotModified) throw e;
Log.e("Bunny", "Server reponded with status code 304 - no changes to file")
Log.e("Revenge", "Server responded with status code 304 - no changes to file")
} catch (e: Throwable) {
// activity.runOnUiThread {
// Toast.makeText(activity.applicationContext, "Failed to fetch JS bundle, Bunny may not load!", Toast.LENGTH_SHORT).show()
// }
onActivityCreate { activity ->
activity.runOnUiThread {
Toast.makeText(
activity.applicationContext,
"Failed to fetch JS bundle, Revenge may not load!",
Toast.LENGTH_SHORT
).show()
}
}

Log.e("Bunny", "Failed to download bundle", e)
Log.e("Revenge", "Failed to download bundle", e)
}
}

Expand Down Expand Up @@ -211,4 +225,4 @@ class Main : IXposedHookLoadPackage {
})
}
}
}
}
Loading
Loading