Posting works now
This commit is contained in:
parent
b5d75af9fb
commit
8f083e127d
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required (VERSION 3.7)
|
cmake_minimum_required (VERSION 3.7)
|
||||||
project (expandurl-mastodon
|
project (expandurl-mastodon
|
||||||
VERSION 0.0.2
|
VERSION 0.1.0
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,5 +25,5 @@ include_directories(${CURL_INCLUDE_DIR})
|
||||||
|
|
||||||
file(GLOB sources src/*.cpp)
|
file(GLOB sources src/*.cpp)
|
||||||
add_executable(expandurl-mastodon ${sources})
|
add_executable(expandurl-mastodon ${sources})
|
||||||
target_link_libraries(expandurl-mastodon mastodon-cpp curl curlpp)
|
target_link_libraries(expandurl-mastodon jsoncpp mastodon-cpp curl curlpp pthread)
|
||||||
install(TARGETS expandurl-mastodon DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS expandurl-mastodon DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,5 +1,7 @@
|
||||||
**expandurl-mastodon** is a Mastodon bot that expands a shortened URL.
|
**expandurl-mastodon** is a Mastodon bot that expands a shortened URL.
|
||||||
|
|
||||||
|
If you want the bot to expand an URL, reply to the post with the URL in it and
|
||||||
|
mention the bot account (@expandurl@botsin.space for example).
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
|
|
||||||
|
@ -28,9 +30,14 @@ Install with `make install`.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
## Error codes
|
You will need to generate an access token yourself at the moment. Create a
|
||||||
|
config file with your account and access token in
|
||||||
|
`${HOME}/.config/expandurl-mastodon.cfg`:
|
||||||
|
|
||||||
Same as [mastodon-cpp](https://github.com/tastytea/mastodon-cpp/blob/master/README.md#error-codes)
|
expandurl@example.social
|
||||||
|
abc123
|
||||||
|
|
||||||
|
Now start expandurl-mastodon without parameters.
|
||||||
|
|
||||||
# Copyright
|
# Copyright
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,24 @@
|
||||||
#define EXPANDURL_MASTODON_HPP
|
#define EXPANDURL_MASTODON_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mastodon-cpp/mastodon-cpp.hpp>
|
||||||
|
#include <mastodon-cpp/easy/all.hpp>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
void signal_handler(int signum);
|
||||||
|
/*!
|
||||||
|
* @brief Extract URLs from HTML
|
||||||
|
*
|
||||||
|
* @return vector of URLs
|
||||||
|
*/
|
||||||
|
const std::vector<string> get_urls(const string &html);
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Expands shortened URLs
|
* @brief Expands shortened URLs
|
||||||
*
|
*
|
||||||
|
@ -33,7 +48,7 @@ const string expand(const string &url);
|
||||||
/*!
|
/*!
|
||||||
* @brief Filters out tracking stuff
|
* @brief Filters out tracking stuff
|
||||||
*
|
*
|
||||||
* Currently remoces all arguments beginning with `utm_`
|
* Currently removes all arguments beginning with `utm_`
|
||||||
*
|
*
|
||||||
* @param url URL to filter
|
* @param url URL to filter
|
||||||
*
|
*
|
||||||
|
@ -41,4 +56,32 @@ const string expand(const string &url);
|
||||||
*/
|
*/
|
||||||
const string strip(const string &url);
|
const string strip(const string &url);
|
||||||
|
|
||||||
|
|
||||||
|
class Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Listener();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Starts listening on Mastodon
|
||||||
|
*/
|
||||||
|
const bool start();
|
||||||
|
/*!
|
||||||
|
* @brief Stops listening on Mastodon
|
||||||
|
*/
|
||||||
|
const void stop();
|
||||||
|
|
||||||
|
std::vector<Mastodon::Easy::Notification> get_new_messages();
|
||||||
|
Mastodon::Easy::Status get_status(const std::uint_fast64_t &id);
|
||||||
|
const bool send_reply(const Mastodon::Easy::Status &status,
|
||||||
|
const string &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
string _instance;
|
||||||
|
string _access_token;
|
||||||
|
string _stream;
|
||||||
|
std::unique_ptr<Mastodon::API::http> _ptr;
|
||||||
|
std::thread _thread;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // EXPANDURL_MASTODON_HPP
|
#endif // EXPANDURL_MASTODON_HPP
|
||||||
|
|
86
src/main.cpp
86
src/main.cpp
|
@ -15,19 +15,99 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <chrono>
|
||||||
|
#include <csignal>
|
||||||
|
#include <regex>
|
||||||
#include <curlpp/cURLpp.hpp>
|
#include <curlpp/cURLpp.hpp>
|
||||||
#include "expandurl-mastodon.hpp"
|
#include "expandurl-mastodon.hpp"
|
||||||
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using Mastodon::Easy;
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
void signal_handler(int signum)
|
||||||
|
{
|
||||||
|
switch (signum)
|
||||||
|
{
|
||||||
|
case SIGINT:
|
||||||
|
if (!running)
|
||||||
|
{
|
||||||
|
cout << "Forced close.\n";
|
||||||
|
exit(signum);
|
||||||
|
}
|
||||||
|
running = false;
|
||||||
|
cout << "Closing program, this could take a few seconds...\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<string> get_urls(const string &html)
|
||||||
|
{
|
||||||
|
const std::regex re_url("href=\"([^\"]+)\" rel");
|
||||||
|
std::smatch match;
|
||||||
|
string buffer = html;
|
||||||
|
std::vector<string> v;
|
||||||
|
|
||||||
|
while (std::regex_search(buffer, match, re_url))
|
||||||
|
{
|
||||||
|
string url = Easy::unescape_html(match[1].str());
|
||||||
|
v.push_back(strip(expand(url)));
|
||||||
|
buffer = match.suffix().str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
curlpp::initialize();
|
curlpp::initialize();
|
||||||
|
|
||||||
// cout << expand(argv[1]) << '\n';
|
Listener listener;
|
||||||
cout << strip(argv[1]) << '\n';
|
listener.start();
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
|
|
||||||
|
for (Easy::Notification ¬if : listener.get_new_messages())
|
||||||
|
{
|
||||||
|
const std::uint_fast64_t id = notif.status().in_reply_to_id();
|
||||||
|
Easy::Status status;
|
||||||
|
if (id > 0)
|
||||||
|
{
|
||||||
|
status = listener.get_status(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listener.send_reply(notif.status(),
|
||||||
|
"I couldn't find the message you replied to. 😞");
|
||||||
|
}
|
||||||
|
if (status.valid())
|
||||||
|
{
|
||||||
|
string message = "";
|
||||||
|
for (const string &url : get_urls(status.content()))
|
||||||
|
{
|
||||||
|
message += url + " \n";
|
||||||
|
}
|
||||||
|
if (listener.send_reply(notif.status(), message))
|
||||||
|
{
|
||||||
|
cout << "Sent reply: " << message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "ERROR: could not send reply to " <<
|
||||||
|
notif.status().id() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.stop();
|
||||||
|
|
||||||
curlpp::Cleanup();
|
curlpp::Cleanup();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/* This file is part of expandurl-mastodon.
|
||||||
|
* 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 <fstream>
|
||||||
|
#include <cstdlib> // getenv()
|
||||||
|
#include <iostream>
|
||||||
|
#include "version.hpp"
|
||||||
|
#include "expandurl-mastodon.hpp"
|
||||||
|
|
||||||
|
using std::cerr;
|
||||||
|
using std::string;
|
||||||
|
using Mastodon::Easy;
|
||||||
|
|
||||||
|
Listener::Listener()
|
||||||
|
: _instance("")
|
||||||
|
, _access_token("")
|
||||||
|
, _stream("")
|
||||||
|
, _ptr(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const bool Listener::start()
|
||||||
|
{
|
||||||
|
const string filepath = static_cast<const string>(getenv("HOME")) +
|
||||||
|
"/.config/expandurl-mastodon.cfg";
|
||||||
|
std::ifstream file(filepath);
|
||||||
|
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
std::getline(file, _instance);
|
||||||
|
_instance = _instance.substr(_instance.find('@') + 1);
|
||||||
|
std::getline(file, _access_token);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "ERROR: Could not open " << filepath << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_thread = std::thread([=]
|
||||||
|
{
|
||||||
|
Easy masto(_instance, _access_token);
|
||||||
|
masto.set_useragent(static_cast<const string>("expandurl-mastodon/") +
|
||||||
|
global::version);
|
||||||
|
masto.get_stream(Mastodon::API::v1::streaming_user, _stream, _ptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void Listener::stop()
|
||||||
|
{
|
||||||
|
if (_ptr)
|
||||||
|
{
|
||||||
|
_ptr->abort_stream();
|
||||||
|
_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Easy::Notification> Listener::get_new_messages()
|
||||||
|
{
|
||||||
|
const string buffer = _stream;
|
||||||
|
_stream.clear();
|
||||||
|
|
||||||
|
std::vector<Easy::Notification> v;
|
||||||
|
for (const Easy::stream_event &event : Easy::parse_stream(buffer))
|
||||||
|
{
|
||||||
|
if (event.first == Easy::event_type::Notification)
|
||||||
|
{
|
||||||
|
v.push_back(Easy::Notification(event.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mastodon::Easy::Status Listener::get_status(const std::uint_fast64_t &id)
|
||||||
|
{
|
||||||
|
Easy masto(_instance, _access_token);
|
||||||
|
std::uint_fast16_t ret;
|
||||||
|
string answer;
|
||||||
|
|
||||||
|
ret = masto.get(Easy::v1::statuses_id, {{ "id", { std::to_string(id) }}}, answer);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
return Easy::Status(answer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "ERROR: " << ret << '\n';
|
||||||
|
return Easy::Status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool Listener::send_reply(const Easy::Status &status,
|
||||||
|
const string &message)
|
||||||
|
{
|
||||||
|
Easy masto(_instance, _access_token);
|
||||||
|
std::uint_fast16_t ret;
|
||||||
|
string answer;
|
||||||
|
const string id = std::to_string(status.id());
|
||||||
|
string strvisibility;
|
||||||
|
|
||||||
|
switch (status.visibility())
|
||||||
|
{
|
||||||
|
case Easy::visibility_type::Private:
|
||||||
|
strvisibility = "private";
|
||||||
|
break;
|
||||||
|
case Easy::visibility_type::Direct:
|
||||||
|
strvisibility = "direct";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strvisibility = "unlisted";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Easy::parametermap parameters =
|
||||||
|
{
|
||||||
|
{ "in_reply_to_id", { id } },
|
||||||
|
{ "visibility", { strvisibility } },
|
||||||
|
{ "status", { '@' + status.account().acct() + ' ' + message } }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (status.sensitive())
|
||||||
|
{
|
||||||
|
parameters.insert({ "sensitive", { "true" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.spoiler_text().empty())
|
||||||
|
{
|
||||||
|
parameters.insert({ "spoiler_text", { status.spoiler_text() } });
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = masto.post(Easy::v1::statuses, parameters, answer);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <sstream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <curlpp/cURLpp.hpp>
|
#include <curlpp/cURLpp.hpp>
|
||||||
#include <curlpp/Options.hpp>
|
#include <curlpp/Options.hpp>
|
||||||
|
@ -30,7 +30,9 @@ namespace curlopts = curlpp::options;
|
||||||
const string expand(const string &url)
|
const string expand(const string &url)
|
||||||
{
|
{
|
||||||
curlpp::Easy request;
|
curlpp::Easy request;
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
request.setOpt(curlopts::WriteStream(&ss));
|
||||||
request.setOpt<curlopts::CustomRequest>("HEAD");
|
request.setOpt<curlopts::CustomRequest>("HEAD");
|
||||||
request.setOpt<curlopts::Url>(url);
|
request.setOpt<curlopts::Url>(url);
|
||||||
request.setOpt<curlopts::UserAgent>
|
request.setOpt<curlopts::UserAgent>
|
||||||
|
|
Loading…
Reference in New Issue