<ClCompile Include="..\..\src\net\curl-easy-session.cpp" />\r
<ClCompile Include="..\..\src\net\curl-slist.cpp" />\r
<ClCompile Include="..\..\src\net\http-client.cpp" />\r
+ <ClCompile Include="..\..\src\net\report-error.cpp" />\r
<ClCompile Include="..\..\src\object-enchant\enchanter-factory.cpp" />\r
<ClCompile Include="..\..\src\mspell\mspell-attack\abstract-mspell.cpp" />\r
<ClCompile Include="..\..\src\mspell\mspell-data.cpp" />\r
<ClInclude Include="..\..\src\net\curl-easy-session.h" />\r
<ClInclude Include="..\..\src\net\curl-slist.h" />\r
<ClInclude Include="..\..\src\net\http-client.h" />\r
+ <ClInclude Include="..\..\src\net\report-error.h" />\r
<ClInclude Include="..\..\src\object-enchant\enchanter-factory.h" />\r
<ClInclude Include="..\..\src\object-enchant\protector\apply-magic-soft-armor.h" />\r
<ClInclude Include="..\..\src\object-enchant\others\apply-magic-lite.h" />\r
<ClCompile Include="..\..\src\util\sha256.cpp">\r
<Filter>util</Filter>\r
</ClCompile>\r
+ <ClCompile Include="..\..\src\net\report-error.cpp">\r
+ <Filter>net</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="..\..\src\combat\shoot.h">\r
<ClInclude Include="..\..\src\external-lib\include-json.h">\r
<Filter>external-lib</Filter>\r
</ClInclude>\r
+ <ClInclude Include="..\..\src\net\report-error.h">\r
+ <Filter>net</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<None Include="..\..\src\wall.bmp" />\r
net/curl-easy-session.cpp net/curl-easy-session.h \
net/curl-slist.cpp net/curl-slist.h \
net/http-client.cpp net/http-client.h \
+ net/report-error.cpp net/report-error.h \
\
object/item-tester-hooker.cpp object/item-tester-hooker.h \
object/object-broken.cpp object/object-broken.h \
--- /dev/null
+#include "external-lib/include-json.h"
+#include "locale/japanese.h"
+#include "net/http-client.h"
+#include "system/angband-version.h"
+#include <sstream>
+#include <string_view>
+
+#if !defined(DISABLE_NET)
+
+namespace {
+
+/*!
+ * @brief エラーレポート送信用のWebhook URLを取得する
+ * @return 取得に成功した場合はWebhook URL、失敗した場合は空文字列
+ */
+std::string fetch_webhook_url_for_sending_error_report()
+{
+ http::Client client;
+ constexpr auto url = "https://hengband.github.io/api/report-error";
+ const auto response = client.get(url);
+ if (!response || (response->status != 200)) {
+ return "";
+ }
+
+ const auto json = nlohmann::json::parse(response->body);
+ return json["webhook_url"];
+}
+
+/*!
+ * @brief 送信するエラーレポートのJSONオブジェクトを作成する
+ *
+ * @param message エラーメッセージ
+ * @return 作成したJSONオブジェクト
+ */
+nlohmann::json create_report_json(std::string_view message)
+{
+ nlohmann::json webhook;
+ webhook["username"] = "Hengband Error Report";
+ constexpr auto conv_error_msg = "Cannot convert to UTF-8";
+ nlohmann::json embed;
+ embed["title"] = sys_to_utf8(get_version()).value_or(conv_error_msg);
+ embed["description"] = sys_to_utf8(message).value_or(conv_error_msg);
+ embed["color"] = 0xff0000; // red
+ webhook["embeds"].push_back(std::move(embed));
+
+ return webhook;
+}
+
+}
+
+/*!
+ * @brief エラーレポートを送信する
+ *
+ * @param description 送信するエラーの説明
+ * @return 送信に成功した場合はtrue、失敗した場合はfalse
+ */
+bool report_error(std::string_view description)
+{
+ const auto webhook_url = fetch_webhook_url_for_sending_error_report();
+ if (webhook_url.empty()) {
+ return false;
+ }
+
+ const auto report_json = create_report_json(description);
+
+ http::Client client;
+ const auto response = client.post(webhook_url, report_json.dump(), "application/json");
+
+ return (response && (response->status == 200));
+}
+
+#endif // !defined(DISABLE_NET)