diff --git a/resource/config.json b/resource/config.json index 32b4957a7f..19eb1d5ac1 100644 --- a/resource/config.json +++ b/resource/config.json @@ -14,7 +14,7 @@ "adbExtraSwipeDuration_Doc": "额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能", "penguinReport": { "Doc": "企鹅物流汇报: https://penguin-stats.cn/", - "cmdFormat": "curl -H \"Content-Type: application/json\" -s -S -m 5 -i -d \"[body]\" \"https://penguin-stats.cn/PenguinStats/api/v2/report\" [extra]", + "cmdFormat": "curl -H \"Content-Type: application/json\" -s -S -m 10 -i -d \"[body]\" \"https://penguin-stats.io/PenguinStats/api/v2/report\" [extra]", "cmdFormat_Doc": "命令格式,想打印详细信息可以尝试添加 -v -i" }, "yituliuReport": { diff --git a/src/MeoAssistant/AsstHttp.hpp b/src/MeoAssistant/AsstHttp.hpp new file mode 100644 index 0000000000..978506d4aa --- /dev/null +++ b/src/MeoAssistant/AsstHttp.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "AsstRanges.hpp" +#include "AsstUtils.hpp" + +#include +#include +#include +#include + +namespace asst::http +{ + class Response : public std::string + { + private: + std::string m_last_error; + unsigned m_status_code = 0; + std::string_view m_protocol_version; + std::string_view m_status_code_info; + std::string_view m_data; + std::unordered_map m_headers; + bool _analyze_status_line(std::string_view status_line) + { + size_t _word_count = 0; + for (const std::string_view& word : views::split(status_line, ' ') | views::transform([](auto str_range) { + return utils::view_cast(str_range); + })) { + ++_word_count; + if (_word_count == 1) { + static const std::unordered_set accepted_protocol_version = { "HTTP/1.1" }; + if (!accepted_protocol_version.contains(utils::view_cast(word))) { + m_last_error = "unsupport protocol version"; + return false; + } + m_protocol_version = utils::view_cast(word); + } + else if (_word_count == 2) { + if (word.length() != 3) { + m_last_error = "status code length is not 3"; + return false; + } + if (!ranges::all_of(word, [](char c) -> bool { return std::isdigit(c); })) { + return false; + } + m_status_code = std::atoi(word.data()); + } + else { + m_status_code_info = std::string_view(word.begin(), status_line.end()); + return true; + } + } + if (_word_count == 2) { + m_status_code_info = ""; + return true; + } + m_last_error = "status line too short"; + return false; + } + bool _analyze_headers_line(std::string_view status_line) + { + size_t _colon_pos = status_line.find(':'); + if (_colon_pos == status_line.npos) { + m_last_error = "connot decode header `" + std::string(status_line) + "`"; + return false; + } + m_headers[utils::view_cast(status_line.substr(0, _colon_pos) | views::transform([](char c) { + return static_cast(std::tolower(c)); + }))] = + status_line.substr(_colon_pos + 1 + status_line.substr(_colon_pos + 1).find_first_not_of(' ')); + return true; + } + + public: + template + requires std::is_constructible_v Response(Args&&... args) + : std::string(std::forward(args)...) + { + bool _is_status_line = true; + // 这里的 \r\n 处理很奇怪,因为 views::split 好像不支持 string,只能 char + for (const auto& line : views::split(*this, '\n')) { + std::string_view line_view; + if (utils::view_cast(line).ends_with('\r')) { + line_view = utils::view_cast(line | views::take(line.size() - 1)); + } + else { + line_view = utils::view_cast(line); + } + // status + if (_is_status_line) { + if (!_analyze_status_line(line_view)) { + return; + } + _is_status_line = false; + } + // headers + else if (!line_view.empty()) { + if (!_analyze_headers_line(line_view)) { + return; + } + } + // data + else { + m_data = std::string_view(line.begin(), this->end()); + return; + } + } + } + + unsigned status_code() const { return m_status_code; } + std::string_view protocol_version() const { return m_protocol_version; } + std::string_view status_code_info() const { return m_status_code_info; } + std::string_view data() const { return m_data; } + std::optional find_header(const std::string& key) const + { + if (auto iter = m_headers.find(key); iter != m_headers.cend()) { + return iter->second; + } + else { + return std::nullopt; + } + } + const auto& headers() const { return m_headers; } + const std::string& get_last_error() const noexcept { return m_last_error; } + + bool success() const { return m_status_code == 200; } + bool status_2xx() const { return m_status_code >= 200 && m_status_code < 300; } + bool status_3xx() const { return m_status_code >= 300 && m_status_code < 400; } + bool status_4xx() const { return m_status_code >= 400 && m_status_code < 500; } + bool status_5xx() const { return m_status_code >= 500 && m_status_code < 600; } + }; +} // namespace asst::http diff --git a/src/MeoAssistant/AsstUtils.hpp b/src/MeoAssistant/AsstUtils.hpp index 7dfd7aaf29..72637bea64 100644 --- a/src/MeoAssistant/AsstUtils.hpp +++ b/src/MeoAssistant/AsstUtils.hpp @@ -1,6 +1,7 @@ #pragma once #include "AsstConf.h" +#include "AsstRanges.hpp" #include #include @@ -29,6 +30,13 @@ namespace asst::utils struct integral_constant_at_template_instantiation : std::integral_constant {}; + template + requires ranges::range && ranges::range + dst_t view_cast(const src_t& src) + { + return dst_t(src.begin(), src.end()); + } + inline void _string_replace_all(std::string& str, const std::string_view& old_value, const std::string_view& new_value) { diff --git a/src/MeoAssistant/AutoRecruitTask.cpp b/src/MeoAssistant/AutoRecruitTask.cpp index 8b88a9418b..cb9af0fde9 100644 --- a/src/MeoAssistant/AutoRecruitTask.cpp +++ b/src/MeoAssistant/AutoRecruitTask.cpp @@ -7,6 +7,7 @@ #include "ProcessTask.h" #include "RecruitConfiger.h" #include "RecruitImageAnalyzer.h" +#include "ReportDataTask.h" #include "Resource.h" #include "AsstRanges.hpp" @@ -454,16 +455,24 @@ asst::AutoRecruitTask::calc_task_result_type asst::AutoRecruitTask::recruit_calc callback(AsstMsg::SubTaskExtraInfo, cb_info); } + bool to_report = false; if (!is_calc_only_task()) { // report if the slot is clean if (!m_dirty_slots.contains(index)) { - async_upload_result(info["details"]); + to_report = true; m_dirty_slots.emplace(index); // mark as dirty } else Log.info("will not report, dirty slots are", m_dirty_slots); } +#ifdef ASST_DEBUG + to_report = true; +#endif + if (to_report) { + upload_result(info["details"]); + } + if (need_exit()) return {}; // refresh @@ -664,16 +673,14 @@ bool asst::AutoRecruitTask::hire_all() return true; } -void asst::AutoRecruitTask::async_upload_result(const json::value& details) +void asst::AutoRecruitTask::upload_result(const json::value& details) { LogTraceFunction; if (m_upload_to_penguin) { - auto upload_future = std::async(std::launch::async, &AutoRecruitTask::upload_to_penguin, this, details); - m_upload_pending.emplace_back(std::move(upload_future)); + upload_to_penguin(details); } if (m_upload_to_yituliu) { - auto upload_future = std::async(std::launch::async, &AutoRecruitTask::upload_to_yituliu, this, details); - m_upload_pending.emplace_back(std::move(upload_future)); + upload_to_yituliu(details); } } @@ -681,12 +688,6 @@ void asst::AutoRecruitTask::upload_to_penguin(const json::value& details) { LogTraceFunction; - auto& opt = Resrc.cfg().get_options(); - - json::value cb_info = basic_info(); - cb_info["subtask"] = "ReportToPenguinStats"; - callback(AsstMsg::SubTaskStart, cb_info); - json::value body; body["server"] = m_server; body["stageId"] = "recruit"; @@ -701,85 +702,69 @@ void asst::AutoRecruitTask::upload_to_penguin(const json::value& details) body["source"] = "MeoAssistant"; body["version"] = Version; - std::string body_escape = utils::string_replace_all_batch(body.to_string(), { { "\"", "\\\"" } }); - -#ifdef _WIN32 - std::string body_escapes = utils::utf8_to_unicode_escape(body_escape); -#else - std::string body_escapes = body_escape; -#endif - std::string extra_param; if (!m_penguin_id.empty()) { extra_param = "-H \"authorization: PenguinID " + m_penguin_id + "\""; } - std::string cmd_line = utils::string_replace_all_batch(opt.penguin_report.cmd_format, - { { "[body]", body_escapes }, { "[extra]", extra_param } }); - Log.info("request_penguin |", cmd_line); - std::string response = utils::callcmd(cmd_line); - - Log.info("response:\n", response); - cb_info["details"]["response"] = response; - - static const std::regex http_ok_regex(R"(HTTP/.+ 200)"); - if (std::regex_search(response, http_ok_regex)) { - callback(AsstMsg::SubTaskCompleted, cb_info); - } - else { - cb_info["why"] = "上报失败"; - callback(AsstMsg::SubTaskError, cb_info); + if (!m_report_penguin_task_ptr) { + m_report_penguin_task_ptr = + std::make_shared(report_penguin_callback, static_cast(this), m_task_chain); } - static const std::regex penguinid_regex(R"(X-Penguin-Set-Penguinid: (\d+))"); - std::smatch penguinid_sm; - if (std::regex_search(response, penguinid_sm, penguinid_regex)) { - json::value id_info = basic_info_with_what("PenguinId"); - m_penguin_id = penguinid_sm[1]; - id_info["details"]["id"] = m_penguin_id; - callback(AsstMsg::SubTaskExtraInfo, id_info); - } + m_report_penguin_task_ptr->set_report_type(ReportType::PenguinStats) + .set_body(body.to_string()) + .set_extra_param(extra_param) + .set_retry_times(5) + .run(); } void asst::AutoRecruitTask::upload_to_yituliu(const json::value& details) { LogTraceFunction; - auto& opt = Resrc.cfg().get_options(); - - json::value cb_info = basic_info(); - cb_info["subtask"] = "ReportToYituliu"; - callback(AsstMsg::SubTaskStart, cb_info); - json::value body = details; body["server"] = m_server; body["source"] = "MeoAssistant"; body["version"] = Version; body["uuid"] = m_yituliu_id; - std::string body_escape = utils::string_replace_all_batch(body.to_string(), { { "\"", "\\\"" } }); - -#ifdef _WIN32 - std::string body_escapes = utils::utf8_to_unicode_escape(body_escape); -#else - std::string body_escapes = body_escape; -#endif - - std::string cmd_line = utils::string_replace_all_batch(opt.yituliu_report.cmd_format, - { { "[body]", body_escapes }, { "[extra]", "" } }); - Log.trace("request_yituliu |", cmd_line); - - std::string response = utils::callcmd(cmd_line); - Log.trace("request_yituliu | response:\n", response); - - cb_info["details"]["response"] = response; - - static const std::regex http_ok_regex(R"(HTTP/.+ 200)"); - if (std::regex_search(response, http_ok_regex)) { - callback(AsstMsg::SubTaskCompleted, cb_info); - } - else { - cb_info["why"] = "上报失败"; - callback(AsstMsg::SubTaskError, cb_info); + if (!m_report_yituliu_task_ptr) { + m_report_yituliu_task_ptr = + std::make_shared(report_yituliu_callback, static_cast(this), m_task_chain); } + + m_report_yituliu_task_ptr->set_report_type(ReportType::YituliuBigData) + .set_body(body.to_string()) + .set_retry_times(1) + .run(); +} + +void asst::AutoRecruitTask::report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg) +{ + LogTraceFunction; + + auto p_this = static_cast(custom_arg); + if (!p_this) { + return; + } + + if (msg == AsstMsg::SubTaskExtraInfo && detail.get("what", std::string()) == "PenguinId") { + std::string id = detail.get("details", "id", std::string()); + p_this->m_penguin_id = id; + } + + p_this->callback(msg, detail); +} + +void asst::AutoRecruitTask::report_yituliu_callback(AsstMsg msg, const json::value& detail, void* custom_arg) +{ + LogTraceFunction; + + auto p_this = static_cast(custom_arg); + if (!p_this) { + return; + } + + p_this->callback(msg, detail); } diff --git a/src/MeoAssistant/AutoRecruitTask.h b/src/MeoAssistant/AutoRecruitTask.h index 6ecb3d05f9..1b906eab3b 100644 --- a/src/MeoAssistant/AutoRecruitTask.h +++ b/src/MeoAssistant/AutoRecruitTask.h @@ -3,13 +3,14 @@ #include "AsstTypes.h" -#include #include #include #include namespace asst { + class ReportDataTask; + class AutoRecruitTask final : public AbstractTask { public: @@ -46,9 +47,11 @@ namespace asst bool initialize_dirty_slot_info(const cv::Mat&); static std::vector start_recruit_analyze(const cv::Mat& image); - void async_upload_result(const json::value& details); + void upload_result(const json::value& details); void upload_to_penguin(const json::value& details); void upload_to_yituliu(const json::value& details); + static void report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg); + static void report_yituliu_callback(AsstMsg msg, const json::value& detail, void* custom_arg); using slot_index = size_t; @@ -88,7 +91,8 @@ namespace asst bool m_upload_to_yituliu = false; std::string m_penguin_id; std::string m_yituliu_id; - std::vector> m_upload_pending; + std::shared_ptr m_report_penguin_task_ptr = nullptr; + std::shared_ptr m_report_yituliu_task_ptr = nullptr; static slot_index slot_index_from_rect(const Rect& r) { diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj index 509e1812bb..a1ef17228f 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj +++ b/src/MeoAssistant/MeoAssistant.vcxproj @@ -19,6 +19,7 @@ + @@ -40,6 +41,7 @@ + @@ -131,6 +133,7 @@ + diff --git a/src/MeoAssistant/MeoAssistant.vcxproj.filters b/src/MeoAssistant/MeoAssistant.vcxproj.filters index 81aef0266d..b674f502a9 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj.filters +++ b/src/MeoAssistant/MeoAssistant.vcxproj.filters @@ -384,6 +384,12 @@ 头文件\Task\Plugin + + 头文件\Task\Sub + + + 头文件\Utils + @@ -656,6 +662,9 @@ 源文件\Task\Plugin + + 源文件\Task\Sub + diff --git a/src/MeoAssistant/ReportDataTask.cpp b/src/MeoAssistant/ReportDataTask.cpp new file mode 100644 index 0000000000..200a3d7197 --- /dev/null +++ b/src/MeoAssistant/ReportDataTask.cpp @@ -0,0 +1,148 @@ +#include "ReportDataTask.h" + +#include "AsstUtils.hpp" +#include "Logger.hpp" +#include "Resource.h" + +#include + +#include +#include +#include +#include + +std::vector> asst::ReportDataTask::m_upload_pending; + +asst::ReportDataTask& asst::ReportDataTask::set_report_type(ReportType type) +{ + m_report_type = type; + return *this; +} + +asst::ReportDataTask& asst::ReportDataTask::set_body(std::string body) +{ + m_body = std::move(body); + return *this; +} + +asst::ReportDataTask& asst::ReportDataTask::set_extra_param(std::string extra_param) +{ + m_extra_param = std::move(extra_param); + return *this; +} + +bool asst::ReportDataTask::_run() +{ + LogTraceFunction; + + std::function report_func; + + switch (m_report_type) { + case ReportType::PenguinStats: + report_func = std::bind(&ReportDataTask::report_to_penguin, this); + break; + case ReportType::YituliuBigData: + report_func = std::bind(&ReportDataTask::report_to_yituliu, this); + break; + default: + return false; + } + + auto upload_future = std::async(std::launch::async, report_func); + m_upload_pending.emplace_back(std::move(upload_future)); + + return true; +} + +void asst::ReportDataTask::report_to_penguin() +{ + using namespace std::chrono; + + LogTraceFunction; + + auto epoch = time_point_cast(steady_clock::now()).time_since_epoch(); + long long tick = duration_cast(epoch).count(); + + static std::default_random_engine rand_engine(std::random_device {}()); + static std::uniform_int_distribution rand_uni(0, UINT64_MAX); + + uint64_t rand = rand_uni(rand_engine); + + std::stringstream key_ss; + key_ss << "MAA" << std::hex << tick << rand; + std::string key = key_ss.str(); + Log.info("X-Penguin-Idempotency-Key:", key); + + m_extra_param += " -H \"X-Penguin-Idempotency-Key: " + std::move(key) + "\""; + + http::Response response = report("ReportToPenguinStats", Resrc.cfg().get_options().penguin_report.cmd_format); + + if (response.success()) [[unlikely]] { + if (auto penguinid_opt = response.find_header("x-penguin-set-penguinid")) { + json::value id_info = basic_info_with_what("PenguinId"); + id_info["details"]["id"] = std::string(penguinid_opt.value()); + callback(AsstMsg::SubTaskExtraInfo, id_info); + } + } +} + +void asst::ReportDataTask::report_to_yituliu() +{ + LogTraceFunction; + + report("ReportToYituliu", Resrc.cfg().get_options().yituliu_report.cmd_format); +} + +asst::http::Response asst::ReportDataTask::report(const std::string& subtask, const std::string& format, + HttpResponsePred success_cond, HttpResponsePred retry_cond) +{ + LogTraceFunction; + + json::value cb_info = basic_info(); + cb_info["subtask"] = subtask; + callback(AsstMsg::SubTaskStart, cb_info); + + http::Response response; + for (int i = 0; i <= m_retry_times; ++i) { + response = escape_and_request(format); + if (success_cond(response)) { + callback(AsstMsg::SubTaskCompleted, cb_info); + return response; + } + else if (!retry_cond(response)) { + break; + } + } + + cb_info["why"] = "上报失败"; + cb_info["details"] = json::object { { "error", response.get_last_error() }, + { "status_code", response.status_code() }, + { "status_code_info", std::string(response.status_code_info()) }, + { "response", std::string(response.data()) } }; + + callback(AsstMsg::SubTaskError, cb_info); + + return response; +} + +asst::http::Response asst::ReportDataTask::escape_and_request(const std::string& format) +{ + LogTraceFunction; + + std::string body_escape = utils::string_replace_all_batch(m_body, { { "\"", "\\\"" } }); + +#ifdef _WIN32 + std::string body_escapes = utils::utf8_to_unicode_escape(body_escape); +#else + std::string body_escapes = body_escape; +#endif + + std::string cmd_line = + utils::string_replace_all_batch(format, { { "[body]", body_escapes }, { "[extra]", m_extra_param } }); + + Log.info("request:\n", cmd_line); + http::Response response = utils::callcmd(cmd_line); + Log.info("response:\n", response); + + return response; +} diff --git a/src/MeoAssistant/ReportDataTask.h b/src/MeoAssistant/ReportDataTask.h new file mode 100644 index 0000000000..1e4867ca76 --- /dev/null +++ b/src/MeoAssistant/ReportDataTask.h @@ -0,0 +1,49 @@ +#pragma once + +#include "AbstractTask.h" +#include "AsstHttp.hpp" +#include "AsstUtils.hpp" + +#include +#include +#include + +namespace asst +{ + enum class ReportType + { + Invaild, + PenguinStats, + YituliuBigData, + }; + + class ReportDataTask : public AbstractTask + { + public: + using AbstractTask::AbstractTask; + virtual ~ReportDataTask() noexcept override = default; + + ReportDataTask& set_report_type(ReportType type); + + ReportDataTask& set_body(std::string body); + ReportDataTask& set_extra_param(std::string extra_param); + + protected: + using HttpResponsePred = std::function; + + virtual bool _run() override; + + void report_to_penguin(); + void report_to_yituliu(); + http::Response escape_and_request(const std::string& format); + http::Response report( + const std::string& subtask, const std::string& format, + HttpResponsePred success_cond = [](const http::Response& response) -> bool { return response.success(); }, + HttpResponsePred retry_cond = [](const http::Response& response) -> bool { return response.status_5xx(); }); + + ReportType m_report_type = ReportType::Invaild; + std::string m_body; + std::string m_extra_param; + static std::vector> m_upload_pending; + }; +} // namespace asst diff --git a/src/MeoAssistant/StageDropsTaskPlugin.cpp b/src/MeoAssistant/StageDropsTaskPlugin.cpp index c38d4983cf..c41854de9c 100644 --- a/src/MeoAssistant/StageDropsTaskPlugin.cpp +++ b/src/MeoAssistant/StageDropsTaskPlugin.cpp @@ -8,6 +8,7 @@ #include "Controller.h" #include "Logger.hpp" #include "ProcessTask.h" +#include "ReportDataTask.h" #include "Resource.h" #include "RuntimeStatus.h" #include "StageDropsImageAnalyzer.h" @@ -90,8 +91,7 @@ bool asst::StageDropsTaskPlugin::_run() } if (m_enable_penguid && !m_is_annihilation) { - auto upload_future = std::async(std::launch::async, &StageDropsTaskPlugin::upload_to_penguin, this); - m_upload_pending.emplace_back(std::move(upload_future)); + upload_to_penguin(); } return true; @@ -205,11 +205,8 @@ void asst::StageDropsTaskPlugin::upload_to_penguin() { LogTraceFunction; - auto& opt = Resrc.cfg().get_options(); - json::value cb_info = basic_info(); cb_info["subtask"] = "ReportToPenguinStats"; - callback(AsstMsg::SubTaskStart, cb_info); // Doc: https://developer.penguin-stats_vec.io/public-api/api-v2-instruction/report-api std::string stage_id = m_cur_info_json.get("stage", "stageId", std::string()); @@ -251,38 +248,38 @@ void asst::StageDropsTaskPlugin::upload_to_penguin() body["source"] = "MeoAssistant"; body["version"] = Version; - std::string body_escape = utils::string_replace_all(body.to_string(), "\"", "\\\""); - std::string cmd_line = utils::string_replace_all(opt.penguin_report.cmd_format, "[body]", body_escape); - std::string extra_param; if (!m_penguin_id.empty()) { extra_param = "-H \"authorization: PenguinID " + m_penguin_id + "\""; } - cmd_line = utils::string_replace_all(cmd_line, "[extra]", extra_param); - Log.info("request_penguin |", cmd_line); - std::string response = utils::callcmd(cmd_line); - - Log.info("response:\n", response); - cb_info["details"]["response"] = response; - - static const std::regex http_ok_regex(R"(HTTP/.+ 200)"); - if (std::regex_search(response, http_ok_regex)) { - callback(AsstMsg::SubTaskCompleted, cb_info); - } - else { - cb_info["why"] = "上报失败"; - callback(AsstMsg::SubTaskError, cb_info); + if (!m_report_penguin_task_ptr) { + m_report_penguin_task_ptr = + std::make_shared(report_penguin_callback, static_cast(this), m_task_chain); } - static const std::regex penguinid_regex(R"(X-Penguin-Set-Penguinid: (\d+))"); - std::smatch penguinid_sm; - if (std::regex_search(response, penguinid_sm, penguinid_regex)) { - json::value id_info = basic_info_with_what("PenguinId"); - m_penguin_id = penguinid_sm[1]; - id_info["details"]["id"] = m_penguin_id; - callback(AsstMsg::SubTaskExtraInfo, id_info); + m_report_penguin_task_ptr->set_report_type(ReportType::PenguinStats) + .set_body(body.to_string()) + .set_extra_param(extra_param) + .set_retry_times(5) + .run(); +} + +void asst::StageDropsTaskPlugin::report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg) +{ + LogTraceFunction; + + auto p_this = static_cast(custom_arg); + if (!p_this) { + return; } + + if (msg == AsstMsg::SubTaskExtraInfo && detail.get("what", std::string()) == "PenguinId") { + std::string id = detail.get("details", "id", std::string()); + p_this->m_penguin_id = id; + } + + p_this->callback(msg, detail); } bool asst::StageDropsTaskPlugin::check_stage_valid() diff --git a/src/MeoAssistant/StageDropsTaskPlugin.h b/src/MeoAssistant/StageDropsTaskPlugin.h index d4c3c137d1..85557221fc 100644 --- a/src/MeoAssistant/StageDropsTaskPlugin.h +++ b/src/MeoAssistant/StageDropsTaskPlugin.h @@ -1,7 +1,7 @@ #pragma once #include "AbstractTaskPlugin.h" -#include +#include #include #include @@ -11,6 +11,7 @@ namespace asst { class ProcessTask; + class ReportDataTask; class StageDropsTaskPlugin final : public AbstractTaskPlugin { @@ -36,6 +37,7 @@ namespace asst bool check_specify_quantity() const; void stop_task(); void upload_to_penguin(); + static void report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg); static constexpr int64_t RecognitionTimeOffset = 20; @@ -48,8 +50,8 @@ namespace asst mutable bool m_is_annihilation = false; bool m_start_button_delay_is_set = false; - std::vector> m_upload_pending; ProcessTask* m_cast_ptr = nullptr; + std::shared_ptr m_report_penguin_task_ptr = nullptr; bool m_enable_penguid = false; std::string m_penguin_id; std::string m_server = "CN";