从 Win32 C++ 程序发送 WhatsApp 消息 - 第二部分






4.98/5 (17投票s)
一种从 C++ 向个人或群组发送 WhatsApp 文档和图片的简单方法
引言
本文是第二部分,紧接在第一部分之后。在本文中,我将解释如何向群组发送图片和文档。正如在第 1 部分中所提到的,有几个服务提供商,我们选择了其中一个(WhatsAppMate)并开始了免费试用。但是,他们使用其服务的代码示例几乎涵盖了所有编程语言,除了 C++。因此,我们为此编写了自己的 C++ 类。发送文档和文件稍微复杂一些,将在本文中进行解释。
背景
WhatsApp 是一个多平台的免费服务,用于通过视频或语音聊天,并向个人或群组发送消息,包括文件、媒体等。WhatsApp 比旧的短信更好,因为它免费且功能更多。在我们日常工作中,我们需要设置各种警报,发送给共享工作或处理某个功能的小组,当该功能自动完成时,它会使通知变得更容易。
Using the Code
对于 GitHub 存储库,请参阅 这个(类)和 这个(基于 MFC 的工具)。
可以发送几种类型的消息,包括个人或群组消息,以及是否附带照片或 PDF 文件。您可以在此页面上阅读相关内容。
以下是您需要构建的组件。或者,我稍后将附上一个编译的(* .exe*)测试应用程序。
//
#define GroupAdmin <YOUR GROUP ADMIN MOBILE PHONE>
#define GroupName <YOUR GROUP NAME>
#define CLIENT_ID <YOUR CLIENT ID>
#define CLIENT_SECRET <YOUR CLIENT SECRET>
#define GROUP_API_SERVER L"api.whatsmate.net"
#define GROUP_API_PATH L"/v3/whatsapp/group/text/message/12"
#define IMAGE_SINGLE_API_URL L"http://api.whatsmate.net/v3/whatsapp/group/image/message/12"
//
我们的用户界面
为了本文的目的,我们添加了一些用户界面。
正如您所看到的,您可以输入一条消息,并选择要随其发送的文档或图像。当您选择文档或图像时,该消息将显示为发送的图像/文档的标题。
发送图像和文档
当发送图像或文档时,我们可以为其添加一个“标题”,它可以是我们的消息。
发送文档的 API 与发送文档的 API 不同,因为它们在接收者的手机上显示为“附件”。但是,图像作为消息的一部分内联显示。因此,在 API 方面,这两者之间存在差异
我们的工具会检查文件的类型并相应地使用 API。
将图像或文档转换为 base64
这是我们如何将图像或文档转换为 base64 字符串
的方法。我们将使用CryptBinaryToString()。
std::wstring document = _T("");
DWORD desiredLength = 0;
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, NULL, &desiredLength);
desiredLength++;
TCHAR* base64content = (TCHAR*)malloc(desiredLength * sizeof(TCHAR));
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, base64content, &desiredLength);
连接 Internet
首先,我们打开一个互联网连接并连接到 API 服务
hOpenHandle = InternetOpen(_T("HTTP"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
return false;
}
hConnectHandle = InternetConnect(hOpenHandle,
GROUP_API_SERVER,
INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP,
0, 1);
if (hConnectHandle == NULL)
{
InternetCloseHandle(hOpenHandle);
return false;
}
打开请求
const wchar_t *AcceptTypes[] = { _T("application/json"),NULL };
HINTERNET hRequest = HttpOpenRequest(hConnectHandle, _T("POST"),
DOCUMENT_API_PATH, NULL, NULL, AcceptTypes, 0, 0);
if (hRequest == NULL)
{
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return false;
}
或者当我们希望发送图像时
const wchar_t *AcceptTypes[] = { _T("application/json"),NULL };
HINTERNET hRequest = HttpOpenRequest(hConnectHandle, _T("POST"),
IMAGE_API_PATH, NULL, NULL, AcceptTypes, 0, 0);
if (hRequest == NULL)
{
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return false;
}
同时我们定义 API 路径如下
#define DOCUMENT_API_PATH L"/v3/whatsapp/group/document/message/12"
#define IMAGE_API_PATH L"/v3/whatsapp/group/image/message/12"
标题栏 (Header)
接下来,我们发布使用以下代码组成的 Header
std::wstring HeaderData;
HeaderData += _T("X-WM-CLIENT-ID: ");
HeaderData += _T(CLIENT_ID);
HeaderData += _T("\r\nX-WM-CLIENT-SECRET: ");
HeaderData += _T(CLIENT_SECRET);
HeaderData += _T("\r\n");
HttpAddRequestHeaders(hRequest, HeaderData.c_str(), HeaderData.size(), NULL);
SendGroupDocument() 函数
以下是将文档发送到 WhatsApp 群组的完整函数
bool SGWhatsApp::SendGroupDocument(LPCTSTR ClientID, LPCTSTR ClientSecret,
LPCTSTR groupAdmin, LPCTSTR groupName, /*LPCTSTR message,*/ LPCTSTR filename,
LPBYTE attachment, int length)
{
BOOL bResults = FALSE;
HINTERNET hOpenHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");
hOpenHandle = InternetOpen(_T("HTTP"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
return bResults;
}
hConnectHandle = InternetConnect(hOpenHandle,
DOCUMENT_API_SERVER,
INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP,
0, 1);
if (hConnectHandle == NULL)
{
InternetCloseHandle(hOpenHandle);
return bResults;
}
const wchar_t *AcceptTypes[] = { _T("application/json"),NULL };
HINTERNET hRequest = HttpOpenRequest(hConnectHandle, _T("POST"), DOCUMENT_API_PATH,
NULL, NULL, AcceptTypes, 0, 0);
if (hRequest == NULL)
{
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return bResults;
}
std::wstring HeaderData;
HeaderData += _T("X-WM-CLIENT-ID: ");
HeaderData += ClientID;
HeaderData += _T("\r\nX-WM-CLIENT-SECRET: ");
HeaderData += ClientSecret;
HeaderData += _T("\r\n");
HttpAddRequestHeaders(hRequest, HeaderData.c_str(), HeaderData.size(), NULL);
std::wstring document = _T("");
DWORD desiredLength = 0;
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, NULL, &desiredLength);
desiredLength++;
TCHAR* base64content = (TCHAR*)malloc(desiredLength * sizeof(TCHAR));
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, base64content, &desiredLength);
std::wstring WJsonData;
WJsonData += _T("{");
WJsonData += _T("\"group_admin\":\"");
WJsonData += groupAdmin;
WJsonData += _T("\",");
WJsonData += _T("\"group_name\":\"");
WJsonData += groupName;
WJsonData += _T("\",");
WJsonData += _T("\"filename\":\"");
WJsonData += filename;
WJsonData += _T("\",");
WJsonData += _T("\"document\":\"");
// Needed to remove CRLF and all spaces symbols
for(size_t i=0; i<lstrlen(base64content); i++)
if (!isspace(base64content[i]))
WJsonData += base64content[i];
WJsonData += _T("\"");
WJsonData += _T("}");
free(base64content);
const std::string JsonData(WJsonData.begin(), WJsonData.end());
bResults = HttpSendRequest(hRequest, NULL, 0, (LPVOID)(JsonData.c_str()), JsonData.size());
TCHAR StatusText[BUFFER_LENGTH] = { 0 };
DWORD StatusTextLen = BUFFER_LENGTH;
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_TEXT, &StatusText, &StatusTextLen, NULL);
bResults = (StatusTextLen && wcscmp(StatusText, L"OK") == FALSE);
DWORD availableBytes = 0;
DWORD downloadedBytes = 0;
LPBYTE nextBytes = (LPBYTE)malloc((availableBytes +1) * sizeof(TCHAR));
memset(nextBytes, 0, (availableBytes + 1) * sizeof(TCHAR));
InternetQueryDataAvailable(hRequest, &availableBytes, 0, 0);
InternetReadFile(hRequest, nextBytes, availableBytes, &downloadedBytes);
free(nextBytes);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return bResults;
}
接收者将看到如下的 WhatsApp 消息
但为了确定,此时我们需要知道一切是否顺利。这是通过查询我们 Http 请求的结果来完成的。
如果有效,我们期望的 StatusText
是“OK”。
TCHAR StatusText[BUFFER_LENGTH] = { 0 };
DWORD StatusTextLen = BUFFER_LENGTH;
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_TEXT, &StatusText, &StatusTextLen, NULL);
bResults = (StatusTextLen && wcscmp(StatusText, L"OK")==FALSE);
SendGroupImage() 函数
接下来,我们有 SendGroupImage()
函数,该函数用于将图像发送到 WhatsApp 群组。图像可以是* .png* 或* .jpg*,它将作为新消息内联显示。
bool SGWhatsApp::SendGroupDocument(LPCTSTR ClientID, LPCTSTR ClientSecret,
LPCTSTR groupAdmin, LPCTSTR groupName, /*LPCTSTR message,*/ LPCTSTR filename,
LPBYTE attachment, int length)
{
BOOL bResults = FALSE;
HINTERNET hOpenHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");
hOpenHandle = InternetOpen(_T("HTTP"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
return bResults;
}
hConnectHandle = InternetConnect(hOpenHandle,
DOCUMENT_API_SERVER,
INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP,
0, 1);
if (hConnectHandle == NULL)
{
InternetCloseHandle(hOpenHandle);
return bResults;
}
const wchar_t *AcceptTypes[] = { _T("application/json"),NULL };
HINTERNET hRequest = HttpOpenRequest(hConnectHandle, _T("POST"),
DOCUMENT_API_PATH, NULL, NULL, AcceptTypes, 0, 0);
if (hRequest == NULL)
{
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return bResults;
}
std::wstring HeaderData;
HeaderData += _T("X-WM-CLIENT-ID: ");
HeaderData += ClientID;
HeaderData += _T("\r\nX-WM-CLIENT-SECRET: ");
HeaderData += ClientSecret;
HeaderData += _T("\r\n");
HttpAddRequestHeaders(hRequest, HeaderData.c_str(), HeaderData.size(), NULL);
std::wstring document = _T("");
DWORD desiredLength = 0;
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, NULL, &desiredLength);
desiredLength++;
TCHAR* base64content = (TCHAR*)malloc(desiredLength * sizeof(TCHAR));
CryptBinaryToString(attachment, length, CRYPT_STRING_BASE64, base64content, &desiredLength);
std::wstring WJsonData;
WJsonData += _T("{");
WJsonData += _T("\"group_admin\":\"");
WJsonData += groupAdmin;
WJsonData += _T("\",");
WJsonData += _T("\"group_name\":\"");
WJsonData += groupName;
WJsonData += _T("\",");
//WJsonData += _T("\"message\":\"");
//WJsonData += message;
//WJsonData += _T("\"");
WJsonData += _T("\"filename\":\"");
WJsonData += filename;
WJsonData += _T("\",");
WJsonData += _T("\"document\":\"");
// Needed to remove CRLF and all spaces symbols
for(size_t i=0; i<lstrlen(base64content); i++)
if (!isspace(base64content[i]))
WJsonData += base64content[i];
WJsonData += _T("\"");
WJsonData += _T("}");
free(base64content);
const std::string JsonData(WJsonData.begin(), WJsonData.end());
bResults = HttpSendRequest(hRequest, NULL, 0, (LPVOID)(JsonData.c_str()), JsonData.size());
TCHAR StatusText[BUFFER_LENGTH] = { 0 };
DWORD StatusTextLen = BUFFER_LENGTH;
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_TEXT, &StatusText, &StatusTextLen, NULL);
bResults = (StatusTextLen && wcscmp(StatusText, L"OK") == FALSE);
DWORD availableBytes = 0;
DWORD downloadedBytes = 0;
LPBYTE nextBytes = (LPBYTE)malloc((availableBytes +1) * sizeof(TCHAR));
memset(nextBytes, 0, (availableBytes + 1) * sizeof(TCHAR));
InternetQueryDataAvailable(hRequest, &availableBytes, 0, 0);
InternetReadFile(hRequest, nextBytes, availableBytes, &downloadedBytes);
free(nextBytes);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
return bResults;
}
谢谢
感谢Ivan Voloschuk的帮助。
历史
- 2018 年 11 月 6 日:初始版本