// Author: tastytea // CC-0 / Public Domain #include "xdgjson/src/xdgjson.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace curlopts = curlpp::options; using std::string; using std::cout; using std::cerr; using std::endl; using std::vector; using std::chrono::system_clock; using std::chrono::hours; using std::string_view; constexpr string_view version = "2020-05-16_1"; struct holiday { string date; string description; vector regions; }; string decode_state(const string &abbr) { const std::map states = { { "BW", "Baden-Württemberg" }, { "BY", "Bayern" }, { "BE", "Berlin" }, { "BB", "Brandenburg" }, { "HB", "Bremen" }, { "HH", "Hamburg" }, { "HE", "Hessen" }, { "MV", "Mecklenburg-Vorpommern" }, { "NI", "Niedersachsen" }, { "NW", "Nordrhein-Westfalen" }, { "RP", "Rheinland-Pfalz" }, { "SL", "Saarland" }, { "SN", "Sachsen" }, { "ST", "Sachsen-Anhalt" }, { "SH", "Schleswig-Holstein" }, { "TH", "Thüringen" } }; if (states.find(abbr) != states.end()) { return states.find(abbr)->second; } return abbr; } string get_date(const system_clock::time_point &timepoint, const string &format) { std::time_t time = system_clock::to_time_t(timepoint); std::tm *timeinfo = std::localtime(&time); char buffer[1024]; std::strftime(buffer, 1024, format.c_str(), timeinfo); return string(buffer); } string get_token(const string &instance) { mastodonpp::Instance server{instance, ""}; mastodonpp::Instance::ObtainToken token{server}; auto answer{token.step_1("feiertagebot", "write:statuses", "https://schlomp.space/tastytea/feiertagebot")}; if (!answer) { cerr << "Error " << answer.error_message << endl; return ""; } string code; cout << "Visit " << answer << " to authorize this application.\n"; cout << "Insert code: "; std::getline(std::cin, code); answer = token.step_2(code); if (!answer) { cerr << "Error " << answer.error_message << endl; return ""; } return answer.body; } int main() { string instance; string access_token; xdgjson config("feiertagebot.json"); config.read(); Json::Value &json = config.get_json(); if (json["instance"].isString()) { instance = json["instance"].asString(); } else { cout << "Instance domain: "; std::getline(std::cin, instance); json["instance"] = instance; } if (json["access_token"].isString()) { access_token = json["access_token"].asString(); } else { access_token = get_token(instance); json["access_token"] = access_token; config.write(); } try { string year = get_date(system_clock::now(), "%Y"); string tomorrow = get_date(system_clock::now() + hours(24), "%Y-%m-%d"); string overmorrow = get_date(system_clock::now() + hours(48), "%Y-%m-%d"); curlpp::Easy request; std::stringstream ss; request.setOpt("https://feiertage-api.de/api/?jahr=" + year); request.setOpt(string("feiertagebot/") += version); request.setOpt(true); ss << request; Json::Value root; ss >> root; string output; for (const string &date : { tomorrow, overmorrow }) { holiday current_day; for (Json::Value::const_iterator it_root = root.begin(); it_root != root.end(); ++it_root) { for (Json::Value::const_iterator it_region = it_root->begin(); it_region != it_root->end(); ++it_region) { if ((*it_region)["datum"] == date) { current_day.date = date; current_day.description = it_region.key().asString(); current_day.regions.push_back(it_root.key().asString()); } } } if (current_day.date.empty()) { continue; } output += "Am " + current_day.date + " ist " + current_day.description + ", ein gesetzlicher Feiertag in "; if (std::find(current_day.regions.begin(), current_day.regions.end(), "NATIONAL") != current_day.regions.end()) { output += "Deutschland.\n\n"; } else { bool firstrun = true; for (const string ®ion : current_day.regions) { if (!firstrun) { output += ", "; } else { firstrun = false; } output += decode_state(region); } output += ".\n\n"; } } if (!output.empty()) { mastodonpp::Instance server{instance, access_token}; mastodonpp::Connection connection{server}; string status{output + "#bot"}; const auto answer{connection.post(mastodonpp::API::v1::statuses, {{"status", output}})}; if (!answer) { cerr << "Error " << answer.error_message << endl; return answer.curl_error_code; } } } catch (const std::exception &e) { cerr << e.what() << endl; return 1; } return 0; }