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