|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/* This file is part of expandurl-mastodon.
|
|
|
|
|
* Copyright © 2018 tastytea <tastytea@tastytea.de> |
|
|
|
|
* Copyright © 2018, 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 |
|
|
|
@ -26,6 +26,7 @@
|
|
|
|
|
|
|
|
|
|
using std::cout; |
|
|
|
|
using std::string; |
|
|
|
|
using std::uint8_t; |
|
|
|
|
|
|
|
|
|
Listener::Listener() |
|
|
|
|
: _instance("") |
|
|
|
@ -59,7 +60,7 @@ Listener::Listener()
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_masto = std::make_unique<Easy>(_instance, _access_token); |
|
|
|
|
_masto = std::make_unique<Easy::API>(_instance, _access_token); |
|
|
|
|
_masto->set_useragent(static_cast<const string>("expandurl-mastodon/") + |
|
|
|
|
global::version); |
|
|
|
|
set_proxy(*_masto); |
|
|
|
@ -69,7 +70,7 @@ Listener::~Listener()
|
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const void Listener::read_config() |
|
|
|
|
void Listener::read_config() |
|
|
|
|
{ |
|
|
|
|
_instance = _config["account"].asString(); |
|
|
|
|
_instance = _instance.substr(_instance.find('@') + 1); |
|
|
|
@ -79,44 +80,19 @@ const void Listener::read_config()
|
|
|
|
|
_proxy_password = _config["proxy"]["password"].asString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const void Listener::start() |
|
|
|
|
void Listener::start() |
|
|
|
|
{ |
|
|
|
|
constexpr uint_fast8_t delay_after_error = 120; |
|
|
|
|
static std::uint_fast16_t ret; |
|
|
|
|
_thread = std::thread([=] |
|
|
|
|
{ |
|
|
|
|
ret = 0; |
|
|
|
|
_running = true; |
|
|
|
|
Easy masto(_instance, _access_token); |
|
|
|
|
masto.set_useragent(static_cast<const string>("expandurl-mastodon/") + |
|
|
|
|
global::version); |
|
|
|
|
set_proxy(masto); |
|
|
|
|
ret = masto.get_stream(Mastodon::API::v1::streaming_user, _stream, _ptr); |
|
|
|
|
syslog(LOG_DEBUG, "Connection lost."); |
|
|
|
|
if (ret != 0 && ret != 14) // 14 means canceled by user
|
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Connection terminated: Error %u", ret); |
|
|
|
|
syslog(LOG_INFO, "Waiting for %u seconds", delay_after_error); |
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); |
|
|
|
|
} |
|
|
|
|
_running = false; |
|
|
|
|
}); |
|
|
|
|
while (!_ptr) |
|
|
|
|
{ |
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
|
|
|
} |
|
|
|
|
Easy::API masto(_instance, _access_token); |
|
|
|
|
_running = true; |
|
|
|
|
|
|
|
|
|
if (ret == 0) |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_NOTICE, "Connected to %s", _instance.c_str()); |
|
|
|
|
} |
|
|
|
|
else if (ret != 14) |
|
|
|
|
{ // If the stream thread sleeps, the main thread should sleep too
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); |
|
|
|
|
} |
|
|
|
|
masto.set_useragent(string("expandurl-mastodon/") + global::version); |
|
|
|
|
set_proxy(masto); |
|
|
|
|
masto.get_stream(Mastodon::API::v1::streaming_user, _ptr, _stream); |
|
|
|
|
|
|
|
|
|
syslog(LOG_NOTICE, "Connecting to %s ...", _instance.c_str()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const void Listener::stop() |
|
|
|
|
void Listener::stop() |
|
|
|
|
{ |
|
|
|
|
if (!configfile.write()) |
|
|
|
|
{ |
|
|
|
@ -150,14 +126,25 @@ const std::vector<Easy::Notification> Listener::get_new_messages()
|
|
|
|
|
{ |
|
|
|
|
for (const Easy::stream_event &event : Easy::parse_stream(_stream)) |
|
|
|
|
{ |
|
|
|
|
if (event.first == Easy::event_type::Notification) |
|
|
|
|
if (event.type == Easy::event_type::Notification) |
|
|
|
|
{ |
|
|
|
|
Easy::Notification notif(event.second); |
|
|
|
|
Easy::Notification notif(event.data); |
|
|
|
|
if (notif.type() == Easy::notification_type::Mention) |
|
|
|
|
{ |
|
|
|
|
v.push_back(notif); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (event.type == Easy::event_type::Error) |
|
|
|
|
{ |
|
|
|
|
constexpr uint8_t delay_after_error = 120; |
|
|
|
|
syslog(LOG_DEBUG, "Connection lost."); |
|
|
|
|
const Json::Value err; |
|
|
|
|
syslog(LOG_ERR, "Connection terminated: Error %u", |
|
|
|
|
err["error_code"].asUInt()); |
|
|
|
|
syslog(LOG_INFO, "Waiting for %u seconds", delay_after_error); |
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); |
|
|
|
|
_running = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
_stream.clear(); |
|
|
|
|
lastping = system_clock::now(); |
|
|
|
@ -183,26 +170,25 @@ const std::vector<Easy::Notification> Listener::catchup()
|
|
|
|
|
if (last_id != "") |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_DEBUG, "Catching up..."); |
|
|
|
|
API::parametermap parameter = |
|
|
|
|
parameters parameter = |
|
|
|
|
{ |
|
|
|
|
{ "since_id", { last_id } }, |
|
|
|
|
{ "exclude_types", { "follow", "favourite", "reblog" } } |
|
|
|
|
}; |
|
|
|
|
string answer; |
|
|
|
|
std::uint_fast16_t ret; |
|
|
|
|
return_call ret;; |
|
|
|
|
|
|
|
|
|
ret = _masto->get(API::v1::notifications, parameter, answer); |
|
|
|
|
ret = _masto->get(API::v1::notifications, parameter); |
|
|
|
|
|
|
|
|
|
if (ret == 0) |
|
|
|
|
if (ret) |
|
|
|
|
{ |
|
|
|
|
for (const string str : Easy::json_array_to_vector(answer)) |
|
|
|
|
for (const string str : Easy::json_array_to_vector(ret.answer)) |
|
|
|
|
{ |
|
|
|
|
v.push_back(Easy::Notification(str)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Could not catch up: Error %u", ret); |
|
|
|
|
syslog(LOG_ERR, "Could not catch up: Error %u", ret.error_code); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -211,26 +197,24 @@ const std::vector<Easy::Notification> Listener::catchup()
|
|
|
|
|
|
|
|
|
|
Mastodon::Easy::Status Listener::get_status(const string &id) |
|
|
|
|
{ |
|
|
|
|
std::uint_fast16_t ret; |
|
|
|
|
string answer; |
|
|
|
|
return_call ret; |
|
|
|
|
|
|
|
|
|
ret = _masto->get(API::v1::statuses_id, {{ "id", { id }}}, |
|
|
|
|
answer); |
|
|
|
|
if (ret == 0) |
|
|
|
|
ret = _masto->get(API::v1::statuses_id, {{ "id", { id }}}); |
|
|
|
|
if (ret) |
|
|
|
|
{ |
|
|
|
|
return Easy::Status(answer); |
|
|
|
|
return Easy::Status(ret.answer); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__); |
|
|
|
|
syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); |
|
|
|
|
return Easy::Status(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const bool Listener::send_reply(const Easy::Status &to_status, |
|
|
|
|
const string &message) |
|
|
|
|
bool Listener::send_reply(const Easy::Status &to_status, |
|
|
|
|
const string &message) |
|
|
|
|
{ |
|
|
|
|
std::uint_fast16_t ret = 0; |
|
|
|
|
Easy::return_entity<Easy::Status> ret; |
|
|
|
|
|
|
|
|
|
Easy::Status new_status; |
|
|
|
|
if (to_status.visibility() == Easy::visibility_type::Public) |
|
|
|
@ -246,52 +230,49 @@ const bool Listener::send_reply(const Easy::Status &to_status,
|
|
|
|
|
new_status.sensitive(to_status.sensitive()); |
|
|
|
|
new_status.spoiler_text(to_status.spoiler_text()); |
|
|
|
|
|
|
|
|
|
_masto->send_toot(new_status, ret); |
|
|
|
|
ret = _masto->send_post(new_status); |
|
|
|
|
|
|
|
|
|
if (ret == 0) |
|
|
|
|
if (ret) |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_DEBUG, "Sent reply"); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__); |
|
|
|
|
syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const string Listener::get_parent_id(const Easy::Notification ¬if) |
|
|
|
|
{ |
|
|
|
|
string answer; |
|
|
|
|
std::uint_fast16_t ret; |
|
|
|
|
return_call ret; |
|
|
|
|
|
|
|
|
|
// Retry up to 2 times
|
|
|
|
|
for (std::uint_fast8_t retries = 1; retries <= 2; ++retries) |
|
|
|
|
{ |
|
|
|
|
// Fetch full status
|
|
|
|
|
ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}}, |
|
|
|
|
answer); |
|
|
|
|
if (ret > 0) |
|
|
|
|
ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}}); |
|
|
|
|
if (!ret) |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Error %u: Could not fetch status (in %s).", |
|
|
|
|
ret, __FUNCTION__); |
|
|
|
|
ret.error_code, __FUNCTION__); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ret = _masto->get(API::v1::statuses_id, |
|
|
|
|
{{ "id", { notif.status().id() }}}, |
|
|
|
|
answer); |
|
|
|
|
{{ "id", { notif.status().id() }}}); |
|
|
|
|
|
|
|
|
|
if (ret > 0) |
|
|
|
|
if (!ret) |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "Error %u: Could not get status (in %s).", |
|
|
|
|
ret, __FUNCTION__); |
|
|
|
|
ret.error_code, __FUNCTION__); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
_config["last_id"] = notif.id(); |
|
|
|
|
const Easy::Status s(answer); |
|
|
|
|
const Easy::Status s(ret.answer); |
|
|
|
|
|
|
|
|
|
// If parent is found, return ID; else retry
|
|
|
|
|
if (!s.in_reply_to_id().empty()) |
|
|
|
@ -309,23 +290,23 @@ const string Listener::get_parent_id(const Easy::Notification ¬if)
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const bool Listener::stillrunning() const |
|
|
|
|
bool Listener::stillrunning() const |
|
|
|
|
{ |
|
|
|
|
return _running; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const bool Listener::register_app() |
|
|
|
|
bool Listener::register_app() |
|
|
|
|
{ |
|
|
|
|
cout << "Account (username@instance): "; |
|
|
|
|
std::cin >> _instance; |
|
|
|
|
_config["account"] = _instance; |
|
|
|
|
_instance = _instance.substr(_instance.find('@') + 1); |
|
|
|
|
|
|
|
|
|
_masto = std::make_unique<Easy>(_instance, ""); |
|
|
|
|
_masto = std::make_unique<Easy::API>(_instance, ""); |
|
|
|
|
_masto->set_useragent(static_cast<const string>("expandurl-mastodon/") + |
|
|
|
|
global::version); |
|
|
|
|
|
|
|
|
|
std::uint_fast16_t ret; |
|
|
|
|
return_call ret; |
|
|
|
|
string client_id, client_secret, url; |
|
|
|
|
ret = _masto->register_app1("expandurl-mastodon", |
|
|
|
|
"urn:ietf:wg:oauth:2.0:oob", |
|
|
|
@ -334,7 +315,7 @@ const bool Listener::register_app()
|
|
|
|
|
client_id, |
|
|
|
|
client_secret, |
|
|
|
|
url); |
|
|
|
|
if (ret == 0) |
|
|
|
|
if (ret) |
|
|
|
|
{ |
|
|
|
|
string code; |
|
|
|
|
cout << "Visit " << url << " to authorize this application.\n"; |
|
|
|
@ -345,25 +326,25 @@ const bool Listener::register_app()
|
|
|
|
|
"urn:ietf:wg:oauth:2.0:oob", |
|
|
|
|
code, |
|
|
|
|
_access_token); |
|
|
|
|
if (ret == 0) |
|
|
|
|
if (ret) |
|
|
|
|
{ |
|
|
|
|
_config["access_token"] = _access_token; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "register_app2(): %u", ret); |
|
|
|
|
syslog(LOG_ERR, "register_app2(): %u", ret.error_code); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
syslog(LOG_ERR, "register_app1(): %u", ret); |
|
|
|
|
syslog(LOG_ERR, "register_app1(): %u", ret.error_code); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const void Listener::set_proxy(Mastodon::Easy &masto) |
|
|
|
|
void Listener::set_proxy(Easy::API &masto) |
|
|
|
|
{ |
|
|
|
|
if (!_proxy.empty()) |
|
|
|
|
{ |
|
|
|
|