|
|
注:我不是原作者,原作者不记得是谁了,最开始是易语言代码,转成C++了的
- // qq.hpp - 获取本机已登录 QQ 账号(WinHTTP 版)
- // 功能:通过本地 QQ 的 ptlogin 接口(localhost:4301)获取当前已登录的 QQ 账号信息。
- // 依赖:需要 nlohmann/json 库(json.hpp)
- // 编译:链接 winhttp.lib
- #pragma once
- #include <windows.h>
- #include <winhttp.h>
- #include <string>
- #include <vector>
- #include <random>
- #include <stdexcept>
- #pragma comment(lib, "winhttp.lib")
- namespace qq_local {
- /**
- * @brief QQ 账号信息结构体
- */
- struct QQInfo {
- std::string uin; ///< QQ 号(字符串形式)
- std::string nickname; ///< 昵称(可能为空)
- };
- /**
- * @brief 忽略 SSL 证书错误(因为本地 4301 端口使用的是自签名证书)
- * @param hRequest WinHTTP 请求句柄
- */
- inline void IgnoreSslErrors(HINTERNET hRequest) {
- DWORD flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
- SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
- SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
- WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS,
- &flags, sizeof(flags));
- }
- /**
- * @brief 获取本机当前已登录的所有 QQ 账号
- *
- * 该函数向本地 QQ 服务(localhost.ptlogin2.qq.com:4301)发送请求,
- * 获取已登录账号的 JSON 列表并解析。
- *
- * @return std::vector<QQInfo> 包含所有已登录账号的 vector,若无登录账号则为空。
- *
- * @throws std::runtime_error 当网络请求失败、响应解析失败或接口不可用时抛出异常。
- *
- * @note 使用前必须确保 QQ 客户端正在运行且已登录至少一个账号。
- * @note 该函数会忽略本地 SSL 证书错误,仅用于本地调试。
- * @note 需要 nlohmann/json 库支持。
- */
- inline std::vector<QQInfo> GetLocalQQ() {
- // 1. 生成随机数作为 pt_local_tk(防缓存)
- std::random_device rd;
- std::mt19937 gen(rd());
- std::uniform_int_distribution<long long> dist(1000000000LL, 2000000000LL);
- long long randNum = dist(gen);
- std::string randStr = std::to_string(randNum);
- std::wstring randWstr(randStr.begin(), randStr.end());
- std::wstring path = L"/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=-" + randWstr;
- // 2. 初始化 WinHTTP 会话
- HINTERNET hSession = WinHttpOpen(L"Mozilla/5.0",
- WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
- WINHTTP_NO_PROXY_NAME,
- WINHTTP_NO_PROXY_BYPASS, 0);
- if (!hSession) {
- throw std::runtime_error("WinHttpOpen 失败");
- }
- // 3. 连接到本地 QQ 服务(端口 4301)
- HINTERNET hConnect = WinHttpConnect(hSession, L"localhost.ptlogin2.qq.com", 4301, 0);
- if (!hConnect) {
- WinHttpCloseHandle(hSession);
- throw std::runtime_error("WinHttpConnect 失败,请确认 QQ 已启动");
- }
- // 4. 创建请求(HTTPS)
- HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", path.c_str(),
- nullptr, WINHTTP_NO_REFERER,
- WINHTTP_DEFAULT_ACCEPT_TYPES,
- WINHTTP_FLAG_SECURE);
- if (!hRequest) {
- WinHttpCloseHandle(hConnect);
- WinHttpCloseHandle(hSession);
- throw std::runtime_error("WinHttpOpenRequest 失败");
- }
- IgnoreSslErrors(hRequest);
- // 5. 添加请求头(Referer 和 Cookie)
- std::wstring headers = L"Referer: https://ui.ptlogin2.qq.com/\r\n";
- headers += L"Cookie: pt_local_token=-" + randWstr;
- if (!WinHttpAddRequestHeaders(hRequest, headers.c_str(), (DWORD)-1,
- WINHTTP_ADDREQ_FLAG_ADD)) {
- WinHttpCloseHandle(hRequest);
- WinHttpCloseHandle(hConnect);
- WinHttpCloseHandle(hSession);
- throw std::runtime_error("WinHttpAddRequestHeaders 失败");
- }
- // 6. 发送请求
- if (!WinHttpSendRequest(hRequest, nullptr, 0, nullptr, 0, 0, 0)) {
- WinHttpCloseHandle(hRequest);
- WinHttpCloseHandle(hConnect);
- WinHttpCloseHandle(hSession);
- throw std::runtime_error("请求发送失败,请确认 QQ 已登录且本地服务正常");
- }
- // 7. 接收响应
- if (!WinHttpReceiveResponse(hRequest, nullptr)) {
- WinHttpCloseHandle(hRequest);
- WinHttpCloseHandle(hConnect);
- WinHttpCloseHandle(hSession);
- throw std::runtime_error("接收响应失败");
- }
- // 8. 读取响应数据
- std::string response;
- DWORD bytesRead = 0;
- char buffer[4096];
- while (WinHttpReadData(hRequest, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
- response.append(buffer, bytesRead);
- }
- WinHttpCloseHandle(hRequest);
- WinHttpCloseHandle(hConnect);
- WinHttpCloseHandle(hSession);
- // 9. 从响应中提取 JSON 字符串(包裹在 JavaScript 回调中)
- std::string marker = "var var_sso_uin_list=";
- size_t start = response.find(marker);
- if (start == std::string::npos) {
- marker = "var var_sso_uin_list =";
- start = response.find(marker);
- if (start == std::string::npos) {
- throw std::runtime_error("响应中未找到 var_sso_uin_list,QQ 可能未登录或接口已变更");
- }
- }
- start += marker.length();
- size_t end = response.find(";", start);
- if (end == std::string::npos) {
- throw std::runtime_error("JSON 结束符 ';' 未找到");
- }
- std::string jsonStr = response.substr(start, end - start);
- // 10. 解析 JSON(需要 nlohmann/json.hpp)
- nlohmann::json qqList;
- try {
- qqList = nlohmann::json::parse(jsonStr);
- }
- catch (const nlohmann::json::parse_error& e) {
- throw std::runtime_error("JSON 解析失败: " + std::string(e.what()));
- }
- if (!qqList.is_array()) {
- return {};
- }
- // 11. 转换为 QQInfo 结构体
- std::vector<QQInfo> result;
- for (const auto& item : qqList) {
- QQInfo info;
- if (item.contains("uin")) {
- if (item["uin"].is_string()) {
- info.uin = item["uin"].get<std::string>();
- }
- else if (item["uin"].is_number()) {
- uint64_t uin_num = item["uin"].get<uint64_t>();
- info.uin = std::to_string(uin_num);
- }
- }
- if (item.contains("nickname") && item["nickname"].is_string()) {
- info.nickname = item["nickname"].get<std::string>();
- }
- if (!info.uin.empty()) {
- result.push_back(info);
- }
- }
- return result;
- }
- } // namespace qq_local
复制代码
|
|