mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-10-19 03:50:53 +02:00
fix(twitter): make hide-ads
patch compatible with any version
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
f1e6cbcdf1
commit
665598836a
@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.json
|
||||||
|
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
abstract class BaseJsonHook : JsonHook {
|
||||||
|
abstract fun apply(json: JSONObject)
|
||||||
|
|
||||||
|
override fun transform(json: JSONObject) = json.apply { apply(json) }
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.json
|
||||||
|
|
||||||
|
import app.revanced.twitter.patches.hook.patch.Hook
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
interface JsonHook : Hook<JSONObject> {
|
||||||
|
/**
|
||||||
|
* Transform a JSONObject.
|
||||||
|
*
|
||||||
|
* @param json The JSONObject.
|
||||||
|
*/
|
||||||
|
fun transform(json: JSONObject): JSONObject
|
||||||
|
|
||||||
|
override fun hook(type: JSONObject) = transform(type)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.json
|
||||||
|
|
||||||
|
import app.revanced.twitter.utils.json.JsonUtils.parseJson
|
||||||
|
import app.revanced.twitter.utils.stream.StreamUtils
|
||||||
|
import org.json.JSONException
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
object JsonHookPatch {
|
||||||
|
private val hooks = buildList<JsonHook> {
|
||||||
|
// Modified by corresponding patch.
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parseJsonHook(jsonInputStream: InputStream): InputStream {
|
||||||
|
var jsonObject = try {
|
||||||
|
parseJson(jsonInputStream)
|
||||||
|
} catch (ignored: IOException) {
|
||||||
|
return jsonInputStream // Unreachable.
|
||||||
|
} catch (ignored: JSONException) {
|
||||||
|
return jsonInputStream
|
||||||
|
}
|
||||||
|
|
||||||
|
for (hook in hooks) jsonObject = hook.hook(jsonObject)
|
||||||
|
|
||||||
|
return StreamUtils.fromString(jsonObject.toString())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.patch
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
|
||||||
|
interface Hook<T> {
|
||||||
|
/**
|
||||||
|
* Hook the given type.
|
||||||
|
* @param type The type to hook
|
||||||
|
*/
|
||||||
|
fun hook(@NonNull type: T): T
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.patch.ads
|
||||||
|
|
||||||
|
import app.revanced.twitter.patches.hook.json.BaseJsonHook
|
||||||
|
import app.revanced.twitter.patches.hook.twifucker.TwiFucker
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
|
||||||
|
object AdsHook : BaseJsonHook() {
|
||||||
|
/**
|
||||||
|
* Strips JSONObject from promoted ads.
|
||||||
|
*
|
||||||
|
* @param json The JSONObject.
|
||||||
|
*/
|
||||||
|
override fun apply(json: JSONObject) = TwiFucker.hidePromotedAds(json)
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.twifucker
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import app.revanced.twitter.patches.hook.twifucker.TwiFuckerUtils.forEach
|
||||||
|
import app.revanced.twitter.patches.hook.twifucker.TwiFuckerUtils.forEachIndexed
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
// https://raw.githubusercontent.com/Dr-TSNG/TwiFucker/880cdf1c1622e54ab45561ffcb4f53d94ed97bae/app/src/main/java/icu/nullptr/twifucker/hook/JsonHook.kt
|
||||||
|
internal object TwiFucker {
|
||||||
|
// root
|
||||||
|
private fun JSONObject.jsonGetInstructions(): JSONArray? =
|
||||||
|
optJSONObject("timeline")?.optJSONArray("instructions")
|
||||||
|
|
||||||
|
private fun JSONObject.jsonGetData(): JSONObject? = optJSONObject("data")
|
||||||
|
|
||||||
|
private fun JSONObject.jsonHasRecommendedUsers(): Boolean = has("recommended_users")
|
||||||
|
|
||||||
|
private fun JSONObject.jsonRemoveRecommendedUsers() {
|
||||||
|
remove("recommended_users")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.jsonCheckAndRemoveRecommendedUsers() {
|
||||||
|
if (jsonHasRecommendedUsers()) {
|
||||||
|
Log.d("revanced", "Handle recommended users: $this")
|
||||||
|
jsonRemoveRecommendedUsers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.jsonHasThreads(): Boolean = has("threads")
|
||||||
|
|
||||||
|
private fun JSONObject.jsonRemoveThreads() {
|
||||||
|
remove("threads")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.jsonCheckAndRemoveThreads() {
|
||||||
|
if (jsonHasThreads()) {
|
||||||
|
Log.d("revabced", "Handle threads: $this")
|
||||||
|
jsonRemoveThreads()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// data
|
||||||
|
private fun JSONObject.dataGetInstructions(): JSONArray? {
|
||||||
|
val timeline = optJSONObject("user_result")?.optJSONObject("result")
|
||||||
|
?.optJSONObject("timeline_response")?.optJSONObject("timeline")
|
||||||
|
?: optJSONObject("timeline_response")?.optJSONObject("timeline")
|
||||||
|
?: optJSONObject("timeline_response")
|
||||||
|
return timeline?.optJSONArray("instructions")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.dataCheckAndRemove() {
|
||||||
|
dataGetInstructions()?.forEach { instruction ->
|
||||||
|
instruction.instructionCheckAndRemove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.dataGetLegacy(): JSONObject? =
|
||||||
|
optJSONObject("tweet_result")?.optJSONObject("result")?.let {
|
||||||
|
if (it.has("tweet")) {
|
||||||
|
it.optJSONObject("tweet")
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}?.optJSONObject("legacy")
|
||||||
|
|
||||||
|
|
||||||
|
// entry
|
||||||
|
private fun JSONObject.entryHasPromotedMetadata(): Boolean =
|
||||||
|
optJSONObject("content")?.optJSONObject("item")?.optJSONObject("content")
|
||||||
|
?.optJSONObject("tweet")
|
||||||
|
?.has("promotedMetadata") == true || optJSONObject("content")?.optJSONObject("content")
|
||||||
|
?.has("tweetPromotedMetadata") == true || optJSONObject("item")?.optJSONObject("content")
|
||||||
|
?.has("tweetPromotedMetadata") == true
|
||||||
|
|
||||||
|
private fun JSONObject.entryGetContentItems(): JSONArray? =
|
||||||
|
optJSONObject("content")?.optJSONArray("items")
|
||||||
|
?: optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items")
|
||||||
|
|
||||||
|
private fun JSONObject.entryIsTweetDetailRelatedTweets(): Boolean =
|
||||||
|
optString("entryId").startsWith("tweetdetailrelatedtweets-")
|
||||||
|
|
||||||
|
private fun JSONObject.entryGetTrends(): JSONArray? =
|
||||||
|
optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items")
|
||||||
|
|
||||||
|
// trend
|
||||||
|
private fun JSONObject.trendHasPromotedMetadata(): Boolean =
|
||||||
|
optJSONObject("item")?.optJSONObject("content")?.optJSONObject("trend")
|
||||||
|
?.has("promotedMetadata") == true
|
||||||
|
|
||||||
|
private fun JSONArray.trendRemoveAds() {
|
||||||
|
val trendRemoveIndex = mutableListOf<Int>()
|
||||||
|
forEachIndexed { trendIndex, trend ->
|
||||||
|
if (trend.trendHasPromotedMetadata()) {
|
||||||
|
Log.d("revanced", "Handle trends ads $trendIndex $trend")
|
||||||
|
trendRemoveIndex.add(trendIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in trendRemoveIndex.asReversed()) {
|
||||||
|
remove(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
private fun JSONObject.instructionTimelineAddEntries(): JSONArray? = optJSONArray("entries")
|
||||||
|
|
||||||
|
private fun JSONObject.instructionGetAddEntries(): JSONArray? =
|
||||||
|
optJSONObject("addEntries")?.optJSONArray("entries")
|
||||||
|
|
||||||
|
private fun JSONObject.instructionCheckAndRemove() {
|
||||||
|
instructionTimelineAddEntries()?.entriesRemoveAnnoyance()
|
||||||
|
instructionGetAddEntries()?.entriesRemoveAnnoyance()
|
||||||
|
}
|
||||||
|
|
||||||
|
// entries
|
||||||
|
private fun JSONArray.entriesRemoveTimelineAds() {
|
||||||
|
val removeIndex = mutableListOf<Int>()
|
||||||
|
forEachIndexed { entryIndex, entry ->
|
||||||
|
entry.entryGetTrends()?.trendRemoveAds()
|
||||||
|
|
||||||
|
if (entry.entryHasPromotedMetadata()) {
|
||||||
|
Log.d("revanced", "Handle timeline ads $entryIndex $entry")
|
||||||
|
removeIndex.add(entryIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
val innerRemoveIndex = mutableListOf<Int>()
|
||||||
|
val contentItems = entry.entryGetContentItems()
|
||||||
|
contentItems?.forEachIndexed inner@{ itemIndex, item ->
|
||||||
|
if (item.entryHasPromotedMetadata()) {
|
||||||
|
Log.d("revanced", "Handle timeline replies ads $entryIndex $entry")
|
||||||
|
if (contentItems.length() == 1) {
|
||||||
|
removeIndex.add(entryIndex)
|
||||||
|
} else {
|
||||||
|
innerRemoveIndex.add(itemIndex)
|
||||||
|
}
|
||||||
|
return@inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in innerRemoveIndex.asReversed()) {
|
||||||
|
contentItems?.remove(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in removeIndex.reversed()) {
|
||||||
|
remove(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONArray.entriesRemoveTweetDetailRelatedTweets() {
|
||||||
|
val removeIndex = mutableListOf<Int>()
|
||||||
|
forEachIndexed { entryIndex, entry ->
|
||||||
|
|
||||||
|
if (entry.entryIsTweetDetailRelatedTweets()) {
|
||||||
|
Log.d("revanced", "Handle tweet detail related tweets $entryIndex $entry")
|
||||||
|
removeIndex.add(entryIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in removeIndex.reversed()) {
|
||||||
|
remove(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONArray.entriesRemoveAnnoyance() {
|
||||||
|
entriesRemoveTimelineAds()
|
||||||
|
entriesRemoveTweetDetailRelatedTweets()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideRecommendedUsers(json: JSONObject) {
|
||||||
|
json.jsonCheckAndRemoveRecommendedUsers()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hidePromotedAds(json: JSONObject) {
|
||||||
|
json.jsonGetInstructions()?.forEach { instruction ->
|
||||||
|
instruction.instructionCheckAndRemove()
|
||||||
|
}
|
||||||
|
json.jsonGetData()?.dataCheckAndRemove()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.twitter.patches.hook.twifucker
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
internal object TwiFuckerUtils {
|
||||||
|
inline fun JSONArray.forEach(action: (JSONObject) -> Unit) {
|
||||||
|
(0 until this.length()).forEach { i ->
|
||||||
|
if (this[i] is JSONObject) {
|
||||||
|
action(this[i] as JSONObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun JSONArray.forEachIndexed(action: (index: Int, JSONObject) -> Unit) {
|
||||||
|
(0 until this.length()).forEach { i ->
|
||||||
|
if (this[i] is JSONObject) {
|
||||||
|
action(i, this[i] as JSONObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.twitter.utils.json
|
||||||
|
|
||||||
|
import app.revanced.twitter.utils.stream.StreamUtils
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
object JsonUtils {
|
||||||
|
@JvmStatic
|
||||||
|
@Throws(IOException::class, JSONException::class)
|
||||||
|
fun parseJson(jsonInputStream: InputStream) = JSONObject(StreamUtils.toString(jsonInputStream))
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.twitter.utils.stream
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
object StreamUtils {
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun toString(inputStream: InputStream): String {
|
||||||
|
ByteArrayOutputStream().use { result ->
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var length: Int
|
||||||
|
while (inputStream.read(buffer).also { length = it } != -1) {
|
||||||
|
result.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
return result.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromString(string: String): InputStream {
|
||||||
|
return ByteArrayInputStream(string.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user