65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (17投票s)

2018 年 11 月 6 日

CPOL

3分钟阅读

viewsIcon

22666

downloadIcon

1666

一种从 C++ 向个人或群组发送 WhatsApp 文档和图片的简单方法

荣获 2018 年 11 月最佳文章第三名

 

引言

本文是第二部分,紧接在第一部分之后。在本文中,我将解释如何向群组发送图片和文档正如在第 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 日:初始版本
© . All rights reserved.