Skip to content

Commit 95dddfa

Browse files
TinyHaiyujincheng08
authored andcommitted
fix: auto like
1 parent 8b6431c commit 95dddfa

File tree

4 files changed

+176
-104
lines changed

4 files changed

+176
-104
lines changed

app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex
4949
val rxGeneralResponseClass by Weak { "com.bilibili.okretro.call.rxjava.RxGeneralResponse" from mClassLoader }
5050
val fastJsonClass by Weak { mHookInfo.fastJson.class_ from mClassLoader }
5151
val bangumiUniformSeasonClass by Weak { mHookInfo.bangumiSeason from mClassLoader }
52-
val sectionClass by Weak { mHookInfo.section.class_ from mClassLoader }
53-
val viewHolderClass by Weak { mHookInfo.section.viewHolder from mClassLoader }
52+
val kingPositionComponentClass by Weak { mHookInfo.autoLike.kingPositionComponent.class_ from mClassLoader }
53+
val storyAbsControllerClass by Weak { mHookInfo.autoLike.storyAbsController.class_ from mClassLoader }
54+
val storyDetailClass by Weak { "com.bilibili.video.story.StoryDetail" from mClassLoader }
5455
val retrofitResponseClass by Weak { mHookInfo.retrofitResponse from mClassLoader }
5556
val themeHelperClass by Weak { mHookInfo.themeHelper.class_ from mClassLoader }
5657
val themeIdHelperClass by Weak { mHookInfo.themeIdHelper.class_ from mClassLoader }
@@ -237,11 +238,11 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex
237238

238239
fun requestField() = mHookInfo.okHttp.response.request.orNull
239240

240-
fun likeMethod() = mHookInfo.section.like.orNull
241+
fun componentMapField() = mHookInfo.autoLike.kingPositionComponent.componentMap.orNull
241242

242-
fun bindViewMethod() = mHookInfo.section.bindView.orNull
243+
fun setMDataMethod() = mHookInfo.autoLike.storyAbsController.setMData.orNull
243244

244-
fun getRootMethod() = mHookInfo.section.getRoot.orNull
245+
fun getMPlayerMethod() = mHookInfo.autoLike.storyAbsController.getMPlayer.orNull
245246

246247
fun themeName() = mHookInfo.themeName.field.orNull
247248

@@ -852,68 +853,74 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex
852853
}?.name ?: return@field
853854
}
854855
}
855-
section = section {
856-
val kingPositionComponentClass = dexHelper.findMethodUsingString(
857-
"LikeClicked(view=",
858-
false,
859-
-1,
860-
-1,
861-
null,
862-
-1,
863-
null,
864-
null,
865-
null,
866-
true
867-
).asSequence().firstNotNullOfOrNull {
868-
dexHelper.decodeMethodIndex(it)?.declaringClass?.run {
869-
var outerClass = this
870-
while (outerClass.declaringClass != null) {
871-
outerClass = outerClass.declaringClass!!
856+
autoLike = autoLike {
857+
kingPositionComponent = kingPositionComponent {
858+
val kingPositionComponentClass = dexHelper.findMethodUsingString(
859+
"LikeClicked(view=",
860+
false,
861+
-1,
862+
-1,
863+
null,
864+
-1,
865+
null,
866+
null,
867+
null,
868+
true
869+
).asSequence().firstNotNullOfOrNull {
870+
dexHelper.decodeMethodIndex(it)?.declaringClass?.run {
871+
var outerClass = this
872+
while (outerClass.declaringClass != null) {
873+
outerClass = outerClass.declaringClass!!
874+
}
875+
outerClass
872876
}
873-
outerClass
874-
}
875-
}
876-
val genericInterface = kingPositionComponentClass?.genericInterfaces?.getOrNull(0)
877-
val parameterType = (genericInterface as? ParameterizedType)?.actualTypeArguments?.getOrNull(0)
878-
val viewHolderClass = if (parameterType is ParameterizedType) {
879-
parameterType.rawType
880-
} else {
881-
parameterType
882-
}?.let { it as? Class<*> }
883-
val getRootMethod = viewHolderClass?.declaredMethods?.firstOrNull {
884-
it.returnType == View::class.java && it.parameterCount == 0
885-
}
886-
val bindViewMethod = kingPositionComponentClass?.declaredMethods?.firstOrNull {
887-
it.isPublic && it.parameterCount == 2
888-
&& it.parameterTypes[0] == viewHolderClass
889-
}
890-
if (kingPositionComponentClass != null && viewHolderClass != null && bindViewMethod != null && getRootMethod != null) {
877+
} ?: return@kingPositionComponent
878+
val componentMapField = kingPositionComponentClass.declaredFields.firstOrNull {
879+
it.type == Map::class.java
880+
} ?: return@kingPositionComponent
891881
class_ = class_ { name = kingPositionComponentClass.name }
892-
viewHolder = class_ { name = viewHolderClass.name }
893-
bindView = method { name = bindViewMethod.name }
894-
getRoot = method { name = getRootMethod.name }
895-
return@section
882+
componentMap = field { name = componentMapField.name }
883+
}
884+
storyAbsController = storyAbsController {
885+
val storyAbsControllerClass = dexHelper.findMethodUsingString(
886+
"notify player state fail",
887+
false,
888+
-1,
889+
-1,
890+
null,
891+
-1,
892+
null,
893+
null,
894+
null,
895+
true
896+
).asSequence().mapNotNull {
897+
dexHelper.decodeMethodIndex(it)?.declaringClass
898+
}.firstOrNull() ?: return@storyAbsController
899+
val storyPagerPlayerInterface = dexHelper.findMethodUsingString(
900+
" play item but is inactivate",
901+
false,
902+
-1,
903+
-1,
904+
null,
905+
-1,
906+
null,
907+
null,
908+
null,
909+
true
910+
).asSequence().mapNotNull {
911+
dexHelper.decodeMethodIndex(it)?.declaringClass?.interfaces?.getOrNull(0)
912+
}.firstOrNull() ?: return@storyAbsController
913+
val storyDetailClass = "com.bilibili.video.story.StoryDetail".from(classloader)
914+
val setMDataMethod = storyAbsControllerClass.declaredMethods.firstOrNull {
915+
it.returnType == Void.TYPE && it.parameterCount == 1 && it.parameterTypes[0] == storyDetailClass
916+
} ?: return@storyAbsController
917+
val getMPlayerMethod = storyAbsControllerClass.declaredMethods.firstOrNull {
918+
storyPagerPlayerInterface.isAssignableFrom(it.returnType) && it.parameterCount == 0
919+
} ?: return@storyAbsController
920+
class_ = class_ { name = storyAbsControllerClass.name }
921+
setMData = method { name = setMDataMethod.name }
922+
getMPlayer = method { name = getMPlayerMethod.name }
896923
}
897-
898-
val sectionClass = dexHelper.findMethodUsingString(
899-
"ActionViewHolder",
900-
false,
901-
-1,
902-
-1,
903-
null,
904-
-1,
905-
null,
906-
null,
907-
null,
908-
true
909-
).firstOrNull()?.let {
910-
dexHelper.decodeMethodIndex(it)
911-
}?.declaringClass ?: return@section
912-
val likeMethod = sectionClass?.superclass?.declaredMethods?.find {
913-
it.parameterTypes.size == 1 && it.returnType == Void.TYPE && !it.isFinal
914-
} ?: return@section
915-
class_ = class_ { name = sectionClass.name }
916-
like = method { name = likeMethod.name }
917924
}
918925
signQuery = signQuery {
919926
val signedQueryClass =

app/src/main/java/me/iacn/biliroaming/hook/AutoLikeHook.kt

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package me.iacn.biliroaming.hook
22

33
import android.view.View
4+
import android.widget.LinearLayout
5+
import de.robv.android.xposed.XC_MethodHook.Unhook
46
import me.iacn.biliroaming.BiliBiliPackage.Companion.instance
57
import me.iacn.biliroaming.utils.*
68

@@ -11,47 +13,98 @@ class AutoLikeHook(classLoader: ClassLoader) : BaseHook(classLoader) {
1113
var detail: Pair<Long, Int>? = null
1214
}
1315

16+
private val likeIds by lazy {
17+
arrayOf(
18+
"frame_like",
19+
"like_layout"
20+
).map { getId(it) }
21+
}
22+
1423
override fun startHook() {
1524
if (!sPrefs.getBoolean("auto_like", false)) return
1625

1726
Log.d("startHook: AutoLike")
1827

19-
val likeIds = arrayOf(
20-
"frame_recommend",
21-
"frame1",
22-
"frame_like"
23-
).map { getId(it) }
28+
val unhookSet = arrayOf<Set<Unhook>?>(null)
29+
unhookSet[0] = instance.kingPositionComponentClass?.hookAfterAllConstructors { param ->
30+
val kingPositionComponent = param.thisObject
31+
val componentMap =
32+
kingPositionComponent.getObjectFieldAs<Map<*, *>>(instance.componentMapField())
33+
34+
val likeComponentClass = componentMap.keys.filterNotNull().firstNotNullOfOrNull {
35+
val componentClass = it.javaClass
36+
componentClass.declaredFields.firstOrNull {
37+
it.type == Runnable::class.java
38+
} ?: return@firstNotNullOfOrNull null
39+
componentClass
40+
} ?: return@hookAfterAllConstructors
41+
42+
val rebindView = likeComponentClass.declaredMethods.firstOrNull {
43+
it.returnType == Void.TYPE && it.parameterCount == 5
44+
&& it.parameterTypes[0] == likeComponentClass
45+
&& it.parameterTypes[4] == LinearLayout::class.java
46+
} ?: return@hookAfterAllConstructors
47+
48+
rebindView.hookAfterMethod { p ->
49+
performLike(p.args[4] as View)
50+
}
51+
52+
unhookSet[0]?.forEach { it.unhook() }
53+
}
2454

25-
instance.likeMethod()?.let { likeMethod ->
26-
instance.sectionClass?.hookAfterAllMethods(likeMethod) { param ->
27-
val sec = param.thisObject ?: return@hookAfterAllMethods
28-
if (!shouldClickLike()) {
29-
return@hookAfterAllMethods
30-
}
31-
val likeView = sec.javaClass.declaredFields.filter {
32-
View::class.java.isAssignableFrom(it.type)
33-
}.firstNotNullOfOrNull {
34-
sec.getObjectFieldOrNullAs<View>(it.name)?.takeIf { v ->
35-
v.id in likeIds
36-
}
37-
}
38-
likeView?.callOnClick()
55+
// 番剧分集切换
56+
instance.viewUniteMossClass?.hookBeforeMethod(
57+
"arcRefresh",
58+
"com.bapis.bilibili.app.viewunite.v1.ArcRefreshReq",
59+
instance.mossResponseHandlerClass
60+
) { param ->
61+
val req = param.args[0]
62+
val aid = req.callMethodAs("getAid")
63+
?: req.callMethodAs<String?>("getBvid")?.let { bv2av(it) }
64+
if (aid == null || aid <= 0L) {
65+
return@hookBeforeMethod
3966
}
67+
param.args[1] = param.args[1].mossResponseHandlerReplaceProxy { reply ->
68+
reply ?: return@mossResponseHandlerReplaceProxy null
69+
val like = reply.callMethod("getReqUser")?.callMethodAs<Int?>("getLike")
70+
?: return@mossResponseHandlerReplaceProxy null
71+
detail = aid to like
72+
null
73+
}
74+
}
75+
76+
// 竖屏模式
77+
instance.storyAbsControllerClass?.hookAfterMethod(
78+
instance.setMDataMethod(),
79+
instance.storyDetailClass
80+
) { param ->
81+
if (param.thisObject.callMethod(instance.getMPlayerMethod()) == null) {
82+
return@hookAfterMethod
83+
}
84+
val storyDetail = instance.fastJsonClass?.callStaticMethodAs<String>(
85+
"toJSONString",
86+
param.args[0]
87+
).toJSONObject()
88+
val playerArgs = storyDetail.optJSONObject("player_args")
89+
val aid = playerArgs?.optLong("aid")
90+
val reqUser = storyDetail.optJSONObject("req_user")
91+
val like = reqUser?.optBoolean("like")
92+
if (aid == null || like == null) {
93+
return@hookAfterMethod
94+
}
95+
detail = aid to if (like) 1 else 0
96+
97+
performLike(param.thisObject as View)
98+
}
99+
}
100+
101+
private fun performLike(root: View) {
102+
val likeView = likeIds.firstNotNullOfOrNull {
103+
root.findViewById(it)
40104
}
41-
instance.bindViewMethod()?.let { bindViewMethod ->
42-
instance.sectionClass?.hookAfterMethod(
43-
bindViewMethod,
44-
instance.viewHolderClass,
45-
instance.continuationClass
46-
) { param ->
47-
if (!shouldClickLike()) {
48-
return@hookAfterMethod
49-
}
50-
val root = param.args[0].callMethodAs<View>(instance.getRootMethod())
51-
val likeView = likeIds.firstNotNullOfOrNull { id ->
52-
root.findViewById(id)
53-
}
54-
likeView?.callOnClick()
105+
likeView?.post {
106+
if (shouldClickLike()) {
107+
likeView.callOnClick()
55108
}
56109
}
57110
}

app/src/main/java/me/iacn/biliroaming/hook/ProtoBufHook.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,11 @@ class ProtoBufHook(classLoader: ClassLoader) : BaseHook(classLoader) {
376376
?.callMethodAs("getAid") ?: -1L
377377
val like = viewReply.callMethod("getReqUser")
378378
?.callMethodAs("getLike") ?: -1
379-
AutoLikeHook.detail = aid to like
379+
380+
if (aid > 0 && like != -1) {
381+
AutoLikeHook.detail = aid to like
382+
}
383+
380384
BangumiPlayUrlHook.qnApplied.set(false)
381385

382386
// 视频详情页荣誉卡片

app/src/main/proto/me/iacn/biliroaming/configs.proto

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,20 @@ message SignQuery {
159159
optional Method method = 2;
160160
}
161161

162-
message Section {
162+
message AutoLike {
163+
optional KingPositionComponent kingPositionComponent = 1;
164+
optional StoryAbsController storyAbsController = 2;
165+
}
166+
167+
message KingPositionComponent {
168+
optional Class class = 1;
169+
optional Field componentMap = 2;
170+
}
171+
172+
message StoryAbsController {
163173
optional Class class = 1;
164-
optional Method like = 2;
165-
optional Class view_holder = 3;
166-
optional Method bind_view = 4;
167-
optional Method get_root = 5;
174+
optional Method setMData = 2;
175+
optional Method getMPlayer = 3;
168176
}
169177

170178
message Drawer {
@@ -330,7 +338,7 @@ message HookInfo {
330338
optional ThemeProcessor theme_processor = 20;
331339
optional Class theme_list_click = 22;
332340
optional ThemeName theme_name = 24;
333-
optional Section section = 25;
341+
optional AutoLike autoLike = 25;
334342
optional SignQuery sign_query = 27;
335343
optional Class general_response = 28;
336344
optional Drawer drawer = 30;

0 commit comments

Comments
 (0)