I am writing a very simple Telegram bot for personal use that will allow me to monitor my program while I am AFK. I'm using WinInet and would like to avoid using someone else's bot or outside libraries such as curl.
All the telegram API says about this is:
Post the file using multipart/form-data in the usual way that files are uploaded via the browser
However, information about this online is hard to find, as most people are using libraries, and I want to learn how it actually works.
I was able to send text messages easily using the query string, but not photos.
In my desperation, I turned to ChatGPT and the following code is what I have right now:
const std::string TELEGRAM_API_URL = "https://api.telegram.org/bot<TOKEN>";
constexpr char ChatGroupID[] = "<GROUP_ID>";
HINTERNET hInternet, hSession, hConnect, hRequest;
// Initialize WinINet
hInternet = InternetOpenA("TelegramBot", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hInternet == NULL)
{
std::cerr << "Failed to initialize WinINet" << std::endl;
return 1;
}
..........
// The following code executes after receiving a specific command from the user on the group chat,
// where the bot is an admin. It's inside a loop which is ommited.
// Read the photo file
std::ifstream pictureFile("C:/Screenshots/screenshot.png", std::ios::binary);
if (!pictureFile.is_open())
{
std::cerr << "Failed to open picture file" << std::endl;
continue;
}
std::vector<char> pictureData((std::istreambuf_iterator<char>(pictureFile)), std::istreambuf_iterator<char>());
pictureFile.close();
// Construct the multipart/form-data payload
std::string boundary = "----Boundary";
std::string contentType = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n";
std::stringstream body;
body << "--" + boundary + "\r\n"
<< "Content-Disposition: form-data; name=\"chat_id\"\r\n\r\n"
<< std::string(ChatGroupID) + "\r\n"
<< "--" + boundary + "\r\n"
<< "Content-Disposition: form-data; name=\"photo\"; filename=\"screenshot.png\"\r\n"
<< "Content-Type: image/png\r\n\r\n";
body.write(pictureData.data(), pictureData.size());
body << "\r\n--" + boundary + "--\r\n";
hSession = InternetConnectA(hInternet, TELEGRAM_API_URL.c_str(), INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (hSession == NULL)
{
std::cerr << "Failed to connect session " << GetLastError() << std::endl;
continue;
}
// Create an HTTP POST request using the connection handle
hRequest = HttpOpenRequestA(hSession, "POST", "/sendphoto", NULL, NULL, NULL, INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION, 0);
if (hRequest == NULL)
{
std::cerr << "Failed to create HTTP request " << GetLastError() << std::endl;
InternetCloseHandle(hSession);
continue;
}
// Set headers for the request
std::string headers = "Content-Length: " + std::to_string(body.str().size()) + "\r\n";
headers += contentType;
headers += "Connection: Keep-Alive\r\n";
// Send the headers and body
if (!HttpSendRequestA(hRequest, headers.c_str(), headers.length(), (LPVOID)body.str().c_str(), body.str().size()))
{
std::cerr << "Failed to send request " << GetLastError() << std::endl;
InternetCloseHandle(hRequest);
InternetCloseHandle(hSession);
continue;
}
std::string response;
while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0)
{
response.append(buffer, bytesRead);
}
// Process the response (parse JSON, handle updates)
std::cout << "Received updates: " << response << std::endl << std::endl;
InternetCloseHandle(hConnect);
HttpSendRequestA is failing with code 12007. What am I doing wrong?