Read and parse config.
This commit is contained in:
parent
7bba87575f
commit
bf29df39e9
|
@ -66,6 +66,9 @@ Example: `http_proxy="http://localhost:3128/" mastorss`
|
||||||
|
|
||||||
| 1 | No profile specified.
|
| 1 | No profile specified.
|
||||||
| 2 | Network error.
|
| 2 | Network error.
|
||||||
|
| 3 | File error.
|
||||||
|
| 4 | Mastodon error.
|
||||||
|
| 9 | Unknown error.
|
||||||
|===========================================================
|
|===========================================================
|
||||||
|
|
||||||
== SEE ALSO
|
== SEE ALSO
|
||||||
|
|
243
src/config.cpp
Normal file
243
src/config.cpp
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
/* This file is part of mastorss.
|
||||||
|
* Copyright © 2019 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 "config.hpp"
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <mastodon-cpp/mastodon-cpp.hpp>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace mastorss
|
||||||
|
{
|
||||||
|
using std::getenv;
|
||||||
|
using std::ifstream;
|
||||||
|
using std::ofstream;
|
||||||
|
using std::cin;
|
||||||
|
using std::cout;
|
||||||
|
using std::stringstream;
|
||||||
|
using std::runtime_error;
|
||||||
|
using std::getline;
|
||||||
|
using std::move;
|
||||||
|
|
||||||
|
std::ostream &operator <<(std::ostream &out, const ProfileData &data)
|
||||||
|
{
|
||||||
|
out << "access_token: \"" << data.access_token << "\", "
|
||||||
|
<< "append: \"" << data.append << "\", "
|
||||||
|
<< "feedurl: \"" << data.feedurl << "\", "
|
||||||
|
<< "fixes: [";
|
||||||
|
for (const auto &fix : data.fixes)
|
||||||
|
{
|
||||||
|
out << '"' << fix << '"';
|
||||||
|
if (fix != *data.fixes.rbegin())
|
||||||
|
{
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << "], "
|
||||||
|
<< "instance: \"" << data.instance << "\", "
|
||||||
|
<< "interval: " << data.interval << ", "
|
||||||
|
<< "max_size: " << data.max_size << ", "
|
||||||
|
<< "skip: [";
|
||||||
|
for (const auto &skip : data.skip)
|
||||||
|
{
|
||||||
|
out << '"' << skip << '"';
|
||||||
|
if (skip != *data.skip.rbegin())
|
||||||
|
{
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << "], "
|
||||||
|
<< "titles_as_cw: " << data.titles_as_cw << ", "
|
||||||
|
<< "titles_only: " << data.titles_only;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Config(string profile)
|
||||||
|
:_profile{move(profile)}
|
||||||
|
{
|
||||||
|
const fs::path filename = get_filename();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Config filename is: " << filename;
|
||||||
|
|
||||||
|
ifstream file{filename};
|
||||||
|
if (file.good())
|
||||||
|
{
|
||||||
|
stringstream rawjson;
|
||||||
|
rawjson << file.rdbuf();
|
||||||
|
rawjson >> _json;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path Config::get_filename() const
|
||||||
|
{
|
||||||
|
char *envdir = getenv("XDG_CONFIG_HOME");
|
||||||
|
fs::path dir;
|
||||||
|
|
||||||
|
if (envdir != nullptr)
|
||||||
|
{
|
||||||
|
dir = envdir;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
envdir = getenv("HOME");
|
||||||
|
if (envdir != nullptr)
|
||||||
|
{
|
||||||
|
dir = fs::path{envdir} /= ".config";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw FileException{"Couldn't find configuration directory."};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (dir /= "mastorss") /= "config-" + _profile + ".json";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::generate()
|
||||||
|
{
|
||||||
|
Json::Value newjson;
|
||||||
|
string line;
|
||||||
|
|
||||||
|
cout << "Instance (domain): ";
|
||||||
|
getline(cin, line);
|
||||||
|
newjson[_profile]["instance"] = line;
|
||||||
|
|
||||||
|
newjson[_profile]["access_token"] = get_access_token(line);
|
||||||
|
|
||||||
|
cout << "URL of the feed: ";
|
||||||
|
std::getline(cin, line);
|
||||||
|
newjson[_profile]["feedurl"] = line;
|
||||||
|
|
||||||
|
cout << "Post only titles? [y/n]: ";
|
||||||
|
std::getline(cin, line);
|
||||||
|
if (line[0] == 'y')
|
||||||
|
{
|
||||||
|
newjson[_profile]["titles_as_cw"] = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newjson[_profile]["titles_as_cw"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "Append this string to each post: ";
|
||||||
|
std::getline(cin, line);
|
||||||
|
newjson[_profile]["append"] = line;
|
||||||
|
|
||||||
|
cout << "Interval between posts in seconds [30]: ";
|
||||||
|
std::getline(cin, line);
|
||||||
|
if (line.empty())
|
||||||
|
{
|
||||||
|
line = "30";
|
||||||
|
}
|
||||||
|
newjson[_profile]["interval"] = std::stoul(line);
|
||||||
|
|
||||||
|
cout << "Maximum size of posts [500]: ";
|
||||||
|
std::getline(cin, line);
|
||||||
|
if (line.empty())
|
||||||
|
{
|
||||||
|
line = "500";
|
||||||
|
}
|
||||||
|
newjson[_profile]["max_size"] = std::stoul(line);
|
||||||
|
|
||||||
|
_json = newjson;
|
||||||
|
ofstream file{get_filename()};
|
||||||
|
if (file.good())
|
||||||
|
{
|
||||||
|
file << newjson.toStyledString();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Wrote config file.";
|
||||||
|
}
|
||||||
|
|
||||||
|
string Config::get_access_token(const string &instance) const
|
||||||
|
{
|
||||||
|
string client_id;
|
||||||
|
string client_secret;
|
||||||
|
string url;
|
||||||
|
Mastodon::API masto(instance, "");
|
||||||
|
|
||||||
|
Mastodon::return_call ret
|
||||||
|
{
|
||||||
|
masto.register_app1("mastorss", "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"write",
|
||||||
|
"https://schlomp.space/tastytea/mastorss",
|
||||||
|
client_id, client_secret, url)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
string code;
|
||||||
|
string access_token;
|
||||||
|
|
||||||
|
cout << "Visit " << url << " to authorize this application.\n";
|
||||||
|
cout << "Insert code: ";
|
||||||
|
std::getline(cin, code);
|
||||||
|
ret = masto.register_app2(client_id, client_secret,
|
||||||
|
"urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
code, access_token);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Got access token: " << access_token;
|
||||||
|
return access_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw MastodonException{ret.error_code};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::parse()
|
||||||
|
{
|
||||||
|
data.access_token = _json[_profile]["access_token"].asString();
|
||||||
|
data.append = _json[_profile]["append"].asString();
|
||||||
|
data.feedurl = _json[_profile]["feedurl"].asString();
|
||||||
|
for (const auto &fix : _json[_profile]["fixes"])
|
||||||
|
{
|
||||||
|
data.fixes.push_back(fix.asString());
|
||||||
|
}
|
||||||
|
data.instance = _json[_profile]["instance"].asString();
|
||||||
|
if (!_json[_profile]["interval"].isNull())
|
||||||
|
{
|
||||||
|
data.interval =
|
||||||
|
static_cast<uint32_t>(_json[_profile]["interval"].asUInt64());
|
||||||
|
}
|
||||||
|
if (!_json[_profile]["max_size"].isNull())
|
||||||
|
{
|
||||||
|
data.max_size =
|
||||||
|
static_cast<uint32_t>(_json[_profile]["max_size"].asUInt64());
|
||||||
|
}
|
||||||
|
for (const auto &skip : _json[_profile]["skip"])
|
||||||
|
{
|
||||||
|
data.skip.push_back(skip.asString());
|
||||||
|
}
|
||||||
|
data.titles_as_cw = _json[_profile]["titles_as_cw"].asBool();
|
||||||
|
data.titles_only = _json[_profile]["titles_only"].asBool();
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Read config: " << data;
|
||||||
|
}
|
||||||
|
} // namespace mastorss
|
83
src/config.hpp
Normal file
83
src/config.hpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/* This file is part of mastorss.
|
||||||
|
* Copyright © 2019 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MASTORSS_CONFIG_HPP
|
||||||
|
#define MASTORSS_CONFIG_HPP
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <jsoncpp/json/json.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace mastorss
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
using std::uint32_t;
|
||||||
|
using std::string;
|
||||||
|
using std::string_view;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief The configuration for a profile as data structure.
|
||||||
|
*
|
||||||
|
* @since 0.10.0
|
||||||
|
*/
|
||||||
|
struct ProfileData
|
||||||
|
{
|
||||||
|
string access_token;
|
||||||
|
string append;
|
||||||
|
string feedurl;
|
||||||
|
vector<string> fixes;
|
||||||
|
string instance;
|
||||||
|
uint32_t interval{30};
|
||||||
|
uint32_t max_size{500};
|
||||||
|
vector<string> skip;
|
||||||
|
bool titles_as_cw{false};
|
||||||
|
bool titles_only{false};
|
||||||
|
|
||||||
|
friend std::ostream &operator <<(std::ostream &out,
|
||||||
|
const ProfileData &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief A configuration file.
|
||||||
|
*
|
||||||
|
* @since 0.10.0
|
||||||
|
*/
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Config(string profile);
|
||||||
|
|
||||||
|
ProfileData data;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const string _profile;
|
||||||
|
Json::Value _json;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
fs::path get_filename() const;
|
||||||
|
void generate();
|
||||||
|
[[nodiscard]]
|
||||||
|
string get_access_token(const string &instance) const;
|
||||||
|
void parse();
|
||||||
|
};
|
||||||
|
} // namespace mastorss
|
||||||
|
|
||||||
|
#endif // MASTORSS_CONFIG_HPP
|
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
#include "exceptions.hpp"
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <utility>
|
||||||
|
|
||||||
using namespace mastorss;
|
using namespace mastorss;
|
||||||
using std::string;
|
|
||||||
using std::to_string;
|
using std::to_string;
|
||||||
|
using std::move;
|
||||||
|
|
||||||
HTTPException::HTTPException(const int error)
|
HTTPException::HTTPException(const int error)
|
||||||
: error_code{static_cast<uint16_t>(error)}
|
: error_code{static_cast<uint16_t>(error)}
|
||||||
|
@ -41,3 +41,23 @@ const char *CURLException::what() const noexcept
|
||||||
static const string error_string{"libCURL error: " + to_string(error_code)};
|
static const string error_string{"libCURL error: " + to_string(error_code)};
|
||||||
return error_string.c_str();
|
return error_string.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MastodonException::MastodonException(const int error)
|
||||||
|
: error_code{static_cast<uint16_t>(error)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
const char *MastodonException::what() const noexcept
|
||||||
|
{
|
||||||
|
static const string error_string{"Mastodon error: "
|
||||||
|
+ to_string(error_code)};
|
||||||
|
return error_string.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileException::FileException(string message)
|
||||||
|
: _message{move(message)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
const char *FileException::what() const noexcept
|
||||||
|
{
|
||||||
|
return _message.c_str();
|
||||||
|
}
|
||||||
|
|
|
@ -19,20 +19,23 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace mastorss
|
namespace mastorss
|
||||||
{
|
{
|
||||||
using std::uint16_t;
|
using std::uint16_t;
|
||||||
using std::exception;
|
using std::exception;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
class HTTPException : public exception
|
class HTTPException : public exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const uint16_t error_code;
|
const uint16_t error_code;
|
||||||
|
|
||||||
explicit HTTPException(const int error);
|
explicit HTTPException(int error);
|
||||||
|
|
||||||
virtual const char *what() const noexcept;
|
[[nodiscard]]
|
||||||
|
const char *what() const noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CURLException : public exception
|
class CURLException : public exception
|
||||||
|
@ -40,9 +43,33 @@ class CURLException : public exception
|
||||||
public:
|
public:
|
||||||
const uint16_t error_code;
|
const uint16_t error_code;
|
||||||
|
|
||||||
explicit CURLException(const int error);
|
explicit CURLException(int error);
|
||||||
|
|
||||||
virtual const char *what() const noexcept;
|
[[nodiscard]]
|
||||||
|
const char *what() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MastodonException : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const uint16_t error_code;
|
||||||
|
|
||||||
|
explicit MastodonException(int error);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const char *what() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileException : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FileException(string message);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const char *what() const noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const string _message;
|
||||||
};
|
};
|
||||||
} // namespace mastorss
|
} // namespace mastorss
|
||||||
|
|
||||||
|
|
28
src/main.cpp
28
src/main.cpp
|
@ -1,6 +1,7 @@
|
||||||
#include "version.hpp"
|
#include "config.hpp"
|
||||||
#include "document.hpp"
|
#include "document.hpp"
|
||||||
#include "exceptions.hpp"
|
#include "exceptions.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
@ -18,6 +19,9 @@ namespace error
|
||||||
{
|
{
|
||||||
constexpr int noprofile = 1;
|
constexpr int noprofile = 1;
|
||||||
constexpr int network = 2;
|
constexpr int network = 2;
|
||||||
|
constexpr int file = 3;
|
||||||
|
constexpr int mastodon = 4;
|
||||||
|
constexpr int unknown = 9;
|
||||||
} // namespace error
|
} // namespace error
|
||||||
|
|
||||||
void print_version();
|
void print_version();
|
||||||
|
@ -62,9 +66,23 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
const string_view profile = args[1];
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Using profile: " << profile;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Document doc("https://ip.tastytea.de/");
|
Config cfg(profile.data());
|
||||||
|
Document doc(cfg.data.feedurl);
|
||||||
|
}
|
||||||
|
catch (const FileException &e)
|
||||||
|
{
|
||||||
|
cerr << e.what() << '\n';
|
||||||
|
return error::file;
|
||||||
|
}
|
||||||
|
catch (const MastodonException &e)
|
||||||
|
{
|
||||||
|
cerr << e.what() << '\n';
|
||||||
|
return error::mastodon;
|
||||||
}
|
}
|
||||||
catch (const HTTPException &e)
|
catch (const HTTPException &e)
|
||||||
{
|
{
|
||||||
|
@ -76,7 +94,11 @@ int main(int argc, char *argv[])
|
||||||
cerr << e.what() << '\n';
|
cerr << e.what() << '\n';
|
||||||
return error::network;
|
return error::network;
|
||||||
}
|
}
|
||||||
|
catch (const runtime_error &e)
|
||||||
|
{
|
||||||
|
cerr << e.what() << '\n';
|
||||||
|
return error::unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user