229 lines
8.0 KiB
C++
229 lines
8.0 KiB
C++
/* This file is part of mastobotmon.
|
|
* Copyright © 2018 tastytea <tastytea@tastytea.de>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <sstream>
|
|
#include <iomanip> // get_time
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <memory>
|
|
#include <jsoncpp/json/json.h>
|
|
#include <mastodon-cpp/easy/entities/account.hpp>
|
|
#include <mastodon-cpp/easy/entities/notification.hpp>
|
|
#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<std::shared_ptr<Easy::Notification>> &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<Easy::Notification> &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<std::chrono::system_clock> 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<std::shared_ptr<Account>> 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<Account> acc(std::make_shared<Account>(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<Account> &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<std::chrono::minutes>(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<std::shared_ptr<Easy::Notification>> notifications;
|
|
for (const string &str : Easy::json_array_to_vector(answer))
|
|
{
|
|
notifications.push_back(std::make_shared<Easy::Notification>(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;
|
|
}
|