Impl save audio message. Close #14
This commit is contained in:
parent
03a954916c
commit
d5a208aae4
@ -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
BIN
WeChatFerry/smc/Codec.lib
Normal file
Binary file not shown.
18
WeChatFerry/smc/codec.h
Normal file
18
WeChatFerry/smc/codec.h
Normal 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);
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user