mirror of
https://github.com/MoonLeeeaf/FuckMaoNemo.git
synced 2025-06-06 11:23:33 +08:00
Compare commits
9 Commits
525f842bea
...
v1.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54117f7887 | ||
|
|
7048c6be58 | ||
|
|
fc4e947691 | ||
|
|
e6370772d7 | ||
|
|
4dfc881c4a | ||
|
|
720d56bc89 | ||
|
|
3a57d0b23f | ||
|
|
59c457675d | ||
|
|
0d6e388ea8 |
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"allFiles": [
|
||||
{
|
||||
"file": "/storage/emulated/0/MoonLeaf/Projects/FuckMaoNemo/app/src/main/res/xml/config.xml",
|
||||
"selection": {
|
||||
"end": {
|
||||
"column": 83,
|
||||
"index": 3436,
|
||||
"line": 97
|
||||
},
|
||||
"start": {
|
||||
"column": 83,
|
||||
"index": 3436,
|
||||
"line": 97
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "/storage/emulated/0/MoonLeaf/Projects/FuckMaoNemo/app/src/main/java/io/github/moonleeeaf/fuckmaonemo/Hook.java",
|
||||
"selection": {
|
||||
"end": {
|
||||
"column": 58,
|
||||
"index": 8459,
|
||||
"line": 207
|
||||
},
|
||||
"start": {
|
||||
"column": 58,
|
||||
"index": 8459,
|
||||
"line": 207
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "/storage/emulated/0/MoonLeaf/Projects/FuckMaoNemo/app/build.gradle",
|
||||
"selection": {
|
||||
"end": {
|
||||
"column": 22,
|
||||
"index": 272,
|
||||
"line": 14
|
||||
},
|
||||
"start": {
|
||||
"column": 22,
|
||||
"index": 272,
|
||||
"line": 14
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selectedFile": "/storage/emulated/0/MoonLeaf/Projects/FuckMaoNemo/app/build.gradle"
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
.androidide
|
||||
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
|
||||
@@ -12,8 +12,8 @@ android {
|
||||
applicationId "io.github.moonleeeaf.fuckmaonemo"
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
versionCode 15000
|
||||
versionName "1.5.0"
|
||||
versionCode 18000
|
||||
versionName "1.8.0"
|
||||
|
||||
vectorDrawables {
|
||||
useSupportLibrary true
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
@@ -43,6 +44,23 @@ public class ConfigActivity extends PreferenceActivity {
|
||||
return false;
|
||||
});
|
||||
|
||||
findPreference("set_newest_works_filter").setOnPreferenceClickListener((p) -> {
|
||||
EditText edit = new EditText(this);
|
||||
|
||||
edit.setText(getPreferenceManager().getSharedPreferences().getString("newest_works_filter_rule_shared", "userId 823651139"));
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("最新作品过滤规则")
|
||||
.setView(edit)
|
||||
.setPositiveButton("保存", (d, w) -> {
|
||||
getPreferenceManager().getSharedPreferences().edit().putString("newest_works_filter_rule_shared", edit.getText().toString()).apply();
|
||||
})
|
||||
.setNegativeButton("取消", (d, w) -> {})
|
||||
.show();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
FileOutputStream fos = openFileOutput("fuck_miao.txt", MODE_WORLD_READABLE);
|
||||
String s = new String(getAssets().open("屏蔽词.txt").readAllBytes());
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package io.github.moonleeeaf.fuckmaonemo;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Application;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.loader.AssetsProvider;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.util.Pair;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.webkit.CookieManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.Toast;
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
@@ -18,13 +25,16 @@ import de.robv.android.xposed.XSharedPreferences;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Proxy;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Hook implements IXposedHookLoadPackage {
|
||||
private static boolean isHooked = false;
|
||||
@@ -98,6 +108,61 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
|
||||
XposedBridge.log("[FuckMaoNemo] 注入中...");
|
||||
|
||||
// 劫持设置按钮的点击事件
|
||||
load("inject_settings_button", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] Hook_注入设置菜单");
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("com.codemao.nemo.fragment.MineFragment", classLoader),
|
||||
"onClick",
|
||||
View.class
|
||||
),
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
View v = (View) mp.args[0];
|
||||
if (v.getId() == 2131297099)
|
||||
v.setId(11451419);
|
||||
}
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
View v = (View) mp.args[0];
|
||||
Context c = v.getContext();
|
||||
if (v.getId() == 11451419) {
|
||||
PopupMenu pop = new PopupMenu(c, v);
|
||||
Menu m = pop.getMenu();
|
||||
m.add("Nemo设置").setOnMenuItemClickListener((mm) -> {
|
||||
c.startActivity(new Intent(c, XposedHelpers.findClass("com.codemao.nemo.activity.SettingActivity", classLoader)));
|
||||
return false;
|
||||
});
|
||||
m.add("模块设置").setOnMenuItemClickListener((mm) -> {
|
||||
c.startActivity(new Intent().setComponent(new ComponentName("io.github.moonleeeaf.fuckmaonemo", ConfigActivity.class.getName())));
|
||||
return false;
|
||||
});
|
||||
m.add("打开内置浏览器").setOnMenuItemClickListener((mm) -> {
|
||||
EditText edit = new EditText(c);
|
||||
new AlertDialog.Builder(c)
|
||||
.setTitle("打开内置浏览器")
|
||||
.setIcon(android.R.drawable.ic_dialog_info)
|
||||
.setView(edit)
|
||||
.setPositiveButton("打开", (d, w) -> {
|
||||
c.startActivity(new Intent().setData(Uri.parse("nemo://com.codemao.nemo/openwith?type=5&url=" + edit.getText())));
|
||||
})
|
||||
.setNegativeButton("取消", (d, w) -> {})
|
||||
.create()
|
||||
.show();
|
||||
|
||||
return false;
|
||||
});
|
||||
pop.show();
|
||||
|
||||
mp.setResult(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}, true);
|
||||
|
||||
// 拦截40x码
|
||||
load("fuck_40x", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] Hook_拦截40x码");
|
||||
@@ -133,6 +198,43 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
);
|
||||
});
|
||||
|
||||
// 劫持分享菜单
|
||||
load("long_press_share_work_to_open_more_menu", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] 长按作品分享更多菜单");
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("com.codemao.nemo.activity.WorkDetailActivity", classLoader),
|
||||
"onCreate",
|
||||
Bundle.class
|
||||
),
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
Activity self = (Activity) mp.thisObject;
|
||||
View v = self.findViewById(
|
||||
XposedHelpers.getStaticIntField(
|
||||
XposedHelpers.findClass("com.codemao.nemo.R$id", classLoader),
|
||||
"ll_share"
|
||||
)
|
||||
);
|
||||
|
||||
long workId = XposedHelpers.getLongField(self, "workId");
|
||||
|
||||
v.setOnLongClickListener((_v) -> {
|
||||
PopupMenu pop = new PopupMenu(self, v);
|
||||
Menu m = pop.getMenu();
|
||||
m.add("获取分享口令").setOnMenuItemClickListener((mm) -> {
|
||||
openWorkShareCodeGetter(workId);
|
||||
return false;
|
||||
});
|
||||
pop.show();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
load("test", () -> {
|
||||
methodToVoid(
|
||||
getMethod(
|
||||
@@ -153,6 +255,93 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
));
|
||||
});
|
||||
|
||||
// 最新作品过滤
|
||||
load("newest_works_filter", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] Hook_最新作品过滤");
|
||||
|
||||
final String[] rules = xsp.getString("newest_works_filter_rule_shared", "userId 823651139").split("\n");
|
||||
|
||||
final ArrayList<NewestWorksFilter> filters = new ArrayList<NewestWorksFilter>();
|
||||
|
||||
for (String s : rules) {
|
||||
String type = s.split(" ")[0];
|
||||
String value = s.split(" ")[1];
|
||||
|
||||
NewestWorksFilter nwf = new NewestWorksFilter(type, value);
|
||||
|
||||
filters.add(nwf);
|
||||
}
|
||||
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("com.codemao.nemo.bean.LatestWorks", classLoader),
|
||||
"getLimit",
|
||||
null //List.class
|
||||
),
|
||||
new XC_MethodReplacement() {
|
||||
@Override
|
||||
protected Object replaceHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("com.codemao.nemo.bean.LatestWorks", classLoader),
|
||||
"getItems",
|
||||
null //List.class
|
||||
),
|
||||
new XC_MethodReplacement() {
|
||||
@Override
|
||||
protected Object replaceHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
List ls = (List) XposedHelpers.getObjectField(mp.thisObject, "items"); // mp.args[0]; //
|
||||
|
||||
ArrayList al = new ArrayList();
|
||||
|
||||
int passed = 0;
|
||||
|
||||
for (Object o : ls) {
|
||||
String workName = (String) XposedHelpers.getObjectField(o, "work_name");
|
||||
String userId = "" + XposedHelpers.getLongField(o, "user_id");
|
||||
|
||||
boolean disadd = false;
|
||||
|
||||
for (NewestWorksFilter filter : filters) {
|
||||
if (filter.matches(userId, workName)) {
|
||||
disadd = true;
|
||||
passed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (disadd) {
|
||||
XposedBridge.log("[FuckMaoNemo] 过滤用户 " + userId + " 的作品 " + workName);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
al.add(o);
|
||||
}
|
||||
|
||||
// XposedHelpers.setObjectField(mp.thisObject, "items", al);
|
||||
|
||||
int offset = XposedHelpers.getIntField(mp.thisObject, "offset");
|
||||
XposedHelpers.setIntField(mp.thisObject, "offset", offset + passed);
|
||||
|
||||
int total = XposedHelpers.getIntField(mp.thisObject, "total");
|
||||
// XposedHelpers.setIntField(mp.thisObject, "total", total - passed);
|
||||
|
||||
XposedBridge.log("[FuckMaoNemo] 过滤完毕 原数据: offset=" + offset + " total=" + total);
|
||||
|
||||
return al;
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// 修复KN作品播放
|
||||
load("fix_kn_player", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] Hook_修复KN作品播放");
|
||||
@@ -233,19 +422,64 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
load("fuck_box3recommend", () -> {
|
||||
XposedBridge.log("[FuckMaoNemo] Hook_岛3我推荐你吗");
|
||||
|
||||
Object mgr = XposedHelpers.findClass("com.giu.xzz.http.RetrofitManager",classLoader).getDeclaredMethod("get", null).invoke(null, null);
|
||||
Object example = mgr.getClass().getDeclaredMethod("create", new Class[] { Class.class }).invoke(mgr, XposedHelpers.findClass("com.codemao.nemo.retrofit.api.DiscoveryService", classLoader));
|
||||
|
||||
XposedBridge.hookAllMethods(
|
||||
example.getClass(),
|
||||
"getRecommendBoxData",
|
||||
new XC_MethodReplacement() {
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("retrofit2.Retrofit$1", classLoader),
|
||||
"invoke",
|
||||
new Class[] {
|
||||
Object.class,
|
||||
Method.class,
|
||||
Object[].class
|
||||
}
|
||||
),
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected Object replaceHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
return XposedHelpers.findClass("io.reactivex.Observable", classLoader).getConstructor(null).newInstance(null);
|
||||
protected void beforeHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
switch (((Method) mp.args[1]).getName()) {
|
||||
case "getRecommendBoxData":
|
||||
mp.setResult(XposedHelpers.callMethod(mp.args[0], "getRecommendPageData", new Object[] {}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
XposedBridge.hookMethod(
|
||||
getMethod(
|
||||
XposedHelpers.findClass("com.giu.xzz.http.RetrofitManager",classLoader),
|
||||
"create",
|
||||
Class.class
|
||||
),
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam mp) throws Throwable {
|
||||
Object s = mp.getResult();
|
||||
|
||||
XposedBridge.hookAllMethods(
|
||||
s.getClass(),
|
||||
"getRecommendBoxData",
|
||||
new XC_MethodReplacement() {
|
||||
@Override
|
||||
protected Object replaceHookedMethod(MethodHookParam arg0) throws Throwable {
|
||||
InvocationHandler handler = new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return java.lang.reflect.Proxy.newProxyInstance(
|
||||
classLoader,
|
||||
new Class[] { XposedHelpers.findClass("io.reactivex.Observable", classLoader) },
|
||||
hanlder
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
*/
|
||||
});
|
||||
|
||||
// 强制显示再创作按钮
|
||||
@@ -409,7 +643,7 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
public interface Callback {
|
||||
public void onCallback() throws Exception;
|
||||
}
|
||||
|
||||
|
||||
public void methodToVoid(Method m) {
|
||||
XposedBridge.hookMethod(m, new XC_MethodReplacement() {
|
||||
@Override
|
||||
@@ -420,7 +654,11 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
}
|
||||
|
||||
public void load(String pref, Callback cb) {
|
||||
if (xsp.getBoolean(pref, false)) {
|
||||
load(pref, cb ,false);
|
||||
}
|
||||
|
||||
public void load(String pref, Callback cb, boolean def) {
|
||||
if (xsp.getBoolean(pref, def)) {
|
||||
try {
|
||||
cb.onCallback();
|
||||
nohengheng++;
|
||||
@@ -431,4 +669,43 @@ public class Hook implements IXposedHookLoadPackage {
|
||||
}
|
||||
}
|
||||
|
||||
public void openWorkShareCodeGetter(long workId) {
|
||||
try {
|
||||
Context self = getApplication();
|
||||
String cookie = CookieManager.getInstance().getCookie("https://api.codemao.cn");
|
||||
int a = cookie.indexOf(";", cookie.indexOf("Bearer "));
|
||||
String token = cookie.substring(cookie.indexOf("Bearer ") + "Bearer ".length(), a == -1 ? cookie.length() : a - 1);
|
||||
|
||||
// XposedBridge.log("cookie=" + cookie);
|
||||
// XposedBridge.log("token=" + token);
|
||||
self.startActivity(new Intent().setFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setData(Uri.parse("nemo://com.codemao.nemo/openwith?type=5&url=" +
|
||||
URLEncoder.encode("https://moonbcmtools.github.io/CodemaoNemoOneKeyBuildShareCode/?token=" + token + "&workId=" + workId, "utf-8")
|
||||
)));
|
||||
} catch(Exception e) {
|
||||
XposedBridge.log(e);
|
||||
// Toast.makeText(self, "[FuckMaoNemo] " + e, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NewestWorksFilter {
|
||||
private String type;
|
||||
private String value;
|
||||
|
||||
public NewestWorksFilter(String type, String value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean matches(String userId, String workName) {
|
||||
if ("workName".equals(this.type)) {
|
||||
return Pattern.matches(value, workName);
|
||||
} else if ("userId".equals(this.type)) {
|
||||
return userId.equals(value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,12 +8,22 @@
|
||||
|
||||
<Preference
|
||||
android:title="作者"
|
||||
android:summary="编程猫 @满月叶\nGitHub @MoonLeeeaf\n喵呜~" />
|
||||
android:summary="编程猫 @满月叶\nGitHub @MoonLeeeaf\nBiliBili @静之彼端\n喵呜~" />
|
||||
|
||||
<Preference
|
||||
android:title="提示"
|
||||
android:summary="目前只支持 LSPosed\n其他暂未测试\n更改功能后重启生效" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="force_enable"
|
||||
android:title="强制启用"
|
||||
android:title="非官方客户端"
|
||||
android:summary="如果你正在使用基于官方 Nemo 修改的客户端,那么可以打开此选项(记得配置模块作用域哦)" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="inject_settings_button"
|
||||
android:title="注入设置菜单"
|
||||
android:summary="字面意思"
|
||||
android:defaultValue="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
@@ -67,6 +77,21 @@
|
||||
android:key="fuck_fcm"
|
||||
android:title="绕过防沉迷"
|
||||
android:summary="滞空防沉迷检测方法的调用实现无伤速通破解防沉迷,无需 Player 链接,不支持叽叽猫砸进里面的岛3,不支持内嵌社区网页的防沉迷,那不属于我的能力范围" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="long_press_share_work_to_open_more_menu"
|
||||
android:title="万能的长按分享菜单"
|
||||
android:summary="功能如下:\n获取作品分享口令(有一定风险)" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="newest_works_filter"
|
||||
android:title="最新作品过滤"
|
||||
android:summary="发你吗个逼的广告这他妈谁看你那臭几把玩意 欢迎殴打我手机号 15675696451" />
|
||||
|
||||
<Preference
|
||||
android:key="set_newest_works_filter"
|
||||
android:title="最新作品过滤列表"
|
||||
android:summary="可以对上面功能进行配置 支持使用作品名称 用户ID等过滤" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="force_set_work_myown"
|
||||
|
||||
Reference in New Issue
Block a user