Impl save audio message. Close #14

This commit is contained in:
Changhua 2023-12-03 16:35:42 +08:00
parent 03a954916c
commit d5a208aae4
12 changed files with 195 additions and 55 deletions

View File

@ -12,6 +12,7 @@ enum Functions {
FUNC_GET_DB_NAMES = 0x13;
FUNC_GET_DB_TABLES = 0x14;
FUNC_GET_USER_INFO = 0x15;
FUNC_GET_AUDIO_MSG = 0x16;
FUNC_SEND_TXT = 0x20;
FUNC_SEND_IMG = 0x21;
FUNC_SEND_FILE = 0x22;
@ -49,6 +50,7 @@ message Request
uint64 ui64 = 12; // 64
bool flag = 13;
AttachMsg att = 14;
AudioMsg am = 15;
}
}
@ -187,3 +189,9 @@ message AttachMsg
string thumb = 2; // thumb
string extra = 3; // extra
}
message AudioMsg
{
uint64 id = 1; // id
string dir = 2; //
}

BIN
WeChatFerry/smc/Codec.lib Normal file

Binary file not shown.

18
WeChatFerry/smc/codec.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
typedef struct {
double filetime;
int32_t totPackets;
} DecTime_t;
int Mp3Encode(std::vector<uint8_t> &pcm, std::string &mp3path, int32_t sr);
int Mp3Encode(std::vector<uint8_t> &pcm, std::vector<uint8_t> &mp3, int32_t sr);
DecTime_t SilkDecode(std::vector<uint8_t> &silk, std::vector<uint8_t> &pcm, int32_t sr);
int Silk2Mp3(std::string inpath, std::string outpath, int sr);
int Silk2Mp3(std::vector<uint8_t> &silk, std::string mp3path, int sr);
int Silk2Mp3(std::vector<uint8_t> &silk, std::vector<uint8_t> &mp3, int sr);

View File

@ -105,7 +105,7 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile />
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@ -118,8 +118,10 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
<AdditionalLibraryDirectories>$(SolutionDir)smc;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
<PostBuildEvent>
<Command>if not exist $(SolutionDir)Out md $(SolutionDir)Out
@ -151,7 +153,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile />
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@ -163,8 +165,10 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
<AdditionalLibraryDirectories>$(SolutionDir)smc;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
<PostBuildEvent>
<Command>if not exist $(SolutionDir)Out md $(SolutionDir)Out
@ -196,6 +200,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -226,6 +231,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<ClInclude Include="..\rpc\pb_types.h" />
<ClInclude Include="..\rpc\pb_util.h" />
<ClInclude Include="..\rpc\proto\wcf.pb.h" />
<ClInclude Include="..\smc\codec.h" />
<ClInclude Include="chatroom_mgmt.h" />
<ClInclude Include="funcs.h" />
<ClInclude Include="exec_sql.h" />

View File

@ -90,6 +90,9 @@
<ClInclude Include="sqlite3.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\smc\codec.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">

View File

@ -195,3 +195,33 @@ int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
return -1;
}
vector<uint8_t> GetAudioData(uint64_t id)
{
DWORD msgMgrAddr = GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
DWORD dbIndex = GET_DWORD(msgMgrAddr + 0x38);
string sql = "SELECT Buf from Media WHERE Reserved0=" + to_string(id) + ";";
for (int i = dbIndex - 1; i >= 0; i--) {
string dbname = "MediaMSG" + to_string(i) + ".db";
DbRows_t rows = ExecDbQuery(dbname, sql);
if (rows.empty()) {
continue;
}
DbRow_t row = rows.front();
if (row.empty()) {
continue;
}
DbField_t field = row.front();
if (field.column.compare("Buf") != 0) {
continue;
}
// 首字节为 0x02估计是混淆用的去掉。
vector<uint8_t> rv(field.content.begin() + 1, field.content.end());
return rv;
}
return vector<uint8_t>();
}

View File

@ -1,8 +1,11 @@
#pragma once
#include <vector>
#include "pb_types.h"
DbNames_t GetDbNames();
DbTables_t GetDbTables(const string db);
DbRows_t ExecDbQuery(const string db, const string sql);
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx);
vector<uint8_t> GetAudioData(uint64_t msgid);

View File

@ -4,6 +4,7 @@
#include <filesystem>
#include <fstream>
#include "codec.h"
#include "exec_sql.h"
#include "funcs.h"
#include "log.h"
@ -93,6 +94,8 @@ string DecryptImage(string src, string dir)
}
replace(dst.begin(), dst.end(), '\\', '/');
} catch (const std::exception &e) {
LOG_ERROR(GB2312ToUtf8(e.what()));
} catch (...) {
LOG_ERROR("Unknow exception.");
return "";
@ -313,3 +316,23 @@ int RevokeMsg(uint64_t id)
return status;
}
string GetAudio(uint64_t id, string dir)
{
string mp3path = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/");
mp3path += to_string(id) + ".mp3";
replace(mp3path.begin(), mp3path.end(), '\\', '/');
if (fs::exists(mp3path)) { // 不重复下载
return mp3path;
}
vector<uint8_t> silk = GetAudioData(id);
if (silk.size() == 0) {
LOG_ERROR("Empty audio data.");
return "";
}
Silk2Mp3(silk, mp3path, 24000);
return mp3path;
}

View File

@ -3,7 +3,8 @@
#include "stdint.h"
#include <string>
string DecryptImage(std::string src, std::string dst);
std::string GetAudio(uint64_t id, std::string dir);
std::string DecryptImage(std::string src, std::string dst);
int RefreshPyq(uint64_t id);
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
int RevokeMsg(uint64_t id);

View File

@ -187,6 +187,30 @@ bool func_get_user_info(uint8_t *out, size_t *len)
return true;
}
bool func_get_audio_msg(uint64_t id, char *dir, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_GET_AUDIO_MSG;
rsp.which_msg = Response_str_tag;
string path = string(dir ? dir : "");
if (path.empty()) {
LOG_ERROR("Empty dir.");
rsp.msg.str = (char *)"";
} else {
rsp.msg.str = (char *)GetAudio(id, dir).c_str();
}
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_send_txt(TextMsg txt, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
@ -692,6 +716,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_get_user_info(out, out_len);
break;
}
case Functions_FUNC_GET_AUDIO_MSG: {
LOG_DEBUG("[Functions_FUNC_GET_AUDIO_MSG]");
ret = func_get_audio_msg(req.msg.am.id, req.msg.am.dir, out, out_len);
break;
}
case Functions_FUNC_SEND_TXT: {
LOG_DEBUG("[Functions_FUNC_SEND_TXT]");
ret = func_send_txt(req.msg.txt, out, out_len);

View File

@ -234,6 +234,23 @@ class Wcf():
return ui
def get_audio_msg(self, id: int, dir: str) -> str:
"""获取语音消息并转成 MP3
Args:
id (int): 语音消息 id
dir (str): MP3 保存目录目录不存在会出错
Returns:
str: 成功返回存储路径空字符串为失败原因见日志
"""
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_GET_AUDIO_MSG # FUNC_GET_AUDIO_MSG
req.am.id = id
req.am.dir = dir
rsp = self._send_request(req)
return rsp.str
def send_text(self, msg: str, receiver: str, aters: Optional[str] = "") -> int:
"""发送文本消息

File diff suppressed because one or more lines are too long