mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-19 00:53:57 +00:00
Compare commits
20 Commits
v2.176.0-d
...
v2.176.2-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cec0b59c1 | ||
|
|
228e8b7d48 | ||
|
|
70e893a5d3 | ||
|
|
28abefeab0 | ||
|
|
b2c7a6a54f | ||
|
|
4570b273a7 | ||
|
|
45d5f708af | ||
|
|
3cd52cbffa | ||
|
|
35803524f4 | ||
|
|
b4312c0945 | ||
|
|
6bbe1a1433 | ||
|
|
c508efd0b0 | ||
|
|
d3d89c9d27 | ||
|
|
9192987b55 | ||
|
|
5e3865cb36 | ||
|
|
7138447a1f | ||
|
|
f96ce4063b | ||
|
|
c282abf677 | ||
|
|
7cf1e35fad | ||
|
|
6a40265e9b |
25
.github/workflows/release.yml
vendored
25
.github/workflows/release.yml
vendored
@@ -23,23 +23,16 @@ jobs:
|
|||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup JDK
|
- name: Cache
|
||||||
uses: actions/setup-java@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
path: |
|
||||||
distribution: 'zulu'
|
${{ runner.home }}/.gradle/caches
|
||||||
cache: gradle
|
${{ runner.home }}/.gradle/wrapper
|
||||||
- name: Setup Node.js
|
.gradle
|
||||||
uses: actions/setup-node@v3
|
build
|
||||||
with:
|
node_modules
|
||||||
node-version: '18'
|
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||||
cache: 'npm'
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: amyu/setup-android@v2
|
|
||||||
with:
|
|
||||||
cache-disabled: false
|
|
||||||
sdk-version: '33'
|
|
||||||
build-tools-version: '33.0.1'
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
80
CHANGELOG.md
80
CHANGELOG.md
@@ -1,3 +1,83 @@
|
|||||||
|
## [2.176.2-dev.2](https://github.com/revanced/revanced-patches/compare/v2.176.2-dev.1...v2.176.2-dev.2) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **syncforreddit/change-oauth-client-id:** trim whitespace from OAuth string ([#2402](https://github.com/revanced/revanced-patches/issues/2402)) ([2afea71](https://github.com/revanced/revanced-patches/commit/2afea71557cfe4eb64d7c7ebf5a07dfd24a11824))
|
||||||
|
|
||||||
|
## [2.176.2-dev.1](https://github.com/revanced/revanced-patches/compare/v2.176.1...v2.176.2-dev.1) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **syncforreddit/change-oauth-client-id:** use downloads directory ([9b5af77](https://github.com/revanced/revanced-patches/commit/9b5af77a229a22466cfe8ed41a21d081beeae960))
|
||||||
|
|
||||||
|
## [2.176.1](https://github.com/revanced/revanced-patches/compare/v2.176.0...v2.176.1) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **syncforreddit/change-oauth-client-id:** use correct signature for Android API ([4db2eba](https://github.com/revanced/revanced-patches/commit/4db2eba6d66f094f1d2400bfa8b9c15a175f796e))
|
||||||
|
|
||||||
|
## [2.176.1-dev.1](https://github.com/revanced/revanced-patches/compare/v2.176.0...v2.176.1-dev.1) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **syncforreddit/change-oauth-client-id:** use correct signature for Android API ([4db2eba](https://github.com/revanced/revanced-patches/commit/4db2eba6d66f094f1d2400bfa8b9c15a175f796e))
|
||||||
|
|
||||||
|
# [2.176.0](https://github.com/revanced/revanced-patches/compare/v2.175.0...v2.176.0) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **instagram/hide-timeline-ads:** constrain to version `275.0.0.27.98` ([#2377](https://github.com/revanced/revanced-patches/issues/2377)) ([6ee836c](https://github.com/revanced/revanced-patches/commit/6ee836c587676ba53c2e4748ec1828e193f1c071))
|
||||||
|
* **songpal:** make patches more resilient ([#2386](https://github.com/revanced/revanced-patches/issues/2386)) ([947bf42](https://github.com/revanced/revanced-patches/commit/947bf42b7c8d029b8a26a5c306869e02a69b62fd))
|
||||||
|
* use correct import ([9b6e730](https://github.com/revanced/revanced-patches/commit/9b6e730395e56e60cf08372dbbc3a10e92ba8e95))
|
||||||
|
* **youtube/hide-get-premium:** shorten descriptions ([f5224c4](https://github.com/revanced/revanced-patches/commit/f5224c4980285ef1e29d17887f02b5b397785245))
|
||||||
|
* **youtube/theme:** use custom light/dark colors for launch splash screen ([#2337](https://github.com/revanced/revanced-patches/issues/2337)) ([8adc05a](https://github.com/revanced/revanced-patches/commit/8adc05a17445dede5f35ee27426d20b750a2a1d3))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **citra:** remove patches ([#2384](https://github.com/revanced/revanced-patches/issues/2384)) ([d182329](https://github.com/revanced/revanced-patches/commit/d1823297ce27fc6f3246123c91d9463080afb7ee))
|
||||||
|
* **finanzonline:** support any version ([#2347](https://github.com/revanced/revanced-patches/issues/2347)) ([cc66b1f](https://github.com/revanced/revanced-patches/commit/cc66b1fc6200bbdcc2697ddd7085d46447775be2))
|
||||||
|
* **reddit:** add `hide-promoted` patch ([#2350](https://github.com/revanced/revanced-patches/issues/2350)) ([c3fd36c](https://github.com/revanced/revanced-patches/commit/c3fd36cfba7fb8246f8b3a29d9310bd88f01c3e7))
|
||||||
|
* **songpal:** add `remove-badge-tab` patch ([bde9053](https://github.com/revanced/revanced-patches/commit/bde9053f04e202a7b394a1dad335470d5495e5fc))
|
||||||
|
* **songpal:** add `remove-notification-badge` patch ([1b77f96](https://github.com/revanced/revanced-patches/commit/1b77f9633cf7e72d653f1d37483b7399e1ad797d))
|
||||||
|
* **syncforreddit/change-oauth-client-id:** improve error message ([a39b2fd](https://github.com/revanced/revanced-patches/commit/a39b2fd9828b514670135918e69ee736cb4615e5))
|
||||||
|
* **syncforreddit:** add `change-oauth-client-id` patch ([#2393](https://github.com/revanced/revanced-patches/issues/2393)) ([c93f0c6](https://github.com/revanced/revanced-patches/commit/c93f0c6a99fb2365747371f0f38eff8e0b77aee1))
|
||||||
|
* **youtube/hide-ads:** hide mix playlists ([dbb38ed](https://github.com/revanced/revanced-patches/commit/dbb38eddfd33db685b8e8be2f2a641a1199e7361))
|
||||||
|
* **youtube/swipe-controls:** add option to save and restore brightness ([3d03471](https://github.com/revanced/revanced-patches/commit/3d03471b1c2f18d22f17167db78764520064353a))
|
||||||
|
|
||||||
|
# [2.176.0-dev.7](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.6...v2.176.0-dev.7) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncforreddit/change-oauth-client-id:** improve error message ([a39b2fd](https://github.com/revanced/revanced-patches/commit/a39b2fd9828b514670135918e69ee736cb4615e5))
|
||||||
|
|
||||||
|
# [2.176.0-dev.6](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.5...v2.176.0-dev.6) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **reddit:** add `hide-promoted` patch ([#2350](https://github.com/revanced/revanced-patches/issues/2350)) ([c3fd36c](https://github.com/revanced/revanced-patches/commit/c3fd36cfba7fb8246f8b3a29d9310bd88f01c3e7))
|
||||||
|
|
||||||
|
# [2.176.0-dev.5](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.4...v2.176.0-dev.5) (2023-06-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncforreddit:** add `change-oauth-client-id` patch ([#2393](https://github.com/revanced/revanced-patches/issues/2393)) ([c93f0c6](https://github.com/revanced/revanced-patches/commit/c93f0c6a99fb2365747371f0f38eff8e0b77aee1))
|
||||||
|
|
||||||
|
# [2.176.0-dev.4](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.3...v2.176.0-dev.4) (2023-06-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **songpal:** make patches more resilient ([#2386](https://github.com/revanced/revanced-patches/issues/2386)) ([947bf42](https://github.com/revanced/revanced-patches/commit/947bf42b7c8d029b8a26a5c306869e02a69b62fd))
|
||||||
|
|
||||||
# [2.176.0-dev.3](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.2...v2.176.0-dev.3) (2023-06-10)
|
# [2.176.0-dev.3](https://github.com/revanced/revanced-patches/compare/v2.176.0-dev.2...v2.176.0-dev.3) (2023-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
38
README.md
38
README.md
@@ -140,17 +140,6 @@ The official ReVanced Patches.
|
|||||||
| `hide-views-stats` | Hides the view stats under tweets. | 9.71.0-release.0 |
|
| `hide-views-stats` | Hides the view stats under tweets. | 9.71.0-release.0 |
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### [📦 `com.reddit.frontpage`](https://play.google.com/store/apps/details?id=com.reddit.frontpage)
|
|
||||||
<details>
|
|
||||||
|
|
||||||
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
|
||||||
|:--------:|:--------------:|:-----------------:|
|
|
||||||
| `hide-ads` | Removes general ads from the Reddit frontpage and subreddits. | all |
|
|
||||||
| `hide-subreddit-banner` | Hides banner ads from comments on subreddits. | all |
|
|
||||||
| `premium-icon-reddit` | Unlocks premium Reddit app icons. | all |
|
|
||||||
| `sanitize-sharing-links` | Removes (tracking) query parameters from the URLs when sharing links. | all |
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### [📦 `com.facebook.orca`](https://play.google.com/store/apps/details?id=com.facebook.orca)
|
### [📦 `com.facebook.orca`](https://play.google.com/store/apps/details?id=com.facebook.orca)
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
@@ -161,6 +150,25 @@ The official ReVanced Patches.
|
|||||||
| `hide-inbox-ads` | Hides ads in inbox. | all |
|
| `hide-inbox-ads` | Hides ads in inbox. | all |
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### [📦 `com.reddit.frontpage`](https://play.google.com/store/apps/details?id=com.reddit.frontpage)
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `hide-ads` | Removes ads from the Reddit. | all |
|
||||||
|
| `premium-icon-reddit` | Unlocks premium Reddit app icons. | all |
|
||||||
|
| `sanitize-sharing-links` | Removes (tracking) query parameters from the URLs when sharing links. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### [📦 `com.laurencedawson.reddit_sync`](https://play.google.com/store/apps/details?id=com.laurencedawson.reddit_sync)
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `change-oauth-client-id` | Changes the OAuth client ID. | all |
|
||||||
|
| `disable-ads` | Disables ads. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
### [📦 `com.spotify.music`](https://play.google.com/store/apps/details?id=com.spotify.music)
|
### [📦 `com.spotify.music`](https://play.google.com/store/apps/details?id=com.spotify.music)
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
@@ -205,14 +213,6 @@ The official ReVanced Patches.
|
|||||||
| `disable-ads` | Disables ads in HexEditor. | all |
|
| `disable-ads` | Disables ads in HexEditor. | all |
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### [📦 `com.laurencedawson.reddit_sync`](https://play.google.com/store/apps/details?id=com.laurencedawson.reddit_sync)
|
|
||||||
<details>
|
|
||||||
|
|
||||||
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
|
||||||
|:--------:|:--------------:|:-----------------:|
|
|
||||||
| `disable-ads` | Disables ads. | all |
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### [📦 `com.spotify.lite`](https://play.google.com/store/apps/details?id=com.spotify.lite)
|
### [📦 `com.spotify.lite`](https://play.google.com/store/apps/details?id=com.spotify.lite)
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson:2.10.1")
|
implementation("com.google.code.gson:gson:2.10.1")
|
||||||
// Required for FlexVer-Java
|
// Required for FlexVer-Java
|
||||||
implementation("com.unascribed:flexver-java:1.0.2")
|
implementation("com.unascribed:flexver-java:1.0.2")
|
||||||
|
|
||||||
|
// A dependency to the Android library unfortunately fails the build,
|
||||||
|
// which is why this is required for the patch change-oauth-client-id
|
||||||
|
compileOnly(project("dummy"))
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|||||||
9
dummy/build.gradle.kts
Normal file
9
dummy/build.gradle.kts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(JavaLanguageVersion.of(11))
|
||||||
|
}
|
||||||
|
}
|
||||||
11
dummy/src/main/java/android/os/Environment.java
Normal file
11
dummy/src/main/java/android/os/Environment.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package android.os;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public final class Environment {
|
||||||
|
public static String DIRECTORY_DOWNLOADS = "Download";
|
||||||
|
|
||||||
|
public static File getExternalStoragePublicDirectory(final String type) {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 2.176.0-dev.3
|
version = 2.176.2-dev.2
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1,3 @@
|
|||||||
|
include("dummy")
|
||||||
|
|
||||||
rootProject.name = "revanced-patches"
|
rootProject.name = "revanced-patches"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.patches.reddit.ad.banner.annotations
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Compatibility
|
|
||||||
import app.revanced.patcher.annotation.Package
|
|
||||||
|
|
||||||
@Compatibility(
|
|
||||||
[Package(
|
|
||||||
"com.reddit.frontpage"
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
internal annotation class HideBannerCompatibility
|
|
||||||
@@ -7,17 +7,12 @@ import app.revanced.patcher.data.ResourceContext
|
|||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patches.reddit.ad.banner.annotations.HideBannerCompatibility
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
@Name("hide-subreddit-banner")
|
@Name("hide-subreddit-banner")
|
||||||
@Description("Hides banner ads from comments on subreddits.")
|
@Description("Hides banner ads from comments on subreddits.")
|
||||||
@HideBannerCompatibility
|
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class HideBannerPatch : ResourcePatch {
|
class HideBannerPatch : ResourcePatch {
|
||||||
override fun execute(context: ResourceContext): PatchResult {
|
override fun execute(context: ResourceContext): PatchResult {
|
||||||
|
|
||||||
context.xmlEditor[RESOURCE_FILE_PATH].use {
|
context.xmlEditor[RESOURCE_FILE_PATH].use {
|
||||||
it.file.getElementsByTagName("merge").item(0).childNodes.apply {
|
it.file.getElementsByTagName("merge").item(0).childNodes.apply {
|
||||||
val attributes = arrayOf("height", "width")
|
val attributes = arrayOf("height", "width")
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.comments.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object HideCommentAdsFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf(
|
||||||
|
"link",
|
||||||
|
// CommentPageRepository is not returning a link object
|
||||||
|
"is not returning a link object"
|
||||||
|
),
|
||||||
|
customFingerprint = { _, classDef ->
|
||||||
|
classDef.sourceFile == "PostDetailPresenter.kt"
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.comments.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
|
import app.revanced.patches.reddit.ad.comments.fingerprints.HideCommentAdsFingerprint
|
||||||
|
|
||||||
|
@Name("hide-comment-ads")
|
||||||
|
@Description("Removes all comment ads.")
|
||||||
|
@RequiresIntegrations
|
||||||
|
@Version("0.0.1")
|
||||||
|
class HideCommentAdsPatch : BytecodePatch(
|
||||||
|
listOf(HideCommentAdsFingerprint)
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
val method = HideCommentAdsFingerprint.result!!.mutableMethod
|
||||||
|
// Returns a blank object instead of the comment ad.
|
||||||
|
method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
new-instance v0, Ljava/lang/Object;
|
||||||
|
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,6 @@ package app.revanced.patches.reddit.ad.general.annotations
|
|||||||
import app.revanced.patcher.annotation.Compatibility
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
import app.revanced.patcher.annotation.Package
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
@Compatibility(
|
@Compatibility([Package("com.reddit.frontpage")])
|
||||||
[Package(
|
|
||||||
"com.reddit.frontpage"
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
internal annotation class HideAdsCompatibility
|
internal annotation class HideAdsCompatibility
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.general.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object AdPostFingerprint : MethodFingerprint(
|
||||||
|
"V",
|
||||||
|
// "children" are present throughout multiple versions
|
||||||
|
strings = listOf("children"),
|
||||||
|
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("Listing;") },
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.general.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object NewAdPostFingerprint : MethodFingerprint(
|
||||||
|
opcodes = listOf(Opcode.INVOKE_VIRTUAL),
|
||||||
|
strings = listOf("chain", "feedElement"),
|
||||||
|
customFingerprint = { _, classDef -> classDef.sourceFile == "AdElementConverter.kt" },
|
||||||
|
)
|
||||||
@@ -4,51 +4,89 @@ import app.revanced.patcher.annotation.Description
|
|||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
|
import app.revanced.patches.reddit.ad.banner.patch.HideBannerPatch
|
||||||
|
import app.revanced.patches.reddit.ad.comments.patch.HideCommentAdsPatch
|
||||||
import app.revanced.patches.reddit.ad.general.annotations.HideAdsCompatibility
|
import app.revanced.patches.reddit.ad.general.annotations.HideAdsCompatibility
|
||||||
|
import app.revanced.patches.reddit.ad.general.fingerprints.AdPostFingerprint
|
||||||
|
import app.revanced.patches.reddit.ad.general.fingerprints.NewAdPostFingerprint
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.StringReference
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||||
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("hide-ads")
|
@Name("hide-ads")
|
||||||
@Description("Removes general ads from the Reddit frontpage and subreddits.")
|
@Description("Removes ads from the Reddit.")
|
||||||
|
@DependsOn([HideBannerPatch::class, HideCommentAdsPatch::class])
|
||||||
@HideAdsCompatibility
|
@HideAdsCompatibility
|
||||||
@Version("0.0.1")
|
@RequiresIntegrations
|
||||||
class HideAdsPatch : BytecodePatch() {
|
@Version("0.0.2")
|
||||||
|
class HideAdsPatch : BytecodePatch(
|
||||||
|
listOf(AdPostFingerprint, NewAdPostFingerprint)
|
||||||
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
context.classes.forEach { classDef ->
|
// region Filter promoted ads (does not work in popular or latest feed)
|
||||||
classDef.methods.forEach methodLoop@{ method ->
|
|
||||||
val implementation = method.implementation ?: return@methodLoop
|
|
||||||
|
|
||||||
implementation.instructions.forEachIndexed { i, instruction ->
|
AdPostFingerprint.result?.mutableMethod?.apply {
|
||||||
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
|
val setPostsListChildren = implementation!!.instructions.first { instruction ->
|
||||||
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed
|
if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false
|
||||||
|
|
||||||
val proxiedClass = context.proxy(classDef).mutableClass
|
val reference = (instruction as ReferenceInstruction).reference as FieldReference
|
||||||
|
reference.name == "children"
|
||||||
val proxiedImplementation = proxiedClass.methods.first {
|
|
||||||
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
|
|
||||||
}.implementation!!
|
|
||||||
|
|
||||||
var newString = "AdPost1"
|
|
||||||
if (proxiedImplementation.instructions[i - 1].opcode == Opcode.CONST_STRING) {
|
|
||||||
newString = "SubredditPost"
|
|
||||||
}
|
|
||||||
proxiedImplementation.replaceInstruction(
|
|
||||||
i, BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING, (proxiedImplementation.instructions[i] as BuilderInstruction21c).registerA, ImmutableStringReference(newString)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val castedInstruction = setPostsListChildren as Instruction22c
|
||||||
|
val itemsRegister = castedInstruction.registerA
|
||||||
|
val listInstanceRegister = castedInstruction.registerB
|
||||||
|
|
||||||
|
// postsList.children = filterChildren(postListItems)
|
||||||
|
removeInstruction(setPostsListChildren.location.index)
|
||||||
|
addInstructions(
|
||||||
|
setPostsListChildren.location.index,
|
||||||
|
"""
|
||||||
|
invoke-static {v$itemsRegister}, $FILTER_METHOD_DESCRIPTOR
|
||||||
|
move-result-object v0
|
||||||
|
iput-object v0, v$listInstanceRegister, ${castedInstruction.reference}
|
||||||
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Remove ads from popular and latest feed
|
||||||
|
|
||||||
|
NewAdPostFingerprint.result?.let { result ->
|
||||||
|
// The new feeds work by inserting posts into lists.
|
||||||
|
// AdElementConverter is conveniently responsible for inserting all feed ads.
|
||||||
|
// By removing the appending instruction no ad posts gets appended to the feed.
|
||||||
|
val index = result.method.implementation!!.instructions.indexOfFirst {
|
||||||
|
if (it.opcode != Opcode.INVOKE_VIRTUAL) return@indexOfFirst false
|
||||||
|
|
||||||
|
val reference = (it as ReferenceInstruction).reference as MethodReference
|
||||||
|
|
||||||
|
reference.name == "add" && reference.definingClass == "Lava/util/ArrayList;"
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mutableMethod.removeInstruction(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val FILTER_METHOD_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/reddit/patches/FilterPromotedLinksPatch;" +
|
||||||
|
"->filterChildren(Ljava/lang/Iterable;)Ljava/util/List;"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,26 @@
|
|||||||
package app.revanced.patches.songpal.badge.fingerprints
|
package app.revanced.patches.songpal.badge.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.songpal.badge.patch.BadgeTabPatch
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
// Located @ ub.i0.h#p (9.5.0)
|
// Located @ ub.i0.h#p (9.5.0)
|
||||||
@FuzzyPatternScanMethod(2)
|
|
||||||
object CreateTabsFingerprint : MethodFingerprint(
|
object CreateTabsFingerprint : MethodFingerprint(
|
||||||
"L",
|
"Ljava/util/List",
|
||||||
accessFlags = AccessFlags.PRIVATE.value,
|
accessFlags = AccessFlags.PRIVATE.value,
|
||||||
opcodes = listOf(
|
customFingerprint = { methodDef, _ ->
|
||||||
Opcode.INVOKE_STATIC,
|
methodDef.implementation?.instructions?.any { instruction ->
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
if (instruction.opcode != Opcode.INVOKE_STATIC) return@any false
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
val reference = (instruction as ReferenceInstruction).reference as MethodReference
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
if (reference.parameterTypes.isNotEmpty()) return@any false
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
if (reference.definingClass != BadgeTabPatch.ACTIVITY_TAB_DESCRIPTOR) return@any false
|
||||||
Opcode.SGET_OBJECT,
|
if (reference.returnType != "[${BadgeTabPatch.ACTIVITY_TAB_DESCRIPTOR}") return@any false
|
||||||
Opcode.INVOKE_INTERFACE,
|
true
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
} ?: false
|
||||||
Opcode.INVOKE_STATIC,
|
}
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.RETURN_OBJECT
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,31 +1,37 @@
|
|||||||
package app.revanced.patches.songpal.badge.fingerprints
|
package app.revanced.patches.songpal.badge.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.songpal.badge.fingerprints.ShowNotificationFingerprint.expectedReference
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
||||||
|
|
||||||
// Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0)
|
// Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0)
|
||||||
@FuzzyPatternScanMethod(2)
|
|
||||||
object ShowNotificationFingerprint : MethodFingerprint(
|
object ShowNotificationFingerprint : MethodFingerprint(
|
||||||
"V",
|
"V",
|
||||||
accessFlags = AccessFlags.PUBLIC.value,
|
accessFlags = AccessFlags.PUBLIC.value,
|
||||||
opcodes = listOf(
|
customFingerprint = custom@{ methodDef, _ ->
|
||||||
Opcode.IGET_OBJECT,
|
methodDef.implementation?.instructions?.any { instruction ->
|
||||||
Opcode.IGET,
|
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@any false
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
with(expectedReference) {
|
||||||
Opcode.IGET_OBJECT,
|
val currentReference = (instruction as ReferenceInstruction).reference as MethodReference
|
||||||
Opcode.INVOKE_VIRTUAL,
|
currentReference.let {
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
if (it.definingClass != definingClass) return@any false
|
||||||
Opcode.CONST,
|
if (it.parameterTypes != parameterTypes) return@any false
|
||||||
Opcode.INVOKE_VIRTUAL,
|
if (it.returnType != returnType) return@any false
|
||||||
Opcode.MOVE_RESULT,
|
}
|
||||||
Opcode.INVOKE_VIRTUAL,
|
}
|
||||||
Opcode.IGET_BOOLEAN,
|
true
|
||||||
Opcode.INVOKE_VIRTUAL,
|
} ?: false
|
||||||
Opcode.CONST,
|
}
|
||||||
Opcode.INVOKE_VIRTUAL,
|
) {
|
||||||
Opcode.RETURN_VOID
|
val expectedReference = ImmutableMethodReference(
|
||||||
|
"Lcom/google/android/material/bottomnavigation/BottomNavigationView;",
|
||||||
|
"getOrCreateBadge", // Non-obfuscated placeholder method name.
|
||||||
|
listOf("I"),
|
||||||
|
"Lcom/google/android/material/badge/BadgeDrawable;",
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.patches.syncforreddit.ads.annotations
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Compatibility
|
|
||||||
import app.revanced.patcher.annotation.Package
|
|
||||||
|
|
||||||
@Compatibility([Package("com.laurencedawson.reddit_sync")])
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
internal annotation class DisableAdsCompatibility
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package app.revanced.patches.syncforreddit.ads.patch
|
package app.revanced.patches.syncforreddit.ads.patch
|
||||||
|
|
||||||
import app.revanced.extensions.toErrorResult
|
import app.revanced.extensions.toErrorResult
|
||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.*
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
@@ -11,7 +9,6 @@ import app.revanced.patcher.patch.PatchResult
|
|||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patches.syncforreddit.ads.annotations.DisableAdsCompatibility
|
|
||||||
import app.revanced.patches.syncforreddit.ads.fingerprints.IsAdsEnabledFingerprint
|
import app.revanced.patches.syncforreddit.ads.fingerprints.IsAdsEnabledFingerprint
|
||||||
import app.revanced.patches.syncforreddit.detection.piracy.patch.DisablePiracyDetectionPatch
|
import app.revanced.patches.syncforreddit.detection.piracy.patch.DisablePiracyDetectionPatch
|
||||||
|
|
||||||
@@ -19,8 +16,8 @@ import app.revanced.patches.syncforreddit.detection.piracy.patch.DisablePiracyDe
|
|||||||
@Name("disable-ads")
|
@Name("disable-ads")
|
||||||
@DependsOn([DisablePiracyDetectionPatch::class])
|
@DependsOn([DisablePiracyDetectionPatch::class])
|
||||||
@Description("Disables ads.")
|
@Description("Disables ads.")
|
||||||
|
@Compatibility([Package("com.laurencedawson.reddit_sync")])
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
@DisableAdsCompatibility
|
|
||||||
class DisableAdsPatch : BytecodePatch(listOf(IsAdsEnabledFingerprint)) {
|
class DisableAdsPatch : BytecodePatch(listOf(IsAdsEnabledFingerprint)) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
IsAdsEnabledFingerprint.result?.mutableMethod?.apply {
|
IsAdsEnabledFingerprint.result?.mutableMethod?.apply {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.patches.syncforreddit.api.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object GetAuthorizationStringFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("authorize.compact?client_id")
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.patches.syncforreddit.api.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object GetBearerTokenFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("Basic")
|
||||||
|
)
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package app.revanced.patches.syncforreddit.api.patch
|
||||||
|
|
||||||
|
import android.os.Environment
|
||||||
|
import app.revanced.patcher.annotation.*
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
|
import app.revanced.patcher.patch.*
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patches.syncforreddit.api.fingerprints.GetAuthorizationStringFingerprint
|
||||||
|
import app.revanced.patches.syncforreddit.api.fingerprints.GetBearerTokenFingerprint
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.StringReference
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("change-oauth-client-id")
|
||||||
|
@Description("Changes the OAuth client ID.")
|
||||||
|
@Compatibility([Package("com.laurencedawson.reddit_sync")])
|
||||||
|
@Version("0.0.1")
|
||||||
|
class ChangeOAuthClientIdPatch : BytecodePatch(
|
||||||
|
listOf(GetAuthorizationStringFingerprint)
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
if (clientId == null) {
|
||||||
|
// Test if on Android
|
||||||
|
try {
|
||||||
|
Class.forName("android.os.Environment")
|
||||||
|
} catch (e: ClassNotFoundException) {
|
||||||
|
return PatchResultError("No client ID provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
File(
|
||||||
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"reddit_client_id_revanced.txt"
|
||||||
|
).also {
|
||||||
|
if (it.exists()) return@also
|
||||||
|
|
||||||
|
val error = """
|
||||||
|
In order to use this patch, you need to provide a client ID.
|
||||||
|
You can do this by creating a file at ${it.absolutePath} with the client ID as its content.
|
||||||
|
Alternatively, you can provide the client ID using patch options.
|
||||||
|
|
||||||
|
You can get your client ID from https://www.reddit.com/prefs/apps.
|
||||||
|
The application type has to be "installed app" and the redirect URI has to be set to "http://redditsync/auth"
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
return PatchResultError(error)
|
||||||
|
}.let { clientId = it.readText().trim() }
|
||||||
|
}
|
||||||
|
|
||||||
|
GetAuthorizationStringFingerprint.result?.also { result ->
|
||||||
|
GetBearerTokenFingerprint.also { it.resolve(context, result.classDef) }.result?.mutableMethod?.apply {
|
||||||
|
val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8))
|
||||||
|
addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const-string v0, "Basic $auth"
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
} ?: return PatchResultError("Could not find required method to patch.")
|
||||||
|
}?.let {
|
||||||
|
val occurrenceIndex = it.scanResult.stringsScanResult!!.matches.first().index
|
||||||
|
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val authorizationStringInstruction = getInstruction<ReferenceInstruction>(occurrenceIndex)
|
||||||
|
val targetRegister = (authorizationStringInstruction as OneRegisterInstruction).registerA
|
||||||
|
val reference = authorizationStringInstruction.reference as StringReference
|
||||||
|
|
||||||
|
val newAuthorizationUrl = reference.string.replace(
|
||||||
|
"client_id=.*?&".toRegex(),
|
||||||
|
"client_id=${clientId!!}&"
|
||||||
|
)
|
||||||
|
|
||||||
|
replaceInstruction(
|
||||||
|
occurrenceIndex,
|
||||||
|
"const-string v$targetRegister, \"$newAuthorizationUrl\""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: return PatchResultError("Could not find required method to patch.")
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : OptionsContainer() {
|
||||||
|
var clientId by option(
|
||||||
|
PatchOption.StringOption(
|
||||||
|
"client-id",
|
||||||
|
null,
|
||||||
|
"OAuth client ID",
|
||||||
|
"The client ID to use for OAuth."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user