diff --git a/WeChatFerry/rpc/proto/wcf.proto b/WeChatFerry/rpc/proto/wcf.proto
index 157b147..d151296 100644
--- a/WeChatFerry/rpc/proto/wcf.proto
+++ b/WeChatFerry/rpc/proto/wcf.proto
@@ -22,6 +22,7 @@ enum Functions {
FUNC_EXEC_DB_QUERY = 0x50;
FUNC_ACCEPT_FRIEND = 0x51;
FUNC_RECV_TRANSFER = 0x52;
+ FUNC_REFRESH_PYQ = 0x53;
FUNC_DECRYPT_IMAGE = 0x60;
FUNC_ADD_ROOM_MEMBERS = 0x70;
FUNC_DEL_ROOM_MEMBERS = 0x71;
diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj
index e28f3f9..0b34661 100644
--- a/WeChatFerry/spy/Spy.vcxproj
+++ b/WeChatFerry/spy/Spy.vcxproj
@@ -233,6 +233,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
+
@@ -257,6 +258,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
+
diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters
index 64edd64..967ac9d 100644
--- a/WeChatFerry/spy/Spy.vcxproj.filters
+++ b/WeChatFerry/spy/Spy.vcxproj.filters
@@ -90,6 +90,9 @@
头文件
+
+ 头文件
+
@@ -149,6 +152,9 @@
源文件
+
+ 源文件
+
diff --git a/WeChatFerry/spy/pyq.cpp b/WeChatFerry/spy/pyq.cpp
new file mode 100644
index 0000000..2ef946c
--- /dev/null
+++ b/WeChatFerry/spy/pyq.cpp
@@ -0,0 +1,70 @@
+#include "framework.h"
+
+#include "spy_types.h"
+#include "util.h"
+
+extern WxCalls_t g_WxCalls;
+extern DWORD g_WeChatWinDllAddr;
+
+typedef struct RawVector {
+ DWORD start;
+ DWORD finish;
+ DWORD end;
+} RawVector_t;
+
+static int GetFirstPage()
+{
+ int rv = -1;
+ DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680;
+ DWORD pyqCall2 = g_WeChatWinDllAddr + 0x14E2140;
+
+ char buf[0xB44] = { 0 };
+ __asm {
+ pushad;
+ call pyqCall1;
+ push 0x1;
+ lea ecx, buf;
+ push ecx;
+ mov ecx, eax;
+ call pyqCall2;
+ mov rv, eax;
+ popad;
+ }
+
+ return rv;
+}
+
+static int GetNextPage(uint64_t id)
+{
+ int rv = -1;
+ DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680;
+ DWORD pyqCall3 = g_WeChatWinDllAddr + 0x14E21E0;
+
+ RawVector_t tmp = { 0 };
+
+ __asm {
+ pushad;
+ call pyqCall1;
+ lea ecx, tmp;
+ push ecx;
+ mov ebx, dword ptr [id + 0x04];
+ push ebx;
+ mov edi, dword ptr [id]
+ push edi;
+ mov ecx, eax;
+ call pyqCall3;
+ mov rv, eax;
+ popad;
+ }
+
+ return rv;
+}
+
+int RefreshPyq(uint64_t id)
+{
+ if (id == 0) {
+ return GetFirstPage();
+ }
+
+ return GetNextPage(id);
+}
diff --git a/WeChatFerry/spy/pyq.h b/WeChatFerry/spy/pyq.h
new file mode 100644
index 0000000..13d7eeb
--- /dev/null
+++ b/WeChatFerry/spy/pyq.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "stdint.h"
+
+int RefreshPyq(uint64_t id);
diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp
index 1f69c1b..f54eb7d 100644
--- a/WeChatFerry/spy/receive_msg.cpp
+++ b/WeChatFerry/spy/receive_msg.cpp
@@ -6,6 +6,7 @@
#include
#include "load_calls.h"
+#include "log.h"
#include "receive_msg.h"
#include "user_info.h"
#include "util.h"
@@ -26,9 +27,16 @@ static DWORD recvMsgCallAddr = 0;
static DWORD recvMsgJumpBackAddr = 0;
static CHAR recvMsgBackupCode[5] = { 0 };
+static DWORD recvPyqHookAddr = 0;
+static DWORD recvPyqCallAddr = 0;
+static DWORD recvPyqJumpBackAddr = 0;
+static CHAR recvPyqBackupCode[5] = { 0 };
+static bool gIsListeningPyq = false;
+
MsgTypes_t GetMsgTypes()
{
const MsgTypes_t m = {
+ { 0x00, "朋友圈消息" },
{ 0x01, "文字" },
{ 0x03, "图片" },
{ 0x22, "语音" },
@@ -164,6 +172,7 @@ void ListenMessage()
HookAddress(recvMsgHookAddr, RecieveMsgFunc, recvMsgBackupCode);
gIsListening = true;
+ ListenPyq();
}
void UnListenMessage()
@@ -173,4 +182,75 @@ void UnListenMessage()
}
UnHookAddress(recvMsgHookAddr, recvMsgBackupCode);
gIsListening = false;
+ UnListenPyq();
+}
+
+void DispatchPyq(DWORD reg)
+{
+ DWORD startAddr = *(DWORD *)(reg + 0x20);
+ DWORD endAddr = *(DWORD *)(reg + 0x24);
+
+ if (startAddr == 0) {
+ return;
+ }
+
+ while (startAddr < endAddr) {
+ WxMsg_t wxMsg;
+
+ wxMsg.type = 0x00; // 朋友圈消息
+ wxMsg.is_self = 0x00;
+ wxMsg.id = GET_QWORD(startAddr);
+ wxMsg.ts = GET_DWORD(startAddr + 0x2C);
+ wxMsg.xml = GetStringByWstrAddr(startAddr + 0x384);
+ wxMsg.sender = GetStringByWstrAddr(startAddr + 0x18);
+ wxMsg.content = GetStringByWstrAddr(startAddr + 0x3C);
+
+ {
+ unique_lock lock(gMutex);
+ gMsgQueue.push(wxMsg); // 推送到队列
+ }
+
+ gCV.notify_all(); // 通知各方消息就绪
+
+ startAddr += 0xB48;
+ }
+}
+
+__declspec(naked) void RecievePyqFunc()
+{
+ __asm {
+ pushad
+ pushfd
+ push [esp + 0x24]
+ call DispatchPyq
+ add esp, 0x4
+ popfd
+ popad
+ call recvPyqCallAddr // 这个为被覆盖的call
+ jmp recvPyqJumpBackAddr // 跳回被HOOK指令的下一条指令
+ }
+}
+
+void ListenPyq()
+{
+ if (gIsListeningPyq || (g_WeChatWinDllAddr == 0)) {
+ return;
+ }
+
+ recvPyqHookAddr = g_WeChatWinDllAddr + 0x14F9E15;
+ recvPyqCallAddr = g_WeChatWinDllAddr + 0x14FA0A0;
+ recvPyqJumpBackAddr = recvPyqHookAddr + 5;
+
+ HookAddress(recvPyqHookAddr, RecievePyqFunc, recvPyqBackupCode);
+ gIsListeningPyq = true;
+}
+
+void UnListenPyq()
+{
+ if (!gIsListeningPyq) {
+ return;
+ }
+
+ UnHookAddress(recvPyqHookAddr, recvPyqBackupCode);
+ gIsListeningPyq = false;
}
diff --git a/WeChatFerry/spy/receive_msg.h b/WeChatFerry/spy/receive_msg.h
index f878c18..ff17170 100644
--- a/WeChatFerry/spy/receive_msg.h
+++ b/WeChatFerry/spy/receive_msg.h
@@ -2,6 +2,8 @@
#include "pb_types.h"
+void ListenPyq();
+void UnListenPyq();
void ListenMessage();
void UnListenMessage();
MsgTypes_t GetMsgTypes();
diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp
index 09de304..3682340 100644
--- a/WeChatFerry/spy/rpc_server.cpp
+++ b/WeChatFerry/spy/rpc_server.cpp
@@ -23,6 +23,7 @@
#include "log.h"
#include "pb_types.h"
#include "pb_util.h"
+#include "pyq.h"
#include "receive_msg.h"
#include "receive_transfer.h"
#include "rpc_server.h"
@@ -345,16 +346,18 @@ static void PushMessage()
if (gCV.wait_for(lock, chrono::milliseconds(1000), []() { return !gMsgQueue.empty(); })) {
while (!gMsgQueue.empty()) {
auto wxmsg = gMsgQueue.front();
+ rsp.msg.wxmsg.id = wxmsg.id;
rsp.msg.wxmsg.is_self = wxmsg.is_self;
rsp.msg.wxmsg.is_group = wxmsg.is_group;
rsp.msg.wxmsg.type = wxmsg.type;
- rsp.msg.wxmsg.id = (char *)wxmsg.id.c_str();
- rsp.msg.wxmsg.xml = (char *)wxmsg.xml.c_str();
- rsp.msg.wxmsg.sender = (char *)wxmsg.sender.c_str();
+ rsp.msg.wxmsg.ts = wxmsg.ts;
rsp.msg.wxmsg.roomid = (char *)wxmsg.roomid.c_str();
rsp.msg.wxmsg.content = (char *)wxmsg.content.c_str();
+ rsp.msg.wxmsg.sender = (char *)wxmsg.sender.c_str();
+ rsp.msg.wxmsg.sign = (char *)wxmsg.sign.c_str();
rsp.msg.wxmsg.thumb = (char *)wxmsg.thumb.c_str();
rsp.msg.wxmsg.extra = (char *)wxmsg.extra.c_str();
+ rsp.msg.wxmsg.xml = (char *)wxmsg.xml.c_str();
gMsgQueue.pop();
LOG_DEBUG("Recv msg: {}", wxmsg.content);
pb_ostream_t stream = pb_ostream_from_buffer(buffer, G_BUF_SIZE);
@@ -486,6 +489,24 @@ bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, siz
return true;
}
+bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len)
+{
+ Response rsp = Response_init_default;
+ rsp.func = Functions_FUNC_REFRESH_PYQ;
+ rsp.which_msg = Response_status_tag;
+
+ rsp.msg.status = RefreshPyq(id);
+
+ pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
+ if (!pb_encode(&stream, Response_fields, &rsp)) {
+ LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
+ return false;
+ }
+ *len = stream.bytes_written;
+
+ return true;
+}
+
bool func_decrypt_image(char *src, char *dst, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
@@ -652,6 +673,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_receive_transfer(req.msg.tf.wxid, req.msg.tf.tfid, req.msg.tf.taid, out, out_len);
break;
}
+ case Functions_FUNC_REFRESH_PYQ: {
+ LOG_DEBUG("[Functions_FUNC_REFRESH_PYQ]");
+ ret = func_refresh_pyq(req.msg.ui64, out, out_len);
+ break;
+ }
case Functions_FUNC_DECRYPT_IMAGE: {
LOG_DEBUG("[FUNCTIONS_FUNC_DECRYPT_IMAGE]");
ret = func_decrypt_image(req.msg.dec.src, req.msg.dec.dst, out, out_len);
diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py
index 5ef85b4..6919dca 100644
--- a/clients/python/wcferry/client.py
+++ b/clients/python/wcferry/client.py
@@ -526,6 +526,22 @@ class Wcf():
rsp = self._send_request(req)
return rsp.status
+ def refresh_pyq(self, id: int) -> int:
+ """刷新朋友圈
+
+ Args:
+ id (int): 开始 id
+
+ Returns:
+ int: 1 为成功,其他失败
+ """
+ req = wcf_pb2.Request()
+ req.func = wcf_pb2.FUNC_REFRESH_PYQ # FUNC_REFRESH_PYQ
+ req.ui64 = id
+ rsp = self._send_request(req)
+ return rsp.status
+
+
def decrypt_image(self, src: str, dst: str) -> bool:
"""解密图片:
diff --git a/clients/python/wcferry/wcf_pb2.py b/clients/python/wcferry/wcf_pb2.py
index 94b36fc..c5a543f 100644
--- a/clients/python/wcferry/wcf_pb2.py
+++ b/clients/python/wcferry/wcf_pb2.py
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xc8\x02\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.AddMembersH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x42\x05\n\x03msg\"\xab\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xa0\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0c\n\x04type\x18\x03 \x01(\x05\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0b\n\x03xml\x18\x05 \x01(\t\x12\x0e\n\x06sender\x18\x06 \x01(\t\x12\x0e\n\x06roomid\x18\x07 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x08 \x01(\t\x12\r\n\x05thumb\x18\t \x01(\t\x12\r\n\x05\x65xtra\x18\n \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x05\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nAddMembers\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t*\xee\x03\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xd8\x02\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.AddMembersH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x12\x0e\n\x04ui64\x18\x0c \x01(\x04H\x00\x42\x05\n\x03msg\"\xab\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xba\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\n\n\x02id\x18\x03 \x01(\x04\x12\x0c\n\x04type\x18\x04 \x01(\r\x12\n\n\x02ts\x18\x05 \x01(\r\x12\x0e\n\x06roomid\x18\x06 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\t\x12\x0e\n\x06sender\x18\x08 \x01(\t\x12\x0c\n\x04sign\x18\t \x01(\t\x12\r\n\x05thumb\x18\n \x01(\t\x12\r\n\x05\x65xtra\x18\x0b \x01(\t\x12\x0b\n\x03xml\x18\x0c \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x05\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nAddMembers\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t*\xb9\x04\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x18\n\x14\x46UNC_ENABLE_RECV_PYQ\x10\x31\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x19\n\x15\x46UNC_DISABLE_RECV_PYQ\x10\x41\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x14\n\x10\x46UNC_REFRESH_PYQ\x10S\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3')
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', globals())
@@ -23,52 +23,52 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._serialized_options = b'\n\013com.iamteer'
_MSGTYPES_TYPESENTRY._options = None
_MSGTYPES_TYPESENTRY._serialized_options = b'8\001'
- _FUNCTIONS._serialized_start=1820
- _FUNCTIONS._serialized_end=2314
+ _FUNCTIONS._serialized_start=1862
+ _FUNCTIONS._serialized_end=2431
_REQUEST._serialized_start=19
- _REQUEST._serialized_end=347
- _RESPONSE._serialized_start=350
- _RESPONSE._serialized_end=649
- _EMPTY._serialized_start=651
- _EMPTY._serialized_end=658
- _WXMSG._serialized_start=661
- _WXMSG._serialized_end=821
- _TEXTMSG._serialized_start=823
- _TEXTMSG._serialized_end=878
- _PATHMSG._serialized_start=880
- _PATHMSG._serialized_end=921
- _XMLMSG._serialized_start=923
- _XMLMSG._serialized_end=994
- _MSGTYPES._serialized_start=996
- _MSGTYPES._serialized_end=1093
- _MSGTYPES_TYPESENTRY._serialized_start=1049
- _MSGTYPES_TYPESENTRY._serialized_end=1093
- _RPCCONTACT._serialized_start=1096
- _RPCCONTACT._serialized_end=1231
- _RPCCONTACTS._serialized_start=1233
- _RPCCONTACTS._serialized_end=1281
- _DBNAMES._serialized_start=1283
- _DBNAMES._serialized_end=1307
- _DBTABLE._serialized_start=1309
- _DBTABLE._serialized_end=1345
- _DBTABLES._serialized_start=1347
- _DBTABLES._serialized_end=1387
- _DBQUERY._serialized_start=1389
- _DBQUERY._serialized_end=1423
- _DBFIELD._serialized_start=1425
- _DBFIELD._serialized_end=1481
- _DBROW._serialized_start=1483
- _DBROW._serialized_end=1520
- _DBROWS._serialized_start=1522
- _DBROWS._serialized_end=1556
- _VERIFICATION._serialized_start=1558
- _VERIFICATION._serialized_end=1611
- _ADDMEMBERS._serialized_start=1613
- _ADDMEMBERS._serialized_end=1656
- _USERINFO._serialized_start=1658
- _USERINFO._serialized_end=1726
- _DECPATH._serialized_start=1728
- _DECPATH._serialized_end=1763
- _TRANSFER._serialized_start=1765
- _TRANSFER._serialized_end=1817
+ _REQUEST._serialized_end=363
+ _RESPONSE._serialized_start=366
+ _RESPONSE._serialized_end=665
+ _EMPTY._serialized_start=667
+ _EMPTY._serialized_end=674
+ _WXMSG._serialized_start=677
+ _WXMSG._serialized_end=863
+ _TEXTMSG._serialized_start=865
+ _TEXTMSG._serialized_end=920
+ _PATHMSG._serialized_start=922
+ _PATHMSG._serialized_end=963
+ _XMLMSG._serialized_start=965
+ _XMLMSG._serialized_end=1036
+ _MSGTYPES._serialized_start=1038
+ _MSGTYPES._serialized_end=1135
+ _MSGTYPES_TYPESENTRY._serialized_start=1091
+ _MSGTYPES_TYPESENTRY._serialized_end=1135
+ _RPCCONTACT._serialized_start=1138
+ _RPCCONTACT._serialized_end=1273
+ _RPCCONTACTS._serialized_start=1275
+ _RPCCONTACTS._serialized_end=1323
+ _DBNAMES._serialized_start=1325
+ _DBNAMES._serialized_end=1349
+ _DBTABLE._serialized_start=1351
+ _DBTABLE._serialized_end=1387
+ _DBTABLES._serialized_start=1389
+ _DBTABLES._serialized_end=1429
+ _DBQUERY._serialized_start=1431
+ _DBQUERY._serialized_end=1465
+ _DBFIELD._serialized_start=1467
+ _DBFIELD._serialized_end=1523
+ _DBROW._serialized_start=1525
+ _DBROW._serialized_end=1562
+ _DBROWS._serialized_start=1564
+ _DBROWS._serialized_end=1598
+ _VERIFICATION._serialized_start=1600
+ _VERIFICATION._serialized_end=1653
+ _ADDMEMBERS._serialized_start=1655
+ _ADDMEMBERS._serialized_end=1698
+ _USERINFO._serialized_start=1700
+ _USERINFO._serialized_end=1768
+ _DECPATH._serialized_start=1770
+ _DECPATH._serialized_end=1805
+ _TRANSFER._serialized_start=1807
+ _TRANSFER._serialized_end=1859
# @@protoc_insertion_point(module_scope)
diff --git a/clients/python/wcferry/wxmsg.py b/clients/python/wcferry/wxmsg.py
index f71ed3f..37d521d 100644
--- a/clients/python/wcferry/wxmsg.py
+++ b/clients/python/wcferry/wxmsg.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import re
+from datetime import datetime
from wcferry import wcf_pb2
@@ -24,6 +25,8 @@ class WxMsg():
self._is_group = msg.is_group
self.type = msg.type
self.id = msg.id
+ self.ts = msg.ts
+ self.sign = msg.sign
self.xml = msg.xml
self.sender = msg.sender
self.roomid = msg.roomid
@@ -33,7 +36,8 @@ class WxMsg():
def __str__(self) -> str:
s = f"{'自己发的:' if self._is_self else ''}"
- s += f"{self.sender}[{self.roomid}]:{self.id}:{self.type}:{self.xml.replace(chr(10), '').replace(chr(9),'')}\n"
+ s += f"{self.sender}[{self.roomid}]|{self.id}|{datetime.fromtimestamp(self.ts)}|{self.type}|{self.sign}"
+ s += f"\n{self.xml.replace(chr(10), '').replace(chr(9),'')}\n"
s += self.content
s += f"\n{self.thumb}" if self.thumb else ""
s += f"\n{self.extra}" if self.extra else ""