/* This file is part of mastobotmon. * Copyright © 2018 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include // get_time #include #include #include #include #include #include #include "version.hpp" #include "mastobotmon.hpp" using std::cout; using std::cerr; using std::cin; using std::string; using std::uint16_t; using Mastodon::Easy; using std::chrono::system_clock; Json::Value config; // Declared in mastobotmon.hpp const bool write_mentions(const string &straccount, std::vector> &mentions) { const string filepath = config["data_dir"].asString() + "/mentions_" + straccount + ".csv"; const std::regex restrip("<[^>]*>"); std::ofstream outfile(filepath, std::ios::app); if (outfile.is_open()) { string output; for (std::shared_ptr &mention : mentions) { output = mention->status().account().acct() + ';'; output += Easy::strtime_utc(mention->status().created_at(), "%T") + ';'; output += mention->status().content() + ';'; output += mention->status().url() + '\n'; output = std::regex_replace(output, restrip, ""); outfile.write(output.c_str(), output.length()); } outfile.close(); cout << "New mentions in: " << filepath << '\n'; return true; } cerr << "Error writing file: " << filepath << '\n'; return false; } const bool write_statistics(const string &straccount, Easy::Account &account_entity) { const string filepath = config["data_dir"].asString() + "/statistics_" + straccount + ".csv"; std::ofstream outfile(filepath, std::ios::app); if (outfile.is_open()) { string output; std::chrono::time_point now = std::chrono::system_clock::now(); std::time_t now_t = std::chrono::system_clock::to_time_t(now); std::tm now_tm = *std::localtime(&now_t); std::stringstream ss; ss << std::put_time(&now_tm, "%Y-%m-%dT%T"); output = ss.str() + ';'; output += std::to_string(account_entity.statuses_count()) + ';'; output += std::to_string(account_entity.followers_count()) + '\n'; outfile.write(output.c_str(), output.length()); outfile.close(); return true; } cerr << "Error writing file: " << filepath << '\n'; return false; } int main(int argc, char *argv[]) { uint16_t mainret = 0; if (!read_config()) { return 1; } if (argc > 1) { if ((std::strncmp(argv[1], "add", 3)) == 0) { add_account(); } } std::vector> accounts; for (auto it = config["accounts"].begin(); it != config["accounts"].end(); ++it) { // Construct an Account object for every account and store it in a vector string instance = it.name(); instance = instance.substr(instance.find('@') + 1); std::shared_ptr acc(std::make_shared(instance, (*it)["access_token"].asString())); //Account *acc = new Account(instance, (*it)["access_token"].asString()); acc->set_useragent("mastobotmon/" + string(global::version)); acc->set_minutes((*it)["minutes"].asUInt()); if (!(*it)["last_mention"].empty()) { acc->set_last_mention_id((*it)["last_mention"].asUInt64()); } accounts.push_back(acc); } if (config["mode"] == "cron") { for (std::shared_ptr &acc : accounts) { std::string answer; uint16_t ret = acc->get(Mastodon::API::v1::accounts_verify_credentials, answer); if (ret == 0) { if (!acc->get_header("X-RateLimit-Remaining").empty() && std::stoi(acc->get_header("X-RateLimit-Remaining")) < 2) { cerr << "ERROR: Reached limit of API calls.\n"; cerr << "Counter will reset at " << acc->get_header("X-RateLimit-Reset") << '\n'; return 2; } Easy::Account account_entity(answer); const string id = std::to_string(account_entity.id()); const string straccount = account_entity.acct() + "@" + acc->get_instance(); write_statistics(straccount, account_entity); Account::parametermap parameters( { { "id", { id } }, { "limit", { "1" } } }); ret = acc->get(Mastodon::API::v1::accounts_id_statuses, parameters, answer); if (ret == 0) { const Easy::Status status(answer); const auto now = std::chrono::system_clock::now(); const auto last = status.created_at(); auto elapsed = std::chrono::duration_cast(now - last); if (elapsed.count() > acc->get_minutes()) { uint16_t minutes = elapsed.count(); std::uint8_t hours = 0; std:: uint8_t days = 0; while (minutes >= 1440) { minutes -= 1440; days += 1; } while (minutes >= 60) { minutes -= 60; hours += 1; } cout << "ALERT: " << account_entity.acct() << " is inactive since "; if (days > 0) { cout << std::to_string(days) << " days, "; } if (hours > 0) { cout << std::to_string(hours) << " hours and "; } cout << std::to_string(minutes) << " minutes.\n"; } ret = acc->get_mentions(answer); if (ret == 0) { std::vector> notifications; for (const string &str : Easy::json_array_to_vector(answer)) { notifications.push_back(std::make_shared(str)); } if (!notifications.empty()) { const std::uint64_t lastid = notifications[0]->id(); acc->set_last_mention_id(lastid); config["accounts"][straccount]["last_mention"] = lastid; write_mentions(straccount, notifications); } } } } if (ret != 0) { cerr << "Error: " << ret << '\n'; mainret = ret; } } } if (!write_config()) { cerr << "Couldn't write config file\n"; } return mainret; }