596 lines
26 KiB
JavaScript
596 lines
26 KiB
JavaScript
// 微信工作手机 SDK v9-final - 全量Frida RPC接口
|
||
// 修复: Java.choose异步问题 -> 改用全局初始化+同步等待
|
||
// 修复: chatroom表列名(owner->roomowner), ContactLabel表, 朋友圈DB路径
|
||
// 账号:游条姐 (wxid_5g37snchpv8e22)
|
||
'use strict';
|
||
|
||
var _dbs = {};
|
||
var _dbsReady = false;
|
||
var _initCount = 0;
|
||
|
||
// ==================== DB初始化(脚本加载时立即执行)====================
|
||
function initAllDBs() {
|
||
_initCount++;
|
||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||
onMatch: function(db) {
|
||
try {
|
||
var p = db.getPath();
|
||
if (!p) return;
|
||
if (p.indexOf('EnMicroMsg') > -1 && p.indexOf('EnMicroMsg.db') > -1) _dbs['main'] = db;
|
||
else if (p.indexOf('SnsMicroMsg') > -1) _dbs['sns'] = db;
|
||
else if (p.indexOf('sns_star_info') > -1) _dbs['sns_star'] = db;
|
||
else if (p.indexOf('WalletCoreDB-2') > -1) _dbs['wallet'] = db;
|
||
else if (p.indexOf('WalletPay') > -1) _dbs['wallet2'] = db;
|
||
else if (p.indexOf('FinderMM029') > -1) _dbs['finder'] = db;
|
||
else if (p.indexOf('FinderMessage006') > -1) _dbs['finder2'] = db;
|
||
else if (p.indexOf('FinderAccounts_01') > -1) _dbs['finder3'] = db;
|
||
else if (p.indexOf('enFavorite') > -1) _dbs['favorite'] = db;
|
||
else if (p.indexOf('FTS5IndexMicroMsg') > -1) _dbs['fts5'] = db;
|
||
else if (p.indexOf('AppBrandComm') > -1) _dbs['appbrand'] = db;
|
||
else if (p.indexOf('WxFileIndex') > -1) _dbs['wxfile'] = db;
|
||
var key = '_path_' + p.split('/').pop().replace('.db','');
|
||
_dbs[key] = db;
|
||
} catch(e) {}
|
||
},
|
||
onComplete: function() {
|
||
_dbsReady = true;
|
||
var dbNames = Object.keys(_dbs).filter(function(k) { return !k.startsWith('_path_'); });
|
||
send('[*] WeChat SDK v9-final loaded. DBs: ' + dbNames.join(', '));
|
||
}
|
||
});
|
||
}
|
||
|
||
// 脚本加载时立即初始化
|
||
Java.perform(function() {
|
||
initAllDBs();
|
||
});
|
||
|
||
function getDB(name) {
|
||
if (!_dbs[name]) {
|
||
// 重新尝试初始化(可能DB刚被加载)
|
||
initAllDBs();
|
||
// 等待一小段时间
|
||
var start = Date.now();
|
||
while (!_dbs[name] && Date.now() - start < 2000) {
|
||
// 忙等待最多2秒
|
||
}
|
||
}
|
||
return _dbs[name] || null;
|
||
}
|
||
|
||
function execSQL(db, sql, args) {
|
||
if (!db) return {error: 'DB not available', rows: [], count: 0};
|
||
try {
|
||
var cursor;
|
||
if (args && args.length > 0) {
|
||
var jArgs = Java.array('java.lang.String', args.map(function(a) { return a ? String(a) : null; }));
|
||
cursor = db.rawQuery(sql, jArgs);
|
||
} else {
|
||
cursor = db.rawQuery(sql, null);
|
||
}
|
||
var cols = [];
|
||
var colCount = cursor.getColumnCount();
|
||
for (var i = 0; i < colCount; i++) cols.push(cursor.getColumnName(i));
|
||
var rows = [];
|
||
while (cursor.moveToNext()) {
|
||
var row = {};
|
||
for (var j = 0; j < colCount; j++) {
|
||
try { row[cols[j]] = cursor.getString(j); } catch(e) { row[cols[j]] = null; }
|
||
}
|
||
rows.push(row);
|
||
}
|
||
cursor.close();
|
||
return {columns: cols, rows: rows, count: rows.length};
|
||
} catch(e) {
|
||
return {error: e.toString(), rows: [], count: 0};
|
||
}
|
||
}
|
||
|
||
function writeSQL(db, sql) {
|
||
if (!db) return {error: 'DB not available'};
|
||
try {
|
||
db.execSQL(sql);
|
||
return {success: true};
|
||
} catch(e) {
|
||
return {error: e.toString()};
|
||
}
|
||
}
|
||
|
||
// ==================== RPC接口 ====================
|
||
rpc.exports = {
|
||
// ===== 系统 =====
|
||
ping: function() {
|
||
var dbStatus = {};
|
||
['main','sns','sns_star','wallet','finder','favorite','fts5','appbrand'].forEach(function(n) {
|
||
dbStatus[n] = !!_dbs[n];
|
||
});
|
||
return {status: 'ok', version: 'v9-final', timestamp: Date.now(), dbs: dbStatus};
|
||
},
|
||
|
||
initDbs: function() {
|
||
initAllDBs();
|
||
var status = {};
|
||
['main','sns','sns_star','wallet','wallet2','finder','finder2','finder3','favorite','fts5','appbrand'].forEach(function(n) {
|
||
status[n] = !!_dbs[n];
|
||
});
|
||
return {status: status, total_dbs: Object.keys(_dbs).length};
|
||
},
|
||
|
||
// ===== 账号 =====
|
||
getAccountInfo: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var wxid = execSQL(db, "SELECT value FROM userinfo WHERE id=2");
|
||
var nick = execSQL(db, "SELECT value FROM userinfo WHERE id=6");
|
||
var avatar = execSQL(db, "SELECT value FROM userinfo WHERE id=4");
|
||
var phone = execSQL(db, "SELECT value FROM userinfo WHERE id=42");
|
||
var region = execSQL(db, "SELECT value FROM userinfo WHERE id=44");
|
||
var sex = execSQL(db, "SELECT value FROM userinfo WHERE id=12");
|
||
return {
|
||
wxid: wxid.rows[0] ? wxid.rows[0].value : null,
|
||
nickname: nick.rows[0] ? nick.rows[0].value : null,
|
||
avatar: avatar.rows[0] ? avatar.rows[0].value : null,
|
||
phone: phone.rows[0] ? phone.rows[0].value : null,
|
||
region: region.rows[0] ? region.rows[0].value : null,
|
||
sex: sex.rows[0] ? sex.rows[0].value : null,
|
||
};
|
||
},
|
||
|
||
// ===== 联系人 =====
|
||
getContacts: function(limit, offset) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 100;
|
||
offset = parseInt(offset) || 0;
|
||
return execSQL(db,
|
||
"SELECT username, alias, conRemark, nickname, type, contactLabelIds " +
|
||
"FROM rcontact WHERE type=3 ORDER BY rowid LIMIT " + limit + " OFFSET " + offset
|
||
);
|
||
},
|
||
|
||
getContactCount: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var r1 = execSQL(db, "SELECT COUNT(*) as friends FROM rcontact WHERE type=3");
|
||
var r2 = execSQL(db, "SELECT COUNT(*) as total FROM rcontact");
|
||
return {
|
||
friends: r1.rows[0] ? parseInt(r1.rows[0].friends) : 0,
|
||
total: r2.rows[0] ? parseInt(r2.rows[0].total) : 0
|
||
};
|
||
},
|
||
|
||
getContactDetail: function(wxid) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
return execSQL(db, "SELECT * FROM rcontact WHERE username='" + wxid + "' LIMIT 1");
|
||
},
|
||
|
||
searchContacts: function(keyword) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
return execSQL(db,
|
||
"SELECT username, alias, conRemark, nickName, type FROM rcontact " +
|
||
"WHERE (nickName LIKE '%" + keyword + "%' OR conRemark LIKE '%" + keyword + "%' OR alias LIKE '%" + keyword + "%') " +
|
||
"AND type NOT IN (0,4) LIMIT 50"
|
||
);
|
||
},
|
||
|
||
// ===== 标签 =====
|
||
getLabels: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
// 先尝试ContactLabel表
|
||
var r = execSQL(db, "SELECT labelId, labelName, createTime FROM ContactLabel ORDER BY labelId");
|
||
if (r.count > 0) return r;
|
||
// 备用:从rcontact的contactLabelIds字段提取唯一ID
|
||
var ids = execSQL(db, "SELECT DISTINCT contactLabelIds FROM rcontact WHERE contactLabelIds != '' AND contactLabelIds IS NOT NULL LIMIT 200");
|
||
var labelSet = {};
|
||
ids.rows.forEach(function(row) {
|
||
var parts = (row.contactLabelIds || '').split(',');
|
||
parts.forEach(function(id) { if (id) labelSet[id.trim()] = true; });
|
||
});
|
||
var rows = Object.keys(labelSet).map(function(id) { return {labelId: id, labelName: 'Label_' + id}; });
|
||
return {columns: ['labelId', 'labelName'], rows: rows, count: rows.length};
|
||
},
|
||
|
||
getLabelContacts: function(labelId) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
return execSQL(db,
|
||
"SELECT username, nickName, conRemark, contactLabelIds FROM rcontact " +
|
||
"WHERE contactLabelIds LIKE '%" + labelId + "%' AND type=3 LIMIT 200"
|
||
);
|
||
},
|
||
|
||
// ===== 统计 =====
|
||
getAllStats: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var friends = execSQL(db, "SELECT COUNT(*) as n FROM rcontact WHERE type=3");
|
||
var groups = execSQL(db, "SELECT COUNT(*) as n FROM chatroom");
|
||
var labels = execSQL(db, "SELECT COUNT(*) as n FROM ContactLabel");
|
||
var msgs = execSQL(db, "SELECT COUNT(*) as n FROM message");
|
||
var imgs = execSQL(db, "SELECT COUNT(*) as n FROM ImgInfo2");
|
||
var videos = execSQL(db, "SELECT COUNT(*) as n FROM videoinfo2");
|
||
var files = execSQL(db, "SELECT COUNT(*) as n FROM FileMsgInfo");
|
||
var labels = execSQL(db, "SELECT COUNT(*) as n FROM ContactLabel");
|
||
return {
|
||
friends: friends.rows[0] ? parseInt(friends.rows[0].n) : 0,
|
||
groups: groups.rows[0] ? parseInt(groups.rows[0].n) : 0,
|
||
messages: msgs.rows[0] ? parseInt(msgs.rows[0].n) : 0,
|
||
images: imgs.rows[0] ? parseInt(imgs.rows[0].n) : 0,
|
||
videos: videos.rows[0] ? parseInt(videos.rows[0].n) : 0,
|
||
files: files.rows[0] ? parseInt(files.rows[0].n) : 0,
|
||
labels: labels.rows[0] ? parseInt(labels.rows[0].n) : 0
|
||
};
|
||
},
|
||
|
||
// ===== 消息 =====
|
||
getRecentMessages: function(hours) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
hours = parseInt(hours) || 24;
|
||
var since = Date.now() - hours * 3600 * 1000;
|
||
return execSQL(db,
|
||
"SELECT msgId, type, content, talker, createTime, isSend FROM message " +
|
||
"WHERE createTime > " + since + " ORDER BY createTime DESC LIMIT 200"
|
||
);
|
||
},
|
||
|
||
getMessages: function(talker, limit) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 50;
|
||
return execSQL(db,
|
||
"SELECT msgId, type, content, talker, createTime, isSend FROM message " +
|
||
"WHERE talker='" + talker + "' ORDER BY createTime DESC LIMIT " + limit
|
||
);
|
||
},
|
||
|
||
sendMessage: function(toUser, content, type) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
type = parseInt(type) || 1;
|
||
var msgId = Date.now();
|
||
var createTime = Date.now();
|
||
var sql = "INSERT INTO message (msgId, type, status, isSend, content, talker, createTime, imgPath, reserved, lvbuffer, transContent, flag, bizClientMsgId, bizChatId, bizChatUserId, msgSeq, historyId) " +
|
||
"VALUES (" + msgId + ", " + type + ", 3, 1, '" + content.replace(/'/g, "''") + "', '" + toUser + "', " + createTime + ", '', 0, NULL, '', 0, '', 0, '', 0, 0)";
|
||
var r = writeSQL(db, sql);
|
||
if (r.success) {
|
||
var verify = execSQL(db, "SELECT msgId, content, talker, createTime FROM message WHERE msgId=" + msgId);
|
||
return {success: true, msgId: msgId, toUser: toUser, content: content, db_verified: verify.count > 0,
|
||
note: "Written to local DB. For real network send, WeChat 8.0.56 network class name needed."};
|
||
}
|
||
return {success: false, error: r.error};
|
||
},
|
||
|
||
massMessage: function(targets, content) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var results = [];
|
||
var successCount = 0;
|
||
(targets || []).forEach(function(toUser) {
|
||
var msgId = Date.now() + Math.floor(Math.random() * 10000);
|
||
var createTime = Date.now();
|
||
var sql = "INSERT INTO message (msgId, type, status, isSend, content, talker, createTime, imgPath, reserved, lvbuffer, transContent, flag, bizClientMsgId, bizChatId, bizChatUserId, msgSeq, historyId) " +
|
||
"VALUES (" + msgId + ", 1, 3, 1, '" + content.replace(/'/g, "''") + "', '" + toUser + "', " + createTime + ", '', 0, NULL, '', 0, '', 0, '', 0, 0)";
|
||
var r = writeSQL(db, sql);
|
||
if (r.success) { successCount++; results.push({toUser: toUser, msgId: msgId, success: true}); }
|
||
else { results.push({toUser: toUser, success: false, error: r.error}); }
|
||
});
|
||
return {total: targets.length, success: successCount, results: results};
|
||
},
|
||
|
||
// ===== 群组 =====
|
||
getGroups: function(limit) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 50;
|
||
// 先查chatroom表的列名
|
||
var cols = execSQL(db, "PRAGMA table_info(chatroom)");
|
||
var colNames = cols.rows.map(function(r) { return r.name; });
|
||
var selectCols = ['chatroomname'];
|
||
if (colNames.indexOf('memberCount') > -1) selectCols.push('memberCount');
|
||
if (colNames.indexOf('roomowner') > -1) selectCols.push('roomowner');
|
||
if (colNames.indexOf('announcement') > -1) selectCols.push('announcement');
|
||
if (colNames.indexOf('selfDisplayName') > -1) selectCols.push('selfDisplayName');
|
||
if (colNames.indexOf('memberlist') > -1) selectCols.push('memberlist');
|
||
if (colNames.indexOf('NickName') > -1) selectCols.push('NickName');
|
||
return execSQL(db, "SELECT " + selectCols.join(', ') + " FROM chatroom ORDER BY rowid DESC LIMIT " + limit);
|
||
},
|
||
|
||
getGroupCount: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
return execSQL(db, "SELECT COUNT(*) as total FROM chatroom");
|
||
},
|
||
|
||
getGroupDetail: function(chatroomId) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
return execSQL(db, "SELECT * FROM chatroom WHERE chatroomname='" + chatroomId + "' LIMIT 1");
|
||
},
|
||
|
||
getGroupMembers: function(chatroomId) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var group = execSQL(db, "SELECT memberlist, roomowner, chatroomnotice, selfDisplayName FROM chatroom WHERE chatroomname='" + chatroomId + "'");
|
||
if (!group.rows[0]) return {error: 'Group not found', rows: [], count: 0};
|
||
var g = group.rows[0];
|
||
var members = (g.memberlist || '').split(';').filter(function(m) { return m.trim(); });
|
||
return {
|
||
chatroomId: chatroomId,
|
||
owner: g.roomowner,
|
||
announcement: g.chatroomnotice,
|
||
selfDisplayName: g.selfDisplayName,
|
||
memberCount: members.length,
|
||
members: members.slice(0, 100),
|
||
rows: members.slice(0, 100).map(function(m) { return {wxid: m}; }),
|
||
count: members.length
|
||
};
|
||
},
|
||
|
||
setGroupAnnouncement: function(chatroomId, announcement) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var sql = "UPDATE chatroom SET chatroomnotice='" + announcement.replace(/'/g, "''") + "' WHERE chatroomname='" + chatroomId + "'";
|
||
return writeSQL(db, sql);
|
||
},
|
||
|
||
setGroupNickname: function(chatroomId, nickname) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var sql = "UPDATE chatroom SET selfDisplayName='" + nickname.replace(/'/g, "''") + "' WHERE chatroomname='" + chatroomId + "'";
|
||
return writeSQL(db, sql);
|
||
},
|
||
|
||
// ===== 朋友圈 =====
|
||
getSnsDbInfo: function() {
|
||
initAllDBs(); // 重新尝试获取DB
|
||
return {
|
||
sns: {available: !!_dbs['sns'], path: 'SnsMicroMsg.db'},
|
||
sns_star: {available: !!_dbs['sns_star'], path: 'sns_star_info.db'}
|
||
};
|
||
},
|
||
|
||
getMoments: function(limit) {
|
||
var db = _dbs['sns'] || _dbs['sns_star'];
|
||
if (!db) {
|
||
initAllDBs();
|
||
db = _dbs['sns'] || _dbs['sns_star'];
|
||
}
|
||
if (!db) return {error: 'SNS DB not loaded. Need to open WeChat moments first.', rows: [], count: 0};
|
||
limit = parseInt(limit) || 20;
|
||
// 尝试不同的表名
|
||
var tables = ['SnsInfo', 'StarSnsInfo', 'sns_info'];
|
||
for (var i = 0; i < tables.length; i++) {
|
||
var r = execSQL(db, "SELECT * FROM " + tables[i] + " ORDER BY createTime DESC LIMIT " + limit);
|
||
if (!r.error) return r;
|
||
}
|
||
return {error: 'No SNS table found', rows: [], count: 0};
|
||
},
|
||
|
||
postMoment: function(content, imgPaths) {
|
||
var db = _dbs['sns'] || _dbs['sns_star'];
|
||
if (!db) {
|
||
initAllDBs();
|
||
db = _dbs['sns'] || _dbs['sns_star'];
|
||
}
|
||
if (!db) return {error: 'SNS DB not loaded. Open WeChat moments first.', success: false};
|
||
var snsId = Date.now();
|
||
var createTime = Math.floor(Date.now() / 1000);
|
||
// 尝试写入StarSnsInfo
|
||
var sql = "INSERT INTO StarSnsInfo (snsId, userName, createTime, content, localFlag, type, sourceType) " +
|
||
"VALUES (" + snsId + ", 'wxid_5g37snchpv8e22', " + createTime + ", '" + content.replace(/'/g, "''") + "', 1, 1, 0)";
|
||
var r = writeSQL(db, sql);
|
||
if (r.success) return {success: true, snsId: snsId, content: content, note: 'Written to local SNS DB.'};
|
||
// 尝试SnsInfo
|
||
sql = "INSERT INTO SnsInfo (snsId, userName, createTime, content, localFlag, type) " +
|
||
"VALUES (" + snsId + ", 'wxid_5g37snchpv8e22', " + createTime + ", '" + content.replace(/'/g, "''") + "', 1, 1)";
|
||
r = writeSQL(db, sql);
|
||
if (r.success) return {success: true, snsId: snsId, content: content, note: 'Written to SnsInfo.'};
|
||
return {success: false, error: r.error};
|
||
},
|
||
|
||
// ===== 视频号 =====
|
||
getFinderInfo: function() {
|
||
var db = getDB('finder');
|
||
if (!db) return {error: 'Finder DB not available'};
|
||
var tables = execSQL(db, "SELECT name FROM sqlite_master WHERE type='table'");
|
||
var result = {tables: tables.rows.map(function(r) { return r.name; })};
|
||
// 读取FinderFeedItem
|
||
var feeds = execSQL(db, "SELECT * FROM FinderFeedItem LIMIT 5");
|
||
if (!feeds.error) result.feeds = feeds;
|
||
return result;
|
||
},
|
||
|
||
getFinderContacts: function(limit) {
|
||
var db = getDB('finder');
|
||
if (!db) return {error: 'Finder DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 20;
|
||
return execSQL(db, "SELECT * FROM FinderMsgContactStorage LIMIT " + limit);
|
||
},
|
||
|
||
getFinderAccounts: function() {
|
||
var db = getDB('finder3');
|
||
if (!db) return {error: 'FinderAccounts DB not available'};
|
||
var tables = execSQL(db, "SELECT name FROM sqlite_master WHERE type='table'");
|
||
var result = {tables: tables.rows.map(function(r) { return r.name; })};
|
||
var accounts = execSQL(db, "SELECT username, nickname, signature, avatarUrl FROM FinderAccount LIMIT 10");
|
||
if (!accounts.error) result.accounts = accounts;
|
||
return result;
|
||
},
|
||
|
||
// ===== 收藏 =====
|
||
getFavorites: function(limit) {
|
||
var db = getDB('fts5') || getDB('favorite');
|
||
if (!db) return {error: 'Favorite DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 20;
|
||
// 尝试不同表名
|
||
var tables = ['FTS5MetaFavorite', 'favorite_item', 'FavoriteItem'];
|
||
for (var i = 0; i < tables.length; i++) {
|
||
var r = execSQL(db, "SELECT * FROM " + tables[i] + " LIMIT " + limit);
|
||
if (!r.error) return r;
|
||
}
|
||
// 列出所有表
|
||
var allTables = execSQL(db, "SELECT name FROM sqlite_master WHERE type='table'");
|
||
return {error: 'No favorite table found', available_tables: allTables.rows.map(function(r){return r.name;}), rows: [], count: 0};
|
||
},
|
||
|
||
// ===== 钱包/支付 =====
|
||
getWalletInfo: function() {
|
||
var db = getDB('wallet');
|
||
if (!db) return {error: 'Wallet DB not available'};
|
||
var tables = execSQL(db, "SELECT name FROM sqlite_master WHERE type='table'");
|
||
return {tables: tables.rows.map(function(r) { return r.name; })};
|
||
},
|
||
|
||
getWalletLedger: function(limit) {
|
||
var db = getDB('wallet');
|
||
if (!db) return {error: 'Wallet DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 20;
|
||
var r = execSQL(db, "SELECT * FROM WalletLedgerRecord ORDER BY rowid DESC LIMIT " + limit);
|
||
return r;
|
||
},
|
||
|
||
getLuckyMoney: function(limit) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 20;
|
||
return execSQL(db, "SELECT * FROM WalletLuckyMoney ORDER BY rowid DESC LIMIT " + limit);
|
||
},
|
||
|
||
// ===== 媒体 =====
|
||
getImageCount: function() {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
return execSQL(db, "SELECT COUNT(*) as total FROM ImgInfo2");
|
||
},
|
||
|
||
getVideoList: function(limit) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 10;
|
||
return execSQL(db, "SELECT * FROM videoinfo2 ORDER BY rowid DESC LIMIT " + limit);
|
||
},
|
||
|
||
getFileList: function(limit) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available', rows: [], count: 0};
|
||
limit = parseInt(limit) || 10;
|
||
return execSQL(db, "SELECT * FROM FileMsgInfo ORDER BY rowid DESC LIMIT " + limit);
|
||
},
|
||
|
||
// ===== 小程序 =====
|
||
getAppBrandList: function() {
|
||
var db = getDB('appbrand');
|
||
if (!db) return {error: 'AppBrand DB not available'};
|
||
var tables = execSQL(db, "SELECT name FROM sqlite_master WHERE type='table'");
|
||
var result = {tables: tables.rows.map(function(r) { return r.name; })};
|
||
var apps = execSQL(db, "SELECT * FROM AppBrandCommonUseApp ORDER BY useCount DESC LIMIT 10");
|
||
if (!apps.error) result.common_apps = apps;
|
||
return result;
|
||
},
|
||
|
||
// ===== 搜索 =====
|
||
globalSearch: function(keyword) {
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
var contacts = execSQL(db,
|
||
"SELECT username, nickName, conRemark FROM rcontact " +
|
||
"WHERE (nickName LIKE '%" + keyword + "%' OR conRemark LIKE '%" + keyword + "%') AND type IN (3,4) LIMIT 10"
|
||
);
|
||
var groups = execSQL(db,
|
||
"SELECT chatroomname FROM chatroom WHERE chatroomname LIKE '%" + keyword + "%' LIMIT 5"
|
||
);
|
||
var messages = execSQL(db,
|
||
"SELECT msgId, content, talker, createTime FROM message WHERE content LIKE '%" + keyword + "%' ORDER BY createTime DESC LIMIT 10"
|
||
);
|
||
return {contacts: contacts, groups: groups, messages: messages};
|
||
},
|
||
|
||
// ===== 原始SQL(调试用)=====
|
||
rawQuery: function(dbName, sql) {
|
||
var db = getDB(dbName);
|
||
if (!db) return {error: 'DB ' + dbName + ' not available', rows: [], count: 0};
|
||
return execSQL(db, sql);
|
||
},
|
||
|
||
rawWrite: function(dbName, sql) {
|
||
var db = getDB(dbName);
|
||
if (!db) return {error: 'DB ' + dbName + ' not available'};
|
||
return writeSQL(db, sql);
|
||
},
|
||
|
||
getTableInfo: function(dbName, tableName) {
|
||
var db = getDB(dbName);
|
||
if (!db) return {error: 'DB ' + dbName + ' not available'};
|
||
return execSQL(db, "PRAGMA table_info(" + tableName + ")");
|
||
},
|
||
|
||
listTables: function(dbName) {
|
||
var db = getDB(dbName);
|
||
if (!db) return {error: 'DB ' + dbName + ' not available'};
|
||
return execSQL(db, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name");
|
||
},
|
||
|
||
// ===== 添加好友 =====
|
||
addFriend: function(wxid, message) {
|
||
// 写入AddContactScene表(如果存在)
|
||
var db = getDB('main');
|
||
if (!db) return {error: 'main DB not available'};
|
||
message = message || '你好,我是游条姐';
|
||
// 检查是否已是好友
|
||
var existing = execSQL(db, "SELECT username, type FROM rcontact WHERE username='" + wxid + "'");
|
||
if (existing.rows[0] && existing.rows[0].type === '3') {
|
||
return {success: false, note: 'Already a friend', wxid: wxid};
|
||
}
|
||
// 记录添加请求(实际网络发送需要真实类名)
|
||
return {
|
||
success: true,
|
||
wxid: wxid,
|
||
message: message,
|
||
note: 'Add friend request prepared. Network send requires WeChat 8.0.56 real class name.',
|
||
existing: existing.rows[0] || null
|
||
};
|
||
},
|
||
|
||
// ===== 截图 =====
|
||
takeScreenshot: function(savePath) {
|
||
try {
|
||
var Runtime = Java.use('java.lang.Runtime');
|
||
var rt = Runtime.getRuntime();
|
||
savePath = savePath || '/sdcard/workphone_screenshot.png';
|
||
rt.exec(['sh', '-c', 'screencap -p ' + savePath]);
|
||
return {success: true, path: savePath};
|
||
} catch(e) {
|
||
return {error: e.toString()};
|
||
}
|
||
},
|
||
|
||
// ===== 导航 =====
|
||
navigate: function(activity) {
|
||
try {
|
||
var Intent = Java.use('android.content.Intent');
|
||
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
|
||
var cls;
|
||
var activityMap = {
|
||
'main': 'com.tencent.mm.ui.LauncherUI',
|
||
'moments': 'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI',
|
||
'contacts': 'com.tencent.mm.ui.contact.AddressBookUI',
|
||
'chat': 'com.tencent.mm.ui.chatting.ChattingUI',
|
||
'finder': 'com.tencent.mm.plugin.finder.ui.FinderUI',
|
||
'wallet': 'com.tencent.mm.plugin.wallet.ui.WalletUI'
|
||
};
|
||
var activityName = activityMap[activity] || activity;
|
||
cls = Java.use(activityName).class;
|
||
var intent = Intent.$new(ctx, cls);
|
||
intent.addFlags(0x10000000); // FLAG_ACTIVITY_NEW_TASK
|
||
ctx.startActivity(intent);
|
||
return {success: true, activity: activityName};
|
||
} catch(e) {
|
||
return {error: e.toString()};
|
||
}
|
||
}
|
||
};
|