From 8870c37a42af9467e066e3f88c4bda1cf8393a39 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 20 Dec 2019 01:11:40 +0100 Subject: [PATCH] Delete old sources. --- src/config.cpp | 194 ------------------------------------------- src/http.cpp | 87 -------------------- src/mastorss.cpp | 164 ------------------------------------ src/mastorss.hpp | 43 ---------- src/parse.cpp | 210 ----------------------------------------------- 5 files changed, 698 deletions(-) delete mode 100644 src/config.cpp delete mode 100644 src/http.cpp delete mode 100644 src/mastorss.cpp delete mode 100644 src/mastorss.hpp delete mode 100644 src/parse.cpp diff --git a/src/config.cpp b/src/config.cpp deleted file mode 100644 index c0edeaa..0000000 --- a/src/config.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* This file is part of mastorss. - * Copyright © 2018, 2019 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 -#include -#include -#include -#include -#include -#include -#include "mastorss.hpp" - -using std::cout; -using std::cerr; -using std::cin; -using std::string; -namespace fs = std::experimental::filesystem; - -std::uint16_t read_config(string &instance, string &access_token, string &feedurl) -{ - bool config_changed = false; - - // Read config file, get access token - std::ifstream file(filepath + "config-" + profile + ".json"); - if (file.is_open()) - { - std::stringstream json; - json << file.rdbuf(); - file.close(); - json >> config; - - instance = config[profile]["instance"].asString(); - access_token = config[profile]["access_token"].asString(); - feedurl = config[profile]["feedurl"].asString(); - if (!config[profile]["max_size"].isNull()) - { - max_size = config[profile]["max_size"].asUInt(); - } - else - { - config[profile]["max_size"] = max_size; - config_changed = true; - } - } - else - { - cout << "Config file not found. Building new one.\n"; - fs::create_directory(filepath); - } - - if (instance.empty()) - { - cout << "Instance: "; - std::getline(cin, instance); - config[profile]["instance"] = instance; - config_changed = true; - } - if (access_token.empty()) - { - cout << "No access token found.\n"; - string client_id, client_secret, 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; - 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) - { - config[profile]["access_token"] = access_token; - config_changed = true; - } - else - { - cerr << "Error code: " << ret.error_code << '\n'; - return ret.error_code; - } - } - else - { - cerr << "Error code: " << ret.error_code << '\n'; - return ret.error_code; - } - - } - if (feedurl.empty()) - { - cout << "feedurl: "; - std::getline(cin, feedurl); - config[profile]["feedurl"] = feedurl; - config_changed = true; - } - if (config[profile]["titles_only"].isNull()) - { - string titles_only; - cout << "post only titles? [y/n]: "; - std::getline(cin, titles_only); - if (titles_only[0] == 'y') - { - config[profile]["titles_only"] = true; - } - else - { - config[profile]["titles_only"] = false; - } - config_changed = true; - } - if (config[profile]["titles_as_cw"].isNull()) - { - string titles_as_cw; - cout << "Use titles as CW? [y/n]: "; - std::getline(cin, titles_as_cw); - if (titles_as_cw[0] == 'y') - { - config[profile]["titles_as_cw"] = true; - } - else - { - config[profile]["titles_as_cw"] = false; - } - config_changed = true; - } - if (config[profile]["append"].isNull()) - { - string append; - cout << "Append this string to each post []: "; - std::getline(cin, append); - config[profile]["append"] = append; - config_changed = true; - } - if (config[profile]["interval"].isNull()) - { - string interval; - cout << "Interval between posts in seconds [60]: "; - std::getline(cin, interval); - if (interval.empty()) - { - interval = "60"; - } - config[profile]["interval"] = Json::Value::UInt64(std::stoul(interval)); - config_changed = true; - } - if (config_changed) - { - write_config(); - } - - return 0; -} - - -bool write_config() -{ - std::ofstream outfile(filepath + "config-" + profile + ".json"); - if (outfile.is_open()) - { - outfile.write(config.toStyledString().c_str(), - config.toStyledString().length()); - outfile.close(); - - return true; - } - - return false; -} diff --git a/src/http.cpp b/src/http.cpp deleted file mode 100644 index 71c0bd8..0000000 --- a/src/http.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* This file is part of mastorss. - * Copyright © 2018, 2019 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mastorss.hpp" - -using std::string; -using std::cerr; - -namespace curlopts = curlpp::options; - -void curlpp_init() -{ - curlpp::initialize(); -} - -std::uint16_t http_get(const string &feedurl, string &answer, - const string &useragent) -{ - try - { - std::ostringstream oss; - curlpp::Easy request; - request.setOpt(feedurl); - request.setOpt(useragent); - request.setOpt( - { - "Connection: close", - }); - request.setOpt(true); - request.setOpt(&oss); - - request.perform(); - std::uint16_t ret = curlpp::infos::ResponseCode::get(request); - if (ret == 200 || ret == 302 || ret == 307) - { // OK or Found or Temporary Redirect - answer = oss.str(); - } - else if (ret == 301 || ret == 308) - { // Moved Permanently or Permanent Redirect - // FIXME: The new URL should be passed back somehow - answer = oss.str(); - } - else - { - return ret; - } - - return 0; - } - // TODO: More error codes - catch (curlpp::RuntimeError &e) - { - cerr << "RUNTIME ERROR: " << e.what() << std::endl; - return 0xffff; - } - catch (curlpp::LogicError &e) - { - cerr << "LOGIC ERROR: " << e.what() << std::endl; - return 0xffff; - } - - return 0; -} diff --git a/src/mastorss.cpp b/src/mastorss.cpp deleted file mode 100644 index 28e33a8..0000000 --- a/src/mastorss.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* This file is part of mastorss. - * Copyright © 2018, 2019 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 -#include -#include // getenv() -#include -#include -#include -#include -#include -#include -#include "version.hpp" -#include "mastorss.hpp" - -using namespace Mastodon; - -using std::cout; -using std::cerr; -using std::cin; -using std::endl; -using std::string; -using std::this_thread::sleep_for; -using std::chrono::seconds; - -// Initialize global variables -std::uint16_t max_size = 500; -const string filepath = string(getenv("HOME")) + "/.config/mastorss/"; -Json::Value config; -std::string profile; - -int main(int argc, char *argv[]) -{ - if (argc < 2) - { - cerr << "usage: " << argv[0] << " [max size]\n"; - return 10; - } - - if (argc == 3) - { - max_size = std::stoi(argv[2]); - } - - string instance = ""; - string access_token = ""; - string feedurl = ""; - profile = argv[1]; - std::uint_fast16_t ret; - string answer; - std::vector entries; - - read_config(instance, access_token, feedurl); - curlpp_init(); - - ret = http_get(feedurl, answer, "mastorss/" + (string)global::version); - if (ret != 0) - { - std::cerr << "Error code: " << ret << '\n'; - std::cerr << answer << '\n'; - return ret; - } - entries = parse_feed(answer); - - string last_entry = config[profile]["last_entry"].asString(); - if (last_entry.empty()) - { - // If no last_entry is stored in the config file, - // make last_entry the second-newest entry. - last_entry = entries.at(1).content(); - } - config[profile]["last_entry"] = entries.front().content(); - - bool new_content = false; - for (auto rit = entries.rbegin(); rit != entries.rend(); ++rit) - { - if (!new_content && (*rit).content().compare(last_entry) == 0) - { - // If the last entry is found in entries, - // start tooting in the next loop. - new_content = true; - continue; - } - else if (!new_content) - { - continue; - } - - Easy::return_entity ret_status; - Mastodon::Easy::API masto(instance, access_token); - - ret_status = masto.send_post(*rit); - - if (!ret_status) - { - const uint8_t err = ret_status.error_code; - switch (err) - { - case 110: - { - cerr << "Error " << err << ": Timeout\n"; - break; - } - case 111: - { - cerr << "Error " << err << ": Connection refused\n"; - cerr << "HTTP Error " << ret_status.http_error_code << endl; - break; - } - case 113: - { - cerr << "Error " << err << ": Could not reach host.\n"; - break; - } - case 192: - case 193: - { - cerr << "Error " << err << ": curlpp error\n"; - break; - } - default: - { - cerr << "Error " << err << '\n'; - cerr << "HTTP status " << ret_status.http_error_code << endl; - } - } - - cerr << ret_status.entity.to_string() << '\n'; - return ret; - } - - if (!ret_status.entity.valid()) - { - cerr << "Could not send post for unknown reasons.\n"; - cerr << "Please file a bug at " - ".\n"; - return 1; - } - - if (rit != entries.rend()) - { // Only sleep if this is not the last entry - sleep_for(seconds(config[profile]["interval"].asUInt64())); - } - } - - // Write the new last_entry only if no error happened. - write_config(); - - return 0; -} diff --git a/src/mastorss.hpp b/src/mastorss.hpp deleted file mode 100644 index 7d9a6e8..0000000 --- a/src/mastorss.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of mastorss. - * Copyright © 2018, 2019 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 . - */ - -#ifndef mastorss_HPP -#define mastorss_HPP - -#include -#include -#include -#include -#include - -using std::string; - -extern std::uint16_t max_size; -extern const string filepath; -extern Json::Value config; -extern std::string profile; - -std::uint16_t read_config(string &instance, string &access_token, string &feedurl); -bool write_config(); - -std::vector parse_feed(const string &xml); -void individual_fixes(string &str); - -std::uint16_t http_get(const string &feedurl, - string &answer, const string &useragent = ""); -void curlpp_init(); - -#endif // mastorss_HPP diff --git a/src/parse.cpp b/src/parse.cpp deleted file mode 100644 index a6fcd58..0000000 --- a/src/parse.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* This file is part of mastorss. - * Copyright © 2018, 2019 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mastorss.hpp" - -using std::cerr; -using std::string; -namespace pt = boost::property_tree; - -std::vector parse_feed(const string &xml) -{ - Json::Value list; - std::vector watchwords; - - std::ifstream file(filepath + "watchwords.json"); - if (file.is_open()) - { - std::stringstream json; - json << file.rdbuf(); - file.close(); - json >> list; - } - else - { - cerr << "WARNING: " << filepath << "watchwords.json not found or not readable.\n"; - } - - // Read profile-specific hashtags or fail silently - const Json::Value &tags_profile = list[profile]["tags"]; - std::transform(tags_profile.begin(), tags_profile.end(), - std::back_inserter(watchwords), - [](const Json::Value &value) - { return value.asString(); }); - - // Read global hashtags or fail silently - const Json::Value &tags_global = list["global"]["tags"]; - std::transform(tags_global.begin(), tags_global.end(), - std::back_inserter(watchwords), - [](const Json::Value &value) - { return value.asString(); }); - - pt::ptree rss; - std::istringstream iss(xml); - pt::read_xml(iss, rss); - std::vector ret; - - for (const pt::ptree::value_type &chanchild : rss.get_child("rss.channel")) - { - if (chanchild.second.size() > 0) - { - if (string(chanchild.first.data()).compare("item") == 0) - { - string title = chanchild.second.get_child("title").data(); - string link = chanchild.second.get_child("link").data(); - string desc = chanchild.second.get_child("description").data(); - - Mastodon::Easy::Status status; - string content = ""; - if (config[profile]["titles_as_cw"].asBool()) - { - status.spoiler_text(Mastodon::unescape_html(title)); - } - else - { - content = title; - } - if (!config[profile]["titles_only"].asBool()) - { - if (!content.empty()) - { - content += "\n\n"; - } - content += desc; - - // Shrink overly long texts, to speed up replace operations - if (content.length() > 2000) - { - content.resize(2000); - } - } - - bool skipthis = false; - try - { - // Skip entries beginning with this text - for (const Json::Value &v : config[profile]["skip"]) - { - const string skip = v.asString(); - if (!skip.empty()) - { - if (title.compare(0, skip.length(), skip) == 0) - { - skipthis = true; - break; - } - } - } - } - catch (const std::exception &e) - { - // Node not found, no problem - } - if (skipthis) - { - continue; - } - - content = Mastodon::unescape_html(content); - - // Try to turn the HTML into human-readable text - std::regex reparagraph("

"); - std::regex recdata1(""); - std::regex restrip("<[^>]*>"); - - individual_fixes(content); - - content = std::regex_replace(content, reparagraph, "\n\n"); - content = std::regex_replace(content, recdata1, ""); - content = std::regex_replace(content, recdata2, ""); - content = std::regex_replace(content, restrip, ""); - // remove \r - content = std::regex_replace(content, std::regex("\\r"), ""); - // replace NO-BREAK SPACE with space (UTF-8: 0xc2a0) - content = std::regex_replace(content, std::regex("\u00a0"), " "); - // remove whitespace between newlines - content = std::regex_replace(content, std::regex("\\n[ \t]+\\n"), ""); - // remove excess newlines - content = std::regex_replace(content, std::regex("\\n{3,}"), "\n\n"); - - for (const string &hashtag : watchwords) - { - std::regex rehashtag("([[:space:][:punct:]]|^)(" + hashtag - + ")([[:space:][:punct:]]|$)", - std::regex_constants::icase); - content = std::regex_replace(content, rehashtag, "$1#$2$3", - std::regex_constants::format_first_only); - } - // Why is this necessary? Why does ##hashtag happen? - content = std::regex_replace(content, std::regex("##"), "#"); - - uint16_t appendix_size = config[profile]["append"].asString().length(); - if ((status.spoiler_text().size() + content.size() + link.size() + appendix_size) - > static_cast(max_size - 4)) - { - content.resize((max_size - status.spoiler_text().size() - - link.size() - appendix_size - 4)); - content.resize(content.rfind(' ')); // Cut at word boundary - content += " […]"; - } - // Remove trailing newlines - while (content.back() == '\n' || - content.back() == '\r') - { - content.resize(content.length() - 1); - } - - content += "\n\n" + link; - - if (!config[profile]["append"].empty()) - { - content += "\n\n" + config[profile]["append"].asString(); - } - status.content(content); - ret.push_back(status); - } - } - } - - return ret; -} - -// Read regular expressions from the config file and delete all matches. -void individual_fixes(string &str) -{ - for (const Json::Value &v : config[profile]["fixes"]) - { - std::regex refix(v.asString()); - str = std::regex_replace(str, refix, ""); - } -}