Skip to content

Commit bc6e2b6

Browse files
committed
refactor: Improve deleted message data extraction and contact name resolution, and implement message insertion in the database.
1 parent 37d1972 commit bc6e2b6

2 files changed

Lines changed: 104 additions & 74 deletions

File tree

app/src/main/java/com/wmods/wppenhacer/xposed/core/db/DelMessageStore.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ private void createDeletedForMeTable(SQLiteDatabase db) {
7575
"UNIQUE(key_id, chat_jid))");
7676
}
7777

78-
// ... (insertMessage remains same)
78+
public void insertMessage(String jid, String msgid, long timestamp) {
79+
try (SQLiteDatabase dbWrite = this.getWritableDatabase()) {
80+
ContentValues values = new ContentValues();
81+
values.put("jid", jid);
82+
values.put("msgid", msgid);
83+
values.put("timestamp", timestamp);
84+
dbWrite.insertWithOnConflict("delmessages", null, values, SQLiteDatabase.CONFLICT_IGNORE);
85+
}
86+
}
7987

8088
public void insertDeletedMessage(DeletedMessage message) {
8189
try (SQLiteDatabase dbWrite = this.getWritableDatabase()) {

app/src/main/java/com/wmods/wppenhacer/xposed/features/general/RecoverDeleteForMe.java

Lines changed: 95 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -88,37 +88,28 @@ private void saveOne(Context context, Object msg) throws Exception {
8888
if (msg == null) return;
8989
Class<?> msgClass = msg.getClass();
9090

91-
// 1. Find Key Field (Scan for first NON-NULL field of Key type)
91+
// 1. Find Key Field
9292
Object key = null;
9393
if (FMessageWpp.Key.TYPE != null) {
9494
key = getFirstNonNullFieldByType(msg, FMessageWpp.Key.TYPE);
9595
}
96-
97-
// Fallback: search by name "key" if type search failed or key type unknown
9896
if (key == null) {
9997
Field keyField = findField(msgClass, "key");
10098
if (keyField != null) key = keyField.get(msg);
10199
}
102-
103100
if (key == null) return;
104101

105-
// 2. Extract Message ID
106-
String msgId = getStr(key, "id");
107-
if (msgId == null) msgId = getStr(key, "A01"); // Obfuscated ID
108-
if (msgId == null) {
109-
XposedBridge.log("WAE: msgId not found on Key " + key.getClass().getName());
110-
return;
111-
}
102+
// 2. Extract Message ID (Key ID)
103+
String keyId = getStr(key, "id");
104+
if (keyId == null) keyId = getStr(key, "A01");
105+
if (keyId == null) return;
112106

113107
// 3. Extract RemoteJid / ChatJid
114108
String chatJid = null;
115109
Object jidObj = getObj(key, "remoteJid");
116110
if (jidObj == null) jidObj = getObj(key, "chatJid");
117-
if (jidObj == null) jidObj = getObj(key, "A00"); // Obfuscated RemoteJid
118-
111+
if (jidObj == null) jidObj = getObj(key, "A00");
119112
if (jidObj != null) chatJid = jidObj.toString();
120-
121-
// Fix: Ensure JID isn't "false" or "true" due to bad reflection
122113
if (chatJid != null && (chatJid.equalsIgnoreCase("false") || chatJid.equalsIgnoreCase("true"))) {
123114
chatJid = null;
124115
}
@@ -127,20 +118,18 @@ private void saveOne(Context context, Object msg) throws Exception {
127118
boolean fromMe = false;
128119
Field fmField = findField(key.getClass(), "fromMe");
129120
if (fmField == null) fmField = findField(key.getClass(), "isFromMe");
130-
if (fmField == null) fmField = findField(key.getClass(), "A02"); // Obfuscated fromMe
131-
121+
if (fmField == null) fmField = findField(key.getClass(), "A02");
132122
if (fmField != null) {
133123
Object v = fmField.get(key);
134124
if (v instanceof Boolean) fromMe = (Boolean) v;
135125
}
136126

137127
// 5. Extract Text Body
138-
String text = getStr(msg, "text");
139-
if (text == null) text = getStr(msg, "body");
140-
if (text == null) text = getStr(msg, "A0Q");
128+
String textContent = getStr(msg, "text");
129+
if (textContent == null) textContent = getStr(msg, "body");
130+
if (textContent == null) textContent = getStr(msg, "A0Q");
141131

142-
// Scan for all strings to find the text
143-
if (text == null) {
132+
if (textContent == null) {
144133
String bestCandidate = null;
145134
Class<?> cls = msgClass;
146135
while (cls != null && cls != Object.class) {
@@ -159,9 +148,7 @@ private void saveOne(Context context, Object msg) throws Exception {
159148
}
160149
cls = cls.getSuperclass();
161150
}
162-
if (bestCandidate != null) {
163-
text = bestCandidate;
164-
}
151+
if (bestCandidate != null) textContent = bestCandidate;
165152
}
166153

167154
// 6. Media Type
@@ -171,78 +158,42 @@ private void saveOne(Context context, Object msg) throws Exception {
171158
if (mtf != null) {
172159
try { mediaType = mtf.getInt(msg); } catch (Exception ignored) {}
173160
}
174-
161+
162+
// 7. Sender JID
175163
String senderJid = fromMe ? "Me" : chatJid;
176164
Object participant = getObj(msg, "participant");
177165
if (participant == null) participant = getObj(msg, "senderJid");
178166
if (participant == null) participant = getObj(msg, "A0b");
179-
180167
if (participant != null) {
181168
String val = participant.toString();
182169
if (!val.equalsIgnoreCase("false") && !val.equalsIgnoreCase("true")) {
183170
senderJid = val;
184171
}
185172
}
186173

187-
// 5. Extract Text Content
188-
String textContent = null;
189-
if (msgObj != null) {
190-
textContent = findLongestString(msgObj);
191-
}
192-
193-
// 6. Extract Media (if any)
194-
int mediaType = -1;
195-
String mediaCaption = null;
174+
// 8. Media Details
196175
String mediaPath = null;
197-
198-
// Original media extraction logic
199-
Field mtf = findField(msgClass, "mediaType");
200-
if (mtf == null) mtf = findField(msgClass, "media_wa_type");
201-
if (mtf != null) {
202-
try { mediaType = mtf.getInt(msgObj); } catch (Exception ignored) {}
203-
}
204-
205-
Object mf = getObj(msgObj, "mediaData");
206-
if (mf == null) mf = getObj(msgObj, "mediaFile");
176+
Object mf = getObj(msg, "mediaData");
177+
if (mf == null) mf = getObj(msg, "mediaFile");
207178
if (mf instanceof File && ((File) mf).exists()) {
208179
mediaPath = ((File) mf).getAbsolutePath();
209180
}
210181

211-
String caption = getStr(msgObj, "caption");
212-
if (caption == null) caption = getStr(msgObj, "mediaCaption");
213-
if (caption == null) caption = getStr(msgObj, "A03");
214-
mediaCaption = caption; // Assign to mediaCaption
182+
String mediaCaption = getStr(msg, "caption");
183+
if (mediaCaption == null) mediaCaption = getStr(msg, "mediaCaption");
184+
if (mediaCaption == null) mediaCaption = getStr(msg, "A03");
215185

216-
long timestamp = System.currentTimeMillis(); // Assuming current time for deleted message
186+
long timestamp = System.currentTimeMillis();
217187

218-
// --- NEW: Contact Name Resolution ---
188+
// 9. Contact Name Resolution
219189
String contactName = null;
220190
try {
221-
if (chatJid != null) {
222-
if (chatJid.contains("@g.us")) {
223-
// Group Chat: Try to get Subject
224-
// We can try to find the "subject" or "name" field in the chat object if available,
225-
// or use a utility if we can find one.
226-
// For now, let's try a common trick: query the Contacts database for the Group JID (WaEnhancer often syncs groups)
227-
// OR better: use the same ContactHelper logic but inside the hook context
228-
contactName = getContactName(context, chatJid);
229-
} else {
230-
// Individual Chat: Resolve from ContactsContract
231-
contactName = getContactName(context, chatJid);
232-
}
233-
}
234-
if (contactName == null && senderJid != null && chatJid.contains("@g.us")) {
235-
// If in group and sender is known, maybe we want sender name?
236-
// But UI wants Title to be Group Name.
237-
// We will leave contactName as null if Group Name not found,
238-
// and let UI handle Sender Name for the bubble separately (or we can save sender name too?)
239-
// For now, let's stick to Chat Title.
240-
}
191+
contactName = resolveWaContactName(context, msg, chatJid);
241192
} catch (Throwable t) {
242193
t.printStackTrace();
243194
}
244195

245-
// Save to Database
196+
// Create and Save
246197
DeletedMessage deletedMessage = new DeletedMessage(
247198
0, keyId, chatJid, senderJid, timestamp, mediaType, textContent, mediaPath, mediaCaption, fromMe, contactName
248199
);
@@ -366,6 +317,28 @@ private String getStr(Object obj, String name) {
366317
return v instanceof String ? (String) v : null;
367318
} catch (Exception e) { return null; }
368319
}
320+
321+
// New utility: Get string by scanning all fields
322+
private String findLongestString(Object obj) {
323+
if (obj == null) return null;
324+
String best = null;
325+
Class<?> cls = obj.getClass();
326+
while(cls != null && cls != Object.class) {
327+
for (Field f : cls.getDeclaredFields()) {
328+
if (f.getType() == String.class) {
329+
f.setAccessible(true);
330+
try {
331+
String s = (String) f.get(obj);
332+
if (s != null && s.length() > 0) {
333+
if (best == null || s.length() > best.length()) best = s;
334+
}
335+
} catch(Exception e) {}
336+
}
337+
}
338+
cls = cls.getSuperclass();
339+
}
340+
return best;
341+
}
369342

370343
private Object getObj(Object obj, String name) {
371344
try {
@@ -374,6 +347,55 @@ private Object getObj(Object obj, String name) {
374347
return f.get(obj);
375348
} catch (Exception e) { return null; }
376349
}
350+
351+
// Internal WA Contact Resolution Helper
352+
private String resolveWaContactName(Context context, Object msgObj, String chatJid) {
353+
try {
354+
// Strategy 1: Look for a "name" or "subject" field directly in a Chat/Contact object attached to msg
355+
// Often msg has a field 'A0J' or similar that is the Contact/ChatInfo
356+
357+
// Scan fields of msgObj for objects that might be "Contact" or "Chat"
358+
Class<?> msgClass = msgObj.getClass();
359+
while (msgClass != null && msgClass != Object.class) {
360+
for (Field f : msgClass.getDeclaredFields()) {
361+
f.setAccessible(true);
362+
Object fieldVal = f.get(msgObj);
363+
if (fieldVal != null && !f.getType().isPrimitive() && !f.getType().getName().startsWith("java.lang")) {
364+
// Check this object for a "name" or "subject" string
365+
String possibleName = getPrivString(fieldVal, "name");
366+
if (possibleName == null) possibleName = getPrivString(fieldVal, "subject");
367+
if (possibleName == null) possibleName = getPrivString(fieldVal, "A0X"); // Obfuscated name?
368+
if (possibleName == null) possibleName = getPrivString(fieldVal, "A0W");
369+
370+
// Validation: Names usually don't have @ or are not JIDs
371+
if (possibleName != null && !possibleName.contains("@") && possibleName.length() > 0) {
372+
XposedBridge.log("WAE: Potentially found name via reflection: " + possibleName);
373+
return possibleName;
374+
}
375+
}
376+
}
377+
msgClass = msgClass.getSuperclass();
378+
}
379+
380+
// Strategy 2: If we failed to find it in msg, fall back to ContactsContract (for individuals)
381+
// But strict cleaning of JID
382+
if (chatJid != null && !chatJid.contains("@g.us")) {
383+
return getContactName(context, chatJid);
384+
}
385+
386+
} catch (Exception e) {
387+
XposedBridge.log("WAE: ResolveWaContactName Error: " + e.getMessage());
388+
}
389+
return null;
390+
}
391+
392+
private String getPrivString(Object obj, String name) {
393+
try {
394+
Field f = findField(obj.getClass(), name);
395+
if (f != null) return (String) f.get(obj);
396+
} catch(Exception e) {}
397+
return null;
398+
}
377399

378400
@Override
379401
public String getPluginName() {

0 commit comments

Comments
 (0)