feat: change to support Revenge

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Palm <git@palmdevs.me>
This commit is contained in:
Cristiandis 2024-09-14 23:09:09 +02:00 • committed by GitHub
parent c6e9b31037
commit d21801c05e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 103 additions and 144 deletions

View file

@ -3,5 +3,6 @@
"MD029": false, "MD029": false,
"MD033": false, "MD033": false,
"MD045": false, "MD045": false,
} },
"deepscan.ignoreConfirmWarning": true
} }

View file

@ -7,13 +7,13 @@ endif
include $(THEOS)/makefiles/common.mk include $(THEOS)/makefiles/common.mk
TWEAK_NAME = BunnyTweak TWEAK_NAME = RevengeTweak
BunnyTweak_FILES = $(shell find Sources/BunnyTweak -name '*.swift') $(shell find Sources/BunnyTweakC -name '*.m' -o -name '*.c' -o -name '*.mm' -o -name '*.cpp') RevengeTweak_FILES = $(shell find Sources/RevengeTweak -name '*.swift') $(shell find Sources/RevengeTweakC -name '*.m' -o -name '*.c' -o -name '*.mm' -o -name '*.cpp')
BunnyTweak_SWIFTFLAGS = -ISources/BunnyTweakC/include RevengeTweak_SWIFTFLAGS = -ISources/RevengeTweakC/include
BunnyTweak_CFLAGS = -fobjc-arc -ISources/BunnyTweakC/include RevengeTweak_CFLAGS = -fobjc-arc -ISources/RevengeTweakC/include
BunnyTweak_BUNDLE_NAME = BunnyPatches RevengeTweak_BUNDLE_NAME = RevengePatches
BunnyTweak_BUNDLE_RESOURCE_DIRS = Resources RevengeTweak_BUNDLE_RESOURCE_DIRS = Resources
include $(THEOS_MAKE_PATH)/tweak.mk include $(THEOS_MAKE_PATH)/tweak.mk

View file

@ -65,23 +65,23 @@ let swiftFlags: [String] = libFlags + [
] ]
let package = Package( let package = Package(
name: "BunnyTweak", name: "RevengeTweak",
platforms: [.iOS(deploymentTarget)], platforms: [.iOS(deploymentTarget)],
products: [ products: [
.library( .library(
name: "BunnyTweak", name: "RevengeTweak",
targets: ["BunnyTweak"] targets: ["RevengeTweak"]
), ),
], ],
targets: [ targets: [
.target( .target(
name: "BunnyTweakC", name: "RevengeTweakC",
cSettings: [.unsafeFlags(cFlags)], cSettings: [.unsafeFlags(cFlags)],
cxxSettings: [.unsafeFlags(cxxFlags)] cxxSettings: [.unsafeFlags(cxxFlags)]
), ),
.target( .target(
name: "BunnyTweak", name: "RevengeTweak",
dependencies: ["BunnyTweakC"], dependencies: ["RevengeTweakC"],
swiftSettings: [.unsafeFlags(swiftFlags)] swiftSettings: [.unsafeFlags(swiftFlags)]
), ),
] ]

View file

@ -1,16 +1,16 @@
# BunnyTweak # RevengeTweak
Tweak to inject [Bunny](https://github.com/pyoncord/Bunny) into Discord. Forked [VendettaTweak](https://github.com/vendetta-mod/VendettaTweak), modified to match with [BunnyXposed](https://github.com/pyoncord/BunnyXposed) behavior. There are still slight differences between these two, and this tweak may be missing some loader features. Tweak to inject [Revenge](https://github.com/revenge-mod/Revenge) into Discord. Forked [BunnyTweak](https://github.com/pyoncord/BunnyTweak), modified to match with [RevengeXposed](https://github.com/revenge-mod/RevengeXposed) behavior. There are still slight differences between these two, and this tweak may be missing some loader features.
> [!NOTE] > [!NOTE]
> As of right now this tweak does not encompass some functionalities when running in a jailed environment with a wildcard certificate \ > As of right now this tweak does not encompass some functionalities when running in a jailed environment with a wildcard certificate \
> If you value these features sign the application with a local dev certificate: > If you value these features sign the application with a local dev certificate:
> - setAlternateAppIcon does not work, thus breaking dynamic app icons > - setAlternateAppIcon does not work, thus breaking dynamic app icons
> - sharing files to the application/selecting items via the Files app does not work > - sharing files to the application/selecting items via the Files app does not work
## Installation ## Installation
Builds can be found in the [Releases](https://github.com/pyoncord/BunnyTweak/releases/latest) tab. Builds can be found in the [Releases](https://github.com/revenge-mod/RevengeTweak/releases/latest) tab.
> [!NOTE] > [!NOTE]
> Raw decrypted IPAs which are used to provide prepatched IPAs are sourced from the [Enmity](https://github.com/enmity-mod/) community. These raw decrypted IPAs are also used throughout Enmity related projects such as [enmity-mod/tweak](https://github.com/enmity-mod/tweak/) and [acquitelol/rosiecord](https://github.com/acquitelol/rosiecord).\ > Raw decrypted IPAs which are used to provide prepatched IPAs are sourced from the [Enmity](https://github.com/enmity-mod/) community. These raw decrypted IPAs are also used throughout Enmity related projects such as [enmity-mod/tweak](https://github.com/enmity-mod/tweak/) and [acquitelol/rosiecord](https://github.com/acquitelol/rosiecord).\
@ -19,7 +19,7 @@ Builds can be found in the [Releases](https://github.com/pyoncord/BunnyTweak/rel
### Jailbroken ### Jailbroken
1. Install the Orion runtime via your preferred package manager, by adding `https://repo.chariz.com/` to your sources, then finding `Orion Runtime`. 1. Install the Orion runtime via your preferred package manager, by adding `https://repo.chariz.com/` to your sources, then finding `Orion Runtime`.
1. Install Bunny by downloading the appropriate Debian package (or by building your own, see [Building BunnyTweak locally](#building-bunnytweak-locally)) and adding it to your package manager. Use the file ending in `arm.deb` for rootful jailbreaks, and the file ending in `arm64.deb` for rootless jailbreaks. 1. Install Revenge by downloading the appropriate Debian package (or by building your own, see [Building RevengeTweak locally](#building-revengetweak-locally)) and adding it to your package manager. Use the file ending in `arm.deb` for rootful jailbreaks, and the file ending in `arm64.deb` for rootless jailbreaks.
### Jailed ### Jailed
@ -29,9 +29,9 @@ Builds can be found in the [Releases](https://github.com/pyoncord/BunnyTweak/rel
> [!NOTE] > [!NOTE]
> TrollStore may display an encryption warning, which you can disregard. > TrollStore may display an encryption warning, which you can disregard.
1. Download and install [Bunny.ipa](https://github.com/pyoncord/BunnyTweak/releases/latest/download/Bunny.ipa) using your preferred sideloading method. 1. Download and install [Revenge.ipa](https://github.com/revenge-mod/RevengeTweak/releases/latest/download/Revenge.ipa) using your preferred sideloading method.
## Building BunnyTweak locally ## Building RevengeTweak locally
> [!NOTE] > [!NOTE]
> These steps assume you use MacOS. > These steps assume you use MacOS.
@ -42,7 +42,7 @@ Builds can be found in the [Releases](https://github.com/pyoncord/BunnyTweak/rel
2. Install the required dependencies. You can do this by running `brew install make ldid` in your terminal. If you do not have brew installed, follow the instructions [here](https://brew.sh/). 2. Install the required dependencies. You can do this by running `brew install make ldid` in your terminal. If you do not have brew installed, follow the instructions [here](https://brew.sh/).
3. Setup your path accordingly. We recommend you run the following before running the next commands, as well as any time you want to build BunnyTweak. 3. Setup your path accordingly. We recommend you run the following before running the next commands, as well as any time you want to build RevengeTweak.
```bash ```bash
export PATH="$(brew --prefix make)/libexec/gnubin:$PATH" export PATH="$(brew --prefix make)/libexec/gnubin:$PATH"
@ -58,9 +58,9 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/theos/theos/master/bin/i
If you've already installed theos, you can run `$THEOS/bin/update-theos` to make sure it's up to date. If you've already installed theos, you can run `$THEOS/bin/update-theos` to make sure it's up to date.
5. Clone this repository with `git clone git@github.com:pyoncord/BunnyTweak.git` and `cd` into it. Replace the URL with your fork if you've forked this repository. 5. Clone this repository with `git clone git@github.com:revenge-mod/RevengeTweak.git` and `cd` into it. Replace the URL with your fork if you've forked this repository.
6. To build BunnyTweak, you can run `rm -rf packages && make clean && make package FINALPACKAGE=1 && make package FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless`. The first command will remove any previous packages, the second will clean the project, the third will build the rootful package (which is denoted by the `arm.deb` ending), and the fourth will build the rootless package (which is denoted by the `arm64.deb` ending). 6. To build RevengeTweak, you can run `rm -rf packages && make clean && make package FINALPACKAGE=1 && make package FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless`. The first command will remove any previous packages, the second will clean the project, the third will build the rootful package (which is denoted by the `arm.deb` ending), and the fourth will build the rootless package (which is denoted by the `arm64.deb` ending).
The first time you run this, it might take a bit longer, but subsequent builds should be much faster. The first time you run this, it might take a bit longer, but subsequent builds should be much faster.
@ -68,7 +68,7 @@ The resulting `.deb` files will be in the `packages` folder. As a reminder, `*ar
## Contributing ## Contributing
If you want to contribute, you will basically need to follow the steps for [Building BunnyTweak locally](#building-bunnytweak-locally), as well as run `make spm` for the Swift LSP to work. If you want to contribute, you will basically need to follow the steps for [Building RevengeTweak locally](#building-revengetweak-locally), as well as run `make spm` for the Swift LSP to work.
<!-- @vladdy was here, battling all these steps so you don't have to. Have fun! :3 --> <!-- @vladdy was here, battling all these steps so you don't have to. Have fun! :3 -->
<!-- @castdrian also was here simplifying these steps immensely --> <!-- @castdrian also was here simplifying these steps immensely -->

View file

@ -1,7 +1,7 @@
globalThis.__PYON_LOADER__ = { globalThis.__PYON_LOADER__ = {
loaderName: "BunnyTweak", loaderName: "RevengeTweak",
loaderVersion: "0.3.1", loaderVersion: "1.0.0",
hasThemeSupport: true, hasThemeSupport: true,
storedTheme: null, storedTheme: null,
fontPatch: 2 fontPatch: 2,
} };

View file

@ -1,4 +1,4 @@
import BunnyTweakC import RevengeTweakC
import Orion import Orion
import UIKit import UIKit
import os import os
@ -32,14 +32,14 @@ class FontsHook: ClassHook<UIFont> {
return orig.fontWithDescriptor(finalDescriptor, size: size) return orig.fontWithDescriptor(finalDescriptor, size: size)
} }
return orig.fontWithDescriptor(descriptor, size: size) return orig.fontWithDescriptor(descriptor, size: size)
} }
} }
func patchFonts(_ main: [String: String], fontDefName: String) { func patchFonts(_ main: [String: String], fontDefName: String) {
for (fontName, url) in main { for (fontName, url) in main {
os_log("Replacing font %{public}@ with URL: %{public}@", log: bunnyLog, type: .info, fontName, url) os_log("Replacing font %{public}@ with URL: %{public}@", log: revengeLog, type: .info, fontName, url)
let fontExtension = URL(string: url)!.pathExtension let fontExtension = URL(string: url)!.pathExtension
let fontCachePath = pyoncordDirectory let fontCachePath = pyoncordDirectory
@ -49,26 +49,26 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
.appendingPathComponent("\(fontName).\(fontExtension)") .appendingPathComponent("\(fontName).\(fontExtension)")
do { do {
os_log("Attempting to register font %{public}@ from %{public}@", log: bunnyLog, type: .info, fontName, url) os_log("Attempting to register font %{public}@ from %{public}@", log: revengeLog, type: .info, fontName, url)
let parent = fontCachePath.deletingLastPathComponent() let parent = fontCachePath.deletingLastPathComponent()
if !FileManager.default.fileExists(atPath: parent.path) { if !FileManager.default.fileExists(atPath: parent.path) {
os_log("Creating parent directory: %{public}@", log: bunnyLog, type: .debug, parent.path) os_log("Creating parent directory: %{public}@", log: revengeLog, type: .debug, parent.path)
try FileManager.default.createDirectory( try FileManager.default.createDirectory(
at: parent, withIntermediateDirectories: true, attributes: nil) at: parent, withIntermediateDirectories: true, attributes: nil)
} }
// JS side should download these already, but just in case... // JS side should download these already, but just in case...
if !FileManager.default.fileExists(atPath: fontCachePath.path) { if !FileManager.default.fileExists(atPath: fontCachePath.path) {
os_log("Downloading font %{public}@ from %{public}@", log: bunnyLog, type: .debug, fontName, url) os_log("Downloading font %{public}@ from %{public}@", log: revengeLog, type: .debug, fontName, url)
if let data = try? Data(contentsOf: URL(string: url)!) { if let data = try? Data(contentsOf: URL(string: url)!) {
os_log("Writing font data to: %{public}@", log: bunnyLog, type: .debug, fontCachePath.path) os_log("Writing font data to: %{public}@", log: revengeLog, type: .debug, fontCachePath.path)
try? data.write(to: fontCachePath) try? data.write(to: fontCachePath)
} }
} }
if let data = try? Data(contentsOf: fontCachePath) { if let data = try? Data(contentsOf: fontCachePath) {
os_log("Registering font %{public}@ with provider", log: bunnyLog, type: .debug, fontName) os_log("Registering font %{public}@ with provider", log: revengeLog, type: .debug, fontName)
let provider = CGDataProvider(data: data as CFData) let provider = CGDataProvider(data: data as CFData)
let font = CGFont(provider!) let font = CGFont(provider!)
var error: Unmanaged<CFError>? var error: Unmanaged<CFError>?
@ -77,7 +77,7 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
if let existingFont = CGFont(font!.postScriptName!) { if let existingFont = CGFont(font!.postScriptName!) {
var unregisterError: Unmanaged<CFError>? var unregisterError: Unmanaged<CFError>?
if !CTFontManagerUnregisterGraphicsFont(existingFont, &unregisterError) { if !CTFontManagerUnregisterGraphicsFont(existingFont, &unregisterError) {
os_log("Failed to deregister font %{public}@: %{public}@", log: bunnyLog, type: .error, os_log("Failed to deregister font %{public}@: %{public}@", log: revengeLog, type: .error,
font!.postScriptName! as String, font!.postScriptName! as String,
String(describing: unregisterError!.takeUnretainedValue())) String(describing: unregisterError!.takeUnretainedValue()))
} }
@ -85,15 +85,15 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
if CTFontManagerRegisterGraphicsFont(font!, &error) { if CTFontManagerRegisterGraphicsFont(font!, &error) {
fontMap[fontName] = font!.postScriptName! as String fontMap[fontName] = font!.postScriptName! as String
os_log("Successfully registered font %{public}@ to %{public}@", log: bunnyLog, type: .info, fontName, font!.postScriptName! as String) os_log("Successfully registered font %{public}@ to %{public}@", log: revengeLog, type: .info, fontName, font!.postScriptName! as String)
} else { } else {
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, String(describing: error!.takeUnretainedValue())) os_log("Failed to register font %{public}@: %{public}@", log: revengeLog, type: .error, fontName, String(describing: error!.takeUnretainedValue()))
} }
} else { } else {
os_log("Failed to read font data from: %{public}@", log: bunnyLog, type: .error, fontCachePath.path) os_log("Failed to read font data from: %{public}@", log: revengeLog, type: .error, fontCachePath.path)
} }
} catch { } catch {
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, error.localizedDescription) os_log("Failed to register font %{public}@: %{public}@", log: revengeLog, type: .error, fontName, error.localizedDescription)
} }
} }
} }

View file

@ -17,7 +17,7 @@ struct LoaderConfig: Codable {
let defaultLoaderConfig = LoaderConfig( let defaultLoaderConfig = LoaderConfig(
customLoadUrl: CustomLoadUrl( customLoadUrl: CustomLoadUrl(
enabled: false, enabled: false,
url: URL(string: "http://localhost:4040/bunny.js")! url: URL(string: "http://localhost:4040/revenge.js")!
) )
) )
@ -25,7 +25,7 @@ let pyoncordDirectory = getPyoncordDirectory()
let loaderConfigUrl = pyoncordDirectory.appendingPathComponent("loader.json") let loaderConfigUrl = pyoncordDirectory.appendingPathComponent("loader.json")
func getLoaderConfig() -> LoaderConfig { func getLoaderConfig() -> LoaderConfig {
os_log("Getting loader config", log: bunnyLog, type: .debug) os_log("Getting loader config", log: revengeLog, type: .debug)
let fileManager = FileManager.default let fileManager = FileManager.default
do { do {
@ -33,14 +33,14 @@ func getLoaderConfig() -> LoaderConfig {
let data = try Data(contentsOf: loaderConfigUrl) let data = try Data(contentsOf: loaderConfigUrl)
let loaderConfig = try JSONDecoder().decode(LoaderConfig.self, from: data) let loaderConfig = try JSONDecoder().decode(LoaderConfig.self, from: data)
os_log("Got loader config", log: bunnyLog, type: .debug) os_log("Got loader config", log: revengeLog, type: .debug)
return loaderConfig return loaderConfig
} else { } else {
throw LoaderConfigError.doesNotExist throw LoaderConfigError.doesNotExist
} }
} catch { } catch {
os_log("Couldn't get loader config", log: bunnyLog, type: .error) os_log("Couldn't get loader config", log: revengeLog, type: .error)
return defaultLoaderConfig return defaultLoaderConfig
} }

View file

@ -1,6 +1,6 @@
import Orion import Orion
import UIKit import UIKit
import BunnyTweakC import RevengeTweakC
import os import os
struct Author: Codable { struct Author: Codable {
@ -56,7 +56,7 @@ func hexToUIColor(_ hex: String) -> UIColor? {
} }
func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) { func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
os_log("Swizzling DCDThemeColor", log: bunnyLog, type: .info) os_log("Swizzling DCDThemeColor", log: revengeLog, type: .info)
let DCDTheme: AnyClass = NSClassFromString("DCDTheme")! let DCDTheme: AnyClass = NSClassFromString("DCDTheme")!
let dcdThemeTarget: AnyClass = object_getClass(DCDTheme)! let dcdThemeTarget: AnyClass = object_getClass(DCDTheme)!
@ -68,25 +68,25 @@ func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
let themeIndex: ThemeIndexType = unsafeBitCast(themeIndexImpl, to: ThemeIndexType.self) let themeIndex: ThemeIndexType = unsafeBitCast(themeIndexImpl, to: ThemeIndexType.self)
let DCDThemeColor: AnyClass = NSClassFromString("DCDThemeColor")! let DCDThemeColor: AnyClass = NSClassFromString("DCDThemeColor")!
os_log("Found instance of DCDThemeColor", log: bunnyLog, type: .debug) os_log("Found instance of DCDThemeColor", log: revengeLog, type: .debug)
let target: AnyClass = object_getClass(DCDThemeColor)! let target: AnyClass = object_getClass(DCDThemeColor)!
os_log("Found DCDThemeColor", log: bunnyLog, type: .debug) os_log("Found DCDThemeColor", log: revengeLog, type: .debug)
var methodCount: UInt32 = 0 var methodCount: UInt32 = 0
let methods = class_copyMethodList(target, &methodCount)! let methods = class_copyMethodList(target, &methodCount)!
os_log("DCDThemeColor has %{public}d methods", log: bunnyLog, type: .debug, methodCount) os_log("DCDThemeColor has %{public}d methods", log: revengeLog, type: .debug, methodCount)
for i in 0..<Int(methodCount) { for i in 0..<Int(methodCount) {
let method = methods[i] let method = methods[i]
let selector = method_getName(method) let selector = method_getName(method)
let methodName = NSStringFromSelector(selector) let methodName = NSStringFromSelector(selector)
os_log( os_log(
"Found method %{public}@", log: bunnyLog, type: .debug, methodName) "Found method %{public}@", log: revengeLog, type: .debug, methodName)
if let semanticColor = semanticColors[methodName] { if let semanticColor = semanticColors[methodName] {
os_log( os_log(
"Swizzling %{public}@", log: bunnyLog, type: .debug, methodName) "Swizzling %{public}@", log: revengeLog, type: .debug, methodName)
let originalImpl = method_getImplementation(method) let originalImpl = method_getImplementation(method)
typealias OriginalType = @convention(c) (AnyObject, Selector) -> UIColor typealias OriginalType = @convention(c) (AnyObject, Selector) -> UIColor
@ -115,28 +115,28 @@ func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
} }
func swizzleUIColor(_ rawColors: [String: String]) { func swizzleUIColor(_ rawColors: [String: String]) {
os_log("Swizzling UIColor", log: bunnyLog, type: .info) os_log("Swizzling UIColor", log: revengeLog, type: .info)
let UIColor: AnyClass = NSClassFromString("UIColor")! let UIColor: AnyClass = NSClassFromString("UIColor")!
os_log("Found instance of UIColor", log: bunnyLog, type: .debug) os_log("Found instance of UIColor", log: revengeLog, type: .debug)
let target: AnyClass = object_getClass(UIColor)! let target: AnyClass = object_getClass(UIColor)!
os_log("Found UIColor", log: bunnyLog, type: .debug) os_log("Found UIColor", log: revengeLog, type: .debug)
var methodCount: UInt32 = 0 var methodCount: UInt32 = 0
let methods = class_copyMethodList(target, &methodCount)! let methods = class_copyMethodList(target, &methodCount)!
os_log("UIColor has %{public}d methods", log: bunnyLog, type: .debug, methodCount) os_log("UIColor has %{public}d methods", log: revengeLog, type: .debug, methodCount)
for i in 0..<Int(methodCount) { for i in 0..<Int(methodCount) {
let method = methods[i] let method = methods[i]
let selector = method_getName(method) let selector = method_getName(method)
let methodName = NSStringFromSelector(selector) let methodName = NSStringFromSelector(selector)
os_log( os_log(
"Found method %{public}@", log: bunnyLog, type: .debug, methodName) "Found method %{public}@", log: revengeLog, type: .debug, methodName)
if let rawColor = rawColors[methodName] { if let rawColor = rawColors[methodName] {
os_log( os_log(
"Swizzling %{public}@", log: bunnyLog, type: .debug, methodName) "Swizzling %{public}@", log: revengeLog, type: .debug, methodName)
let rawColorBlock: @convention(block) (AnyObject) -> UIColor = { let rawColorBlock: @convention(block) (AnyObject) -> UIColor = {
(self: AnyObject) -> UIColor in (self: AnyObject) -> UIColor in
return hexToUIColor(rawColor)! return hexToUIColor(rawColor)!

View file

@ -1,18 +1,18 @@
import Orion import Orion
import BunnyTweakC import RevengeTweakC
import os import os
let bunnyLog = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "bunny") let revengeLog = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "revenge")
let source = URL(string: "bunny")! let source = URL(string: "revenge")!
let install_prefix = String(cString: get_install_prefix()) let install_prefix = String(cString: get_install_prefix())
let isJailbroken = FileManager.default.fileExists(atPath: "\(install_prefix)/Library/Application Support/BunnyTweak/BunnyPatches.bundle") let isJailbroken = FileManager.default.fileExists(atPath: "\(install_prefix)/Library/Application Support/RevengeTweak/RevengePatches.bundle")
let bunnyPatchesBundlePath = isJailbroken ? "\(install_prefix)/Library/Application Support/BunnyTweak/BunnyPatches.bundle" : "\(Bundle.main.bundleURL.path)/BunnyPatches.bundle" let revengePatchesBundlePath = isJailbroken ? "\(install_prefix)/Library/Application Support/RevengeTweak/RevengePatches.bundle" : "\(Bundle.main.bundleURL.path)/RevengePatches.bundle"
class FileManagerLoadHook: ClassHook<FileManager> { class FileManagerLoadHook: ClassHook<FileManager> {
func containerURLForSecurityApplicationGroupIdentifier(_ groupIdentifier: NSString?) -> URL? { func containerURLForSecurityApplicationGroupIdentifier(_ groupIdentifier: NSString?) -> URL? {
os_log("containerURLForSecurityApplicationGroupIdentifier called! %{public}@ groupIdentifier", log: bunnyLog, type: .debug, groupIdentifier ?? "nil") os_log("containerURLForSecurityApplicationGroupIdentifier called! %{public}@ groupIdentifier", log: revengeLog, type: .debug, groupIdentifier ?? "nil")
if (isJailbroken) { if (isJailbroken) {
return orig.containerURLForSecurityApplicationGroupIdentifier(groupIdentifier) return orig.containerURLForSecurityApplicationGroupIdentifier(groupIdentifier)
@ -26,15 +26,15 @@ class FileManagerLoadHook: ClassHook<FileManager> {
class LoadHook: ClassHook<RCTCxxBridge> { class LoadHook: ClassHook<RCTCxxBridge> {
func executeApplicationScript(_ script: Data, url: URL, async: Bool) { func executeApplicationScript(_ script: Data, url: URL, async: Bool) {
os_log("executeApplicationScript called!", log: bunnyLog, type: .debug) os_log("executeApplicationScript called!", log: revengeLog, type: .debug)
let loaderConfig = getLoaderConfig() let loaderConfig = getLoaderConfig()
let bunnyPatchesBundle = Bundle(path: bunnyPatchesBundlePath)! let revengePatchesBundle = Bundle(path: revengePatchesBundlePath)!
if let patchPath = bunnyPatchesBundle.url(forResource: "payload-base", withExtension: "js") { if let patchPath = revengePatchesBundle.url(forResource: "payload-base", withExtension: "js") {
let patchData = try! Data(contentsOf: patchPath) let patchData = try! Data(contentsOf: patchPath)
os_log("Executing payload base", log: bunnyLog, type: .debug) os_log("Executing payload base", log: revengeLog, type: .debug)
orig.executeApplicationScript(patchData, url: source, async: true) orig.executeApplicationScript(patchData, url: source, async: true)
} }
@ -48,15 +48,15 @@ class LoadHook: ClassHook<RCTCxxBridge> {
var bundleUrl: URL var bundleUrl: URL
if loaderConfig.customLoadUrl.enabled { if loaderConfig.customLoadUrl.enabled {
os_log( os_log(
"Custom load URL enabled, with URL %{public}@ ", log: bunnyLog, type: .info, "Custom load URL enabled, with URL %{public}@ ", log: revengeLog, type: .info,
loaderConfig.customLoadUrl.url.absoluteString) loaderConfig.customLoadUrl.url.absoluteString)
bundleUrl = loaderConfig.customLoadUrl.url bundleUrl = loaderConfig.customLoadUrl.url
} else { } else {
bundleUrl = URL( bundleUrl = URL(
string: "https://raw.githubusercontent.com/pyoncord/bunny-builds/main/bunny.js")! string: "https://github.com/revenge-mod/Revenge/releases/latest/download/revenge.js")!
} }
os_log("Fetching JS bundle", log: bunnyLog, type: .info) os_log("Fetching JS bundle", log: revengeLog, type: .info)
var bundleRequest = URLRequest( var bundleRequest = URLRequest(
url: bundleUrl, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 3.0) url: bundleUrl, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 3.0)
@ -68,7 +68,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
let fetchTask = URLSession.shared.dataTask(with: bundleRequest) { data, response, error in let fetchTask = URLSession.shared.dataTask(with: bundleRequest) { data, response, error in
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
os_log("Successfully fetched JS Bundle", log: bunnyLog, type: .debug) os_log("Successfully fetched JS Bundle", log: revengeLog, type: .debug)
bundle = data bundle = data
try? bundle?.write(to: pyoncordDirectory.appendingPathComponent("bundle.js")) try? bundle?.write(to: pyoncordDirectory.appendingPathComponent("bundle.js"))
@ -92,40 +92,40 @@ class LoadHook: ClassHook<RCTCxxBridge> {
} }
let preloadsDirectory = pyoncordDirectory.appendingPathComponent("preloads") let preloadsDirectory = pyoncordDirectory.appendingPathComponent("preloads")
if FileManager.default.fileExists(atPath: preloadsDirectory.path) { if FileManager.default.fileExists(atPath: preloadsDirectory.path) {
do { do {
let contents = try FileManager.default.contentsOfDirectory( let contents = try FileManager.default.contentsOfDirectory(
at: preloadsDirectory, includingPropertiesForKeys: nil, options: []) at: preloadsDirectory, includingPropertiesForKeys: nil, options: [])
for fileURL in contents { for fileURL in contents {
if fileURL.pathExtension == "js" { if fileURL.pathExtension == "js" {
os_log( os_log(
"Executing preload JS file %{public}@ ", log: bunnyLog, type: .info, fileURL.absoluteString) "Executing preload JS file %{public}@ ", log: revengeLog, type: .info, fileURL.absoluteString)
if let data = try? Data(contentsOf: fileURL) { if let data = try? Data(contentsOf: fileURL) {
orig.executeApplicationScript(data, url: source, async: async) orig.executeApplicationScript(data, url: source, async: async)
} }
} }
} }
} catch { } catch {
os_log("Error reading contents of preloads directory", log: bunnyLog, type: .error) os_log("Error reading contents of preloads directory", log: revengeLog, type: .error)
} }
} }
if bundle != nil { if bundle != nil {
os_log("Executing JS bundle", log: bunnyLog, type: .info) os_log("Executing JS bundle", log: revengeLog, type: .info)
orig.executeApplicationScript(bundle!, url: source, async: async) orig.executeApplicationScript(bundle!, url: source, async: async)
} else { } else {
os_log("Unable to fetch JS bundle", log: bunnyLog, type: .error) os_log("Unable to fetch JS bundle", log: revengeLog, type: .error)
} }
os_log("Executing original script", log: bunnyLog, type: .info) os_log("Executing original script", log: revengeLog, type: .info)
orig.executeApplicationScript(script, url: url, async: async) orig.executeApplicationScript(script, url: url, async: async)
} }
} }
struct BunnyTweak: Tweak { struct RevengeTweak: Tweak {
func tweakDidActivate() { func tweakDidActivate() {
if let themeData = try? Data( if let themeData = try? Data(
contentsOf: pyoncordDirectory.appendingPathComponent("current-theme.json")) { contentsOf: pyoncordDirectory.appendingPathComponent("current-theme.json")) {

View file

@ -3,14 +3,14 @@ import Foundation
func getPyoncordDirectory() -> URL { func getPyoncordDirectory() -> URL {
let documentDirectoryURL = try! FileManager.default.url( let documentDirectoryURL = try! FileManager.default.url(
for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let pyoncordFolderURL = documentDirectoryURL.appendingPathComponent("pyoncord") let pyoncordFolderURL = documentDirectoryURL.appendingPathComponent("pyoncord")
// Create the "pyoncord" folder if it doesn't exist // Create the "pyoncord" folder if it doesn't exist
if !FileManager.default.fileExists(atPath: pyoncordFolderURL.path) { if !FileManager.default.fileExists(atPath: pyoncordFolderURL.path) {
try! FileManager.default.createDirectory( try! FileManager.default.createDirectory(
at: pyoncordFolderURL, withIntermediateDirectories: true, attributes: nil) at: pyoncordFolderURL, withIntermediateDirectories: true, attributes: nil)
} }
return pyoncordFolderURL return pyoncordFolderURL
} }

View file

@ -1,4 +1,4 @@
module BunnyTweakC { module RevengeTweakC {
umbrella "." umbrella "."
export * export *
} }

View file

@ -1,13 +1,13 @@
{ {
"name": "Pyoncord App Repo", "name": "Revenge App Repo",
"identifier": "io.github.pyoncord.repo", "identifier": "io.github.revenge-mod.repo",
"iconURL": "https://pyoncord.github.io/assets/icon-altstore.png", "iconURL": "https://cdn.discordapp.com/attachments/1205207690352005243/1284228073415249950/icon-altstore.png?ex=66e5de18&is=66e48c98&hm=737f59f228f7d887881fff1fcb6d8b08258b91c3c0d4df8501dfc499b51d870d&",
"apps": [ "apps": [
{ {
"name": "Bunny", "name": "Revenge",
"bundleIdentifier": "com.hammerandchisel.discord", "bundleIdentifier": "com.hammerandchisel.discord",
"developerName": "Pylix", "developerName": "Cristiandis",
"iconURL": "https://pyoncord.github.io/assets/icon-altstore.png", "iconURL": "https://cdn.discordapp.com/attachments/1205207690352005243/1284228073415249950/icon-altstore.png?ex=66e5de18&is=66e48c98&hm=737f59f228f7d887881fff1fcb6d8b08258b91c3c0d4df8501dfc499b51d870d&",
"localizedDescription": "A client mod for Discord", "localizedDescription": "A client mod for Discord",
"subtitle": "A client mod for Discord", "subtitle": "A client mod for Discord",
"tintColor": "7289da", "tintColor": "7289da",
@ -15,50 +15,8 @@
{ {
"version": "246.0", "version": "246.0",
"date": "2024-09-14", "date": "2024-09-14",
"size": 103195545, "size": 103194075,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v246.0/Bunny.ipa" "downloadURL": "https://github.com/revenge-mod/RevengeTweak/releases/download/v246.0/Revenge.ipa"
},
{
"version": "237.0",
"date": "2024-07-13",
"size": 114323285,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v237.0/Bunny.ipa"
},
{
"version": "235.0",
"date": "2024-06-27",
"size": 112173273,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v235.0/Bunny.ipa"
},
{
"version": "234.0",
"date": "2024-06-24",
"size": 111671815,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v234.0/Bunny.ipa"
},
{
"version": "230.0",
"date": "2024-05-18",
"size": 102457139,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v230.0/Bunny.ipa"
},
{
"version": "229.0",
"date": "2024-05-16",
"size": 103776118,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v229.0/Bunny.ipa"
},
{
"version": "226.1",
"date": "2024-04-30",
"size": 102705408,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v226.1/Pyoncord.ipa"
},
{
"version": "223.0",
"date": "2024-04-08",
"size": 106082919,
"downloadURL": "https://github.com/pyoncord/BunnyTweak/releases/download/v223.0/Pyoncord.ipa"
} }
], ],
"appPermissions": {} "appPermissions": {}

12
control
View file

@ -1,11 +1,11 @@
Package: io.github.pyoncord.app Package: io.github.revenge-mod.app
Name: Bunny Name: Revenge
Version: 0.3.1 Version: 0.0.1
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: A mod for Discord's mobile apps. Description: A mod for Discord's mobile apps.
Maintainer: Pylix Maintainer: Cristiandis
Author: Pylix, FieryFlames Author: Pylix, FieryFlames, Cristiandis
Icon: https://avatars.githubusercontent.com/u/132727175 Icon: https://avatars.githubusercontent.com/u/132727175
Section: Tweaks Section: Tweaks
Depends: ${ORION}, firmware (>= 13.0) Depends: ${ORION}, firmware (>= 13.0)
Conflicts: app.enmity, mod.vendetta Conflicts: app.enmity, mod.vendetta, mod.bunny