From 8f083e127daf50910c48f5c35e07f31cc6420f26 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 11 May 2018 06:57:41 +0200 Subject: [PATCH] Posting works now --- CMakeLists.txt | 4 +- README.md | 11 ++- src/expandurl-mastodon.hpp | 45 ++++++++++- src/main.cpp | 86 +++++++++++++++++++- src/masto.cpp | 157 +++++++++++++++++++++++++++++++++++++ src/url.cpp | 4 +- 6 files changed, 298 insertions(+), 9 deletions(-) create mode 100644 src/masto.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fa6f7b..1e10af0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.7) project (expandurl-mastodon - VERSION 0.0.2 + VERSION 0.1.0 LANGUAGES CXX ) @@ -25,5 +25,5 @@ include_directories(${CURL_INCLUDE_DIR}) file(GLOB sources src/*.cpp) 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}) diff --git a/README.md b/README.md index bbafc09..1edb07e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ **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 @@ -28,9 +30,14 @@ Install with `make install`. # 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 diff --git a/src/expandurl-mastodon.hpp b/src/expandurl-mastodon.hpp index 81fd407..c079ef0 100644 --- a/src/expandurl-mastodon.hpp +++ b/src/expandurl-mastodon.hpp @@ -18,9 +18,24 @@ #define EXPANDURL_MASTODON_HPP #include +#include +#include +#include +#include +#include +#include using std::string; +void signal_handler(int signum); +/*! + * @brief Extract URLs from HTML + * + * @return vector of URLs + */ +const std::vector get_urls(const string &html); + + /*! * @brief Expands shortened URLs * @@ -33,7 +48,7 @@ const string expand(const string &url); /*! * @brief Filters out tracking stuff * - * Currently remoces all arguments beginning with `utm_` + * Currently removes all arguments beginning with `utm_` * * @param url URL to filter * @@ -41,4 +56,32 @@ const string expand(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 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 _ptr; + std::thread _thread; +}; + #endif // EXPANDURL_MASTODON_HPP diff --git a/src/main.cpp b/src/main.cpp index 1411bbb..6165367 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,19 +15,99 @@ */ #include -#include +#include +#include +#include #include #include "expandurl-mastodon.hpp" using std::cout; +using std::cerr; 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 get_urls(const string &html) +{ + const std::regex re_url("href=\"([^\"]+)\" rel"); + std::smatch match; + string buffer = html; + std::vector 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[]) { + signal(SIGINT, signal_handler); curlpp::initialize(); - // cout << expand(argv[1]) << '\n'; - cout << strip(argv[1]) << '\n'; + Listener listener; + 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(); diff --git a/src/masto.cpp b/src/masto.cpp new file mode 100644 index 0000000..8f205d6 --- /dev/null +++ b/src/masto.cpp @@ -0,0 +1,157 @@ +/* This file is part of expandurl-mastodon. + * 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 // getenv() +#include +#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(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("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 Listener::get_new_messages() +{ + const string buffer = _stream; + _stream.clear(); + + std::vector 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; + } +} diff --git a/src/url.cpp b/src/url.cpp index 5ee986c..b5f2f67 100644 --- a/src/url.cpp +++ b/src/url.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include #include #include @@ -30,7 +30,9 @@ namespace curlopts = curlpp::options; const string expand(const string &url) { curlpp::Easy request; + std::stringstream ss; + request.setOpt(curlopts::WriteStream(&ss)); request.setOpt("HEAD"); request.setOpt(url); request.setOpt