Skip to content

Commit

Permalink
Add Tasker automation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dev4Mod committed Jun 11, 2024
1 parent 4b3d6a5 commit 1e0e574
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.wmods.wppenhacer.xposed.features.general.ShareLimit;
import com.wmods.wppenhacer.xposed.features.general.ShowEditMessage;
import com.wmods.wppenhacer.xposed.features.general.StatusDownload;
import com.wmods.wppenhacer.xposed.features.general.Tasker;
import com.wmods.wppenhacer.xposed.features.general.ViewOnce;
import com.wmods.wppenhacer.xposed.features.privacy.FreezeLastSeen;
import com.wmods.wppenhacer.xposed.features.privacy.GhostMode;
Expand Down Expand Up @@ -211,7 +212,8 @@ private static void plugins(@NonNull ClassLoader loader, @NonNull XSharedPrefere
ViewOnce.class,
CallType.class,
MediaPreview.class,
FilterGroups.class
FilterGroups.class,
Tasker.class
};

for (var classe : classes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1528,4 +1528,13 @@ public static Class getFilterView(ClassLoader loader) throws Exception {
return results.get(0).getInstance(loader);
});
}

public static Class loadActionUser(ClassLoader loader) throws Exception {
return UnobfuscatorCache.getInstance().getClass(loader, () -> {
var results = dexkit.findClass(new FindClass().matcher(new ClassMatcher().addUsingString("UserActions/reportIfBadTime: time=")));
if (results.isEmpty()) throw new RuntimeException("ActionUser class not found");
return results.get(0).getInstance(loader);
});
}

}
205 changes: 125 additions & 80 deletions app/src/main/java/com/wmods/wppenhacer/xposed/core/WppCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.widget.Toast;

import androidx.annotation.Nullable;

Expand All @@ -18,6 +19,7 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -43,6 +45,7 @@ public class WppCore {
private static Object mContactManager;
private static SharedPreferences privPrefs;
private static Object mStartUpConfig;
private static Object mActionUser;

public static void addMenuItemClass(Class<?> aClass, OnMenuCreate listener) {
var list = listenerMenu.computeIfAbsent(aClass, k -> new ArrayList<>());
Expand All @@ -56,102 +59,144 @@ public static void addMenuItemString(String className, OnMenuCreate listener) {
list.add(listener);
}

public static Object getConversation() {
return mConversation;
}

public static void sendMessage(String number, String message) {
try {
var senderMethod = ReflectionUtils.findMethodUsingFilterIfExists(mActionUser.getClass(), (method) -> List.class.isAssignableFrom(method.getReturnType()) && ReflectionUtils.findIndexOfType(method.getParameterTypes(), String.class) != -1);
if (senderMethod != null) {
var userJid = createUserJid(number + "@s.whatsapp.net");
if (userJid == null) {
Utils.showToast("UserJID not found", Toast.LENGTH_SHORT);
return;
}
var newObject = new Object[senderMethod.getParameterCount()];
var index = ReflectionUtils.findIndexOfType(senderMethod.getParameterTypes(), String.class);
newObject[index - 1] = 0;
newObject[index] = message;
newObject[newObject.length - 1] = false;
newObject[newObject.length - 2] = false;
newObject[newObject.length - 3] = false;
var index2 = ReflectionUtils.findIndexOfType(senderMethod.getParameterTypes(), List.class);
newObject[index2] = Collections.singletonList(userJid);
senderMethod.invoke(mActionUser, newObject);
Utils.showToast("Message sent to " + number, Toast.LENGTH_SHORT);
}
} catch (Exception e) {
Utils.showToast("Error in sending message:" + e.getMessage(), Toast.LENGTH_SHORT);
XposedBridge.log(e);
}
}


public interface ObjectOnChangeListener {
void onChange(Object object, String type);

}

public static void Initialize(ClassLoader loader) throws Exception {
privPrefs = Utils.getApplication().getSharedPreferences("WaGlobal", Context.MODE_PRIVATE);
privPrefs = Utils.getApplication().getSharedPreferences("WaGlobal", Context.MODE_PRIVATE);

// init Main activity
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.whatsapp.HomeActivity", loader), "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mainActivity = param.thisObject;
}
});
// init Main activity
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.whatsapp.HomeActivity", loader), "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mainActivity = param.thisObject;
}
});

XposedBridge.hookAllMethods(XposedHelpers.findClass("com.whatsapp.HomeActivity", loader), "onResume", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mainActivity = param.thisObject;
}
});

XposedHelpers.findAndHookMethod(Activity.class, "onCreateOptionsMenu", Menu.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
for (Class<?> aClass : listenerMenu.keySet()) {
if (!aClass.isInstance(param.thisObject)) return;
for (OnMenuCreate listener : listenerMenu.get(aClass)) {
listener.onBeforeCreate((Activity) param.thisObject, (Menu) param.args[0]);
}
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.whatsapp.HomeActivity", loader), "onResume", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mainActivity = param.thisObject;
}
});

XposedHelpers.findAndHookMethod(Activity.class, "onCreateOptionsMenu", Menu.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
for (Class<?> aClass : listenerMenu.keySet()) {
if (!aClass.isInstance(param.thisObject)) return;
for (OnMenuCreate listener : listenerMenu.get(aClass)) {
listener.onBeforeCreate((Activity) param.thisObject, (Menu) param.args[0]);
}
}
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
for (Class<?> aClass : listenerMenu.keySet()) {
if (!aClass.isInstance(param.thisObject)) return;
for (OnMenuCreate listener : listenerMenu.get(aClass)) {
listener.onAfterCreate((Activity) param.thisObject, (Menu) param.args[0]);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
for (Class<?> aClass : listenerMenu.keySet()) {
if (!aClass.isInstance(param.thisObject)) return;
for (OnMenuCreate listener : listenerMenu.get(aClass)) {
listener.onAfterCreate((Activity) param.thisObject, (Menu) param.args[0]);
}
}
});

// init ContactManager
getContactMethod = Unobfuscator.loadGetContactInfoMethod(loader);
XposedBridge.hookAllConstructors(getContactMethod.getDeclaringClass(), new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
mContactManager = param.thisObject;
}
});

// init UserJID
var mSendReadClass = XposedHelpers.findClass("com.whatsapp.jobqueue.job.SendReadReceiptJob", loader);
var subClass = Arrays.stream(mSendReadClass.getConstructors()).filter(c -> c.getParameterTypes().length == 8).findFirst().orElse(null).getParameterTypes()[0];
mGenJidClass = Arrays.stream(subClass.getFields()).filter(field -> Modifier.isStatic(field.getModifiers())).findFirst().orElse(null).getType();
mGenJidMethod = Arrays.stream(mGenJidClass.getMethods()).filter(m -> m.getParameterCount() == 1 && !Modifier.isStatic(m.getModifiers())).findFirst().orElse(null);
// Bottom Dialog
bottomDialog = Unobfuscator.loadDialogViewClass(loader);

// Conversation
var onStartMethod = Unobfuscator.loadAntiRevokeOnStartMethod(loader);
var onResumeMethod = Unobfuscator.loadAntiRevokeOnResumeMethod(loader);
convChatField = Unobfuscator.loadAntiRevokeConvChatField(loader);
chatJidField = Unobfuscator.loadAntiRevokeChatJidField(loader);
XposedBridge.hookMethod(onStartMethod, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
mConversation = (Activity) param.thisObject;
for (ObjectOnChangeListener listener : listenerChat) {
listener.onChange(mConversation, "onStartConversation");
}
}
});

XposedBridge.hookMethod(onResumeMethod, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
mConversation = (Activity) param.thisObject;
for (ObjectOnChangeListener listener : listenerChat) {
listener.onChange(mConversation, "onResumeConversation");
}
}
});

// init ContactManager
getContactMethod = Unobfuscator.loadGetContactInfoMethod(loader);
XposedBridge.hookAllConstructors(getContactMethod.getDeclaringClass(), new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
mContactManager = param.thisObject;
}
});

// init UserJID
var mSendReadClass = XposedHelpers.findClass("com.whatsapp.jobqueue.job.SendReadReceiptJob", loader);
var subClass = Arrays.stream(mSendReadClass.getConstructors()).filter(c -> c.getParameterTypes().length == 8).findFirst().orElse(null).getParameterTypes()[0];
mGenJidClass = Arrays.stream(subClass.getFields()).filter(field -> Modifier.isStatic(field.getModifiers())).findFirst().orElse(null).getType();
mGenJidMethod = Arrays.stream(mGenJidClass.getMethods()).filter(m -> m.getParameterCount() == 1 && !Modifier.isStatic(m.getModifiers())).findFirst().orElse(null);
// Bottom Dialog
bottomDialog = Unobfuscator.loadDialogViewClass(loader);

// Conversation
var onStartMethod = Unobfuscator.loadAntiRevokeOnStartMethod(loader);
var onResumeMethod = Unobfuscator.loadAntiRevokeOnResumeMethod(loader);
convChatField = Unobfuscator.loadAntiRevokeConvChatField(loader);
chatJidField = Unobfuscator.loadAntiRevokeChatJidField(loader);
XposedBridge.hookMethod(onStartMethod, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
mConversation = (Activity) param.thisObject;
for (ObjectOnChangeListener listener : listenerChat) {
listener.onChange(mConversation, "onStartConversation");
}
});

// StartUpPrefs
var startPrefsConfig = Unobfuscator.loadStartPrefsConfig(loader);
XposedBridge.hookMethod(startPrefsConfig, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mStartUpConfig = param.thisObject;
}
});

XposedBridge.hookMethod(onResumeMethod, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
mConversation = (Activity) param.thisObject;
for (ObjectOnChangeListener listener : listenerChat) {
listener.onChange(mConversation, "onResumeConversation");
}
});
}
});

// StartUpPrefs
var startPrefsConfig = Unobfuscator.loadStartPrefsConfig(loader);
XposedBridge.hookMethod(startPrefsConfig, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mStartUpConfig = param.thisObject;
}
});

var actionUser = Unobfuscator.loadActionUser(loader);
XposedBridge.hookAllConstructors(actionUser, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
mActionUser = param.thisObject;
XposedBridge.log("mActionUser: " + mActionUser);
}
});


}

public static int getDefaultTheme() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.wmods.wppenhacer.xposed.features.general;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import com.wmods.wppenhacer.xposed.core.Feature;
import com.wmods.wppenhacer.xposed.core.Unobfuscator;
import com.wmods.wppenhacer.xposed.core.Utils;
import com.wmods.wppenhacer.xposed.core.WppCore;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class Tasker extends Feature {
private static Object fMessage;

public Tasker(@NonNull ClassLoader classLoader, @NonNull XSharedPreferences preferences) {
super(classLoader, preferences);
}

@Override
public void doHook() throws Throwable {
if (!prefs.getBoolean("tasker", false)) return;
hookReceiveMessage();
registerSenderMessage();
}

@NonNull
@Override
public String getPluginName() {
return "Tasker";
}

private void registerSenderMessage() {
IntentFilter filter = new IntentFilter("com.wmods.wppenhacer.MESSAGE_SENT");
ContextCompat.registerReceiver(Utils.getApplication(), new SenderMessageBroadcastReceiver(), filter, ContextCompat.RECEIVER_EXPORTED);
}

public void hookReceiveMessage() throws Throwable {
var method = Unobfuscator.loadReceiptMethod(classLoader);
var method2 = Unobfuscator.loadReceiptOutsideChat(classLoader);
var newMessageMethod = Unobfuscator.loadNewMessageMethod(classLoader);
var fieldMessageKey = Unobfuscator.loadMessageKeyField(classLoader);

XposedBridge.hookMethod(method2, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
fMessage = param.args[0];
}
});

XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args[4] == "sender" || param.args[1] == null) return;
var messageKey = fieldMessageKey.get(fMessage);
var userJid = XposedHelpers.getObjectField(messageKey, "A00");
var rawJid = WppCore.getRawString(userJid);
var number = WppCore.stripJID(rawJid);
var msg = (String) newMessageMethod.invoke(fMessage);
new Handler(Utils.getApplication().getMainLooper()).post(() -> {
Intent intent = new Intent("com.wmods.wppenhacer.MESSAGE_RECEIVED");
intent.putExtra("number", number);
intent.putExtra("message", msg);
Utils.getApplication().sendBroadcast(intent);
});
}
});

}

public static class SenderMessageBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
XposedBridge.log("Message sent");
var number = intent.getStringExtra("number");
var message = intent.getStringExtra("message");
if (number == null || message == null) return;
number = number.replaceAll("\\D", "");
WppCore.sendMessage(number, message);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ public static Object getField(Field loadProfileInfoField, Object thisObject) {

public static int findIndexOfType(Object[] args, Class<?> type) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null && type.isAssignableFrom(args[i].getClass())) return i;
if (args[i] == null) continue;
if (args[i] instanceof Class) {
if (type.isAssignableFrom((Class) args[i])) return i;
continue;
}
if (type.isAssignableFrom(args[i].getClass())) return i;
}
return -1;
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,6 @@
<string name="call_type_buttons_declined">Call declined</string>
<string name="call_type_buttons_uncallable">Unable to receive WhatsApp calls</string>
<string name="call_type_buttons_ended">Not answered</string>
<string name="enable_tasker_automation">Enable Tasker Automation</string>
<string name="enable_tasker_automation_sum">Enables the possibility of using intent to receive and send messages in Tasker</string>
</resources>
Loading

0 comments on commit 1e0e574

Please sign in to comment.