feiertagebot/feiertagebot.cpp

229 lines
6.2 KiB
C++

// Author: tastytea <tastytea@tastytea.de>
// CC-0 / Public Domain
#include "xdgjson/src/xdgjson.hpp"
#include <mastodonpp/mastodonpp.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Exception.hpp>
#include <curlpp/Infos.hpp>
#include <json/json.h>
#include <string>
#include <string_view>
#include <exception>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <map>
#include <chrono>
#include <ctime>
#include <cstdint>
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<string> regions;
};
string decode_state(const string &abbr)
{
const std::map<string, string> 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<curlopts::Url>("https://feiertage-api.de/api/?jahr="
+ year);
request.setOpt<curlopts::UserAgent>(string("feiertagebot/") += version);
request.setOpt<curlopts::FollowLocation>(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 &region : 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;
}