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_NAMES = 0x13;
|
||||||
FUNC_GET_DB_TABLES = 0x14;
|
FUNC_GET_DB_TABLES = 0x14;
|
||||||
FUNC_GET_USER_INFO = 0x15;
|
FUNC_GET_USER_INFO = 0x15;
|
||||||
|
FUNC_GET_AUDIO_MSG = 0x16;
|
||||||
FUNC_SEND_TXT = 0x20;
|
FUNC_SEND_TXT = 0x20;
|
||||||
FUNC_SEND_IMG = 0x21;
|
FUNC_SEND_IMG = 0x21;
|
||||||
FUNC_SEND_FILE = 0x22;
|
FUNC_SEND_FILE = 0x22;
|
||||||
@ -49,6 +50,7 @@ message Request
|
|||||||
uint64 ui64 = 12; // 64 位整数,通用
|
uint64 ui64 = 12; // 64 位整数,通用
|
||||||
bool flag = 13;
|
bool flag = 13;
|
||||||
AttachMsg att = 14;
|
AttachMsg att = 14;
|
||||||
|
AudioMsg am = 15;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,3 +189,9 @@ message AttachMsg
|
|||||||
string thumb = 2; // 消息中的 thumb
|
string thumb = 2; // 消息中的 thumb
|
||||||
string extra = 3; // 消息中的 extra
|
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>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</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 />
|
<PrecompiledHeaderOutputFile />
|
||||||
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
@ -118,8 +118,10 @@
|
|||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableUAC>false</EnableUAC>
|
<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>
|
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)smc;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist $(SolutionDir)Out md $(SolutionDir)Out
|
<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>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</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 />
|
<PrecompiledHeaderOutputFile />
|
||||||
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
@ -163,8 +165,10 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
|
|||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableUAC>false</EnableUAC>
|
<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>
|
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)smc;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist $(SolutionDir)Out md $(SolutionDir)Out
|
<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>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableUAC>false</EnableUAC>
|
<EnableUAC>false</EnableUAC>
|
||||||
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalOptions> /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<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_types.h" />
|
||||||
<ClInclude Include="..\rpc\pb_util.h" />
|
<ClInclude Include="..\rpc\pb_util.h" />
|
||||||
<ClInclude Include="..\rpc\proto\wcf.pb.h" />
|
<ClInclude Include="..\rpc\proto\wcf.pb.h" />
|
||||||
|
<ClInclude Include="..\smc\codec.h" />
|
||||||
<ClInclude Include="chatroom_mgmt.h" />
|
<ClInclude Include="chatroom_mgmt.h" />
|
||||||
<ClInclude Include="funcs.h" />
|
<ClInclude Include="funcs.h" />
|
||||||
<ClInclude Include="exec_sql.h" />
|
<ClInclude Include="exec_sql.h" />
|
||||||
|
@ -90,6 +90,9 @@
|
|||||||
<ClInclude Include="sqlite3.h">
|
<ClInclude Include="sqlite3.h">
|
||||||
<Filter>头文件</Filter>
|
<Filter>头文件</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\smc\codec.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="dllmain.cpp">
|
<ClCompile Include="dllmain.cpp">
|
||||||
|
@ -195,3 +195,33 @@ int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
|
|||||||
|
|
||||||
return -1;
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "pb_types.h"
|
#include "pb_types.h"
|
||||||
|
|
||||||
DbNames_t GetDbNames();
|
DbNames_t GetDbNames();
|
||||||
DbTables_t GetDbTables(const string db);
|
DbTables_t GetDbTables(const string db);
|
||||||
DbRows_t ExecDbQuery(const string db, const string sql);
|
DbRows_t ExecDbQuery(const string db, const string sql);
|
||||||
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx);
|
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 <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "codec.h"
|
||||||
#include "exec_sql.h"
|
#include "exec_sql.h"
|
||||||
#include "funcs.h"
|
#include "funcs.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@ -93,6 +94,8 @@ string DecryptImage(string src, string dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
replace(dst.begin(), dst.end(), '\\', '/');
|
replace(dst.begin(), dst.end(), '\\', '/');
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LOG_ERROR(GB2312ToUtf8(e.what()));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
LOG_ERROR("Unknow exception.");
|
LOG_ERROR("Unknow exception.");
|
||||||
return "";
|
return "";
|
||||||
@ -313,3 +316,23 @@ int RevokeMsg(uint64_t id)
|
|||||||
|
|
||||||
return status;
|
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 "stdint.h"
|
||||||
#include <string>
|
#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 RefreshPyq(uint64_t id);
|
||||||
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
||||||
int RevokeMsg(uint64_t id);
|
int RevokeMsg(uint64_t id);
|
||||||
|
@ -187,6 +187,30 @@ bool func_get_user_info(uint8_t *out, size_t *len)
|
|||||||
return true;
|
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)
|
bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len)
|
||||||
{
|
{
|
||||||
Response rsp = Response_init_default;
|
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);
|
ret = func_get_user_info(out, out_len);
|
||||||
break;
|
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: {
|
case Functions_FUNC_SEND_TXT: {
|
||||||
LOG_DEBUG("[Functions_FUNC_SEND_TXT]");
|
LOG_DEBUG("[Functions_FUNC_SEND_TXT]");
|
||||||
ret = func_send_txt(req.msg.txt, out, out_len);
|
ret = func_send_txt(req.msg.txt, out, out_len);
|
||||||
|
@ -234,6 +234,23 @@ class Wcf():
|
|||||||
|
|
||||||
return ui
|
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:
|
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