Compare commits

..

1 Commits

Author SHA1 Message Date
tastytea b3d24f2342
Enhanced CI
the build failed Details
2018-08-14 15:56:53 +02:00
11 changed files with 196 additions and 293 deletions

View File

@ -2,81 +2,39 @@ pipeline:
download: download:
image: plugins/download image: plugins/download
pull: true pull: true
source: https://schlomp.space/tastytea/mastodon-cpp/releases/download/0.106.0/libmastodon-cpp_0.106.0-0_amd64.deb source: https://schlomp.space/attachments/41e6e007-d8fb-483e-90e6-68b98e114562
destination: mastodon-cpp.deb destination: mastodon-cpp.deb
gcc6: gcc6:
image: debian:stretch-slim image: gcc:6
pull: true pull: true
environment:
- LANG=C.utf8
- CXX=g++-6
- CXXFLAGS=-pipe -O2
commands: commands:
- echo "APT::Default-Release \"stretch\";" >> /etc/apt/apt.conf.d/00default_release - echo "deb http://deb.debian.org/debian stretch main" >> /etc/apt/sources.list.d/stretch.list
- echo "deb http://deb.debian.org/debian sid main" >> /etc/apt/sources.list.d/sid.list - echo "deb http://deb.debian.org/debian buster main" >> /etc/apt/sources.list.d/buster.list
- echo "APT::Default-Release \"jessie\";" >> /etc/apt/apt.conf.d/00default_release
- apt-get update -q - apt-get update -q
- apt-get install -qy build-essential cmake pkg-config - apt-get install -qyt stretch cmake
- apt-get install -qy libjsoncpp-dev libcurl4-openssl-dev libxdg-basedir-dev asciidoc - apt-get install -qy libcurl4-openssl-dev libjsoncpp-dev libxdg-basedir-dev
- apt-get install -qy -t sid libcurlpp-dev - apt-get install -qy -t buster libcurlpp-dev
- dpkg -i mastodon-cpp.deb - dpkg -i mastodon-cpp.deb
- rm -rf build && mkdir -p build && cd build - mkdir -p build && cd build
- cmake .. - cmake ..
- make VERBOSE=1 - make VERBOSE=1
- make install DESTDIR=install - make install DESTDIR=install
gcc7: gcc7:
image: debian:stretch-slim image: gcc:7
pull: true pull: true
environment:
- LANG=C.utf8
- CXX=g++-7
- CXXFLAGS=-pipe -O2
commands: commands:
- echo "deb http://deb.debian.org/debian buster main" >> /etc/apt/sources.list.d/buster.list
- echo "APT::Default-Release \"stretch\";" >> /etc/apt/apt.conf.d/00default_release - echo "APT::Default-Release \"stretch\";" >> /etc/apt/apt.conf.d/00default_release
- echo "deb http://deb.debian.org/debian sid main" >> /etc/apt/sources.list.d/sid.list
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main" >> /etc/apt/sources.list.d/ubuntu-toolchain-r.list
- apt-get update -q - apt-get update -q
- apt-get install -qy gnupg - apt-get install -qy cmake
- gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 0x60c317803a41ba51845e371a1e9377a2ba9ef27f - apt-get install -qy libcurl4-openssl-dev libjsoncpp-dev libxdg-basedir-dev
- gpg --armor --export 0x60c317803a41ba51845e371a1e9377a2ba9ef27f | apt-key add - - apt-get install -qy -t buster libcurlpp-dev
- apt-get update -q
- apt-get install -qy build-essential cmake pkg-config
- apt-get install -qy -t xenial g++-7
- apt-get install -qy libjsoncpp-dev libcurl4-openssl-dev libxdg-basedir-dev asciidoc
- apt-get install -qy -t sid libcurlpp-dev
- dpkg -i mastodon-cpp.deb - dpkg -i mastodon-cpp.deb
- rm -rf build && mkdir -p build && cd build - mkdir -p build && cd build
- cmake .. - cmake ..
- make VERBOSE=1 - make VERBOSE=1
- make install DESTDIR=install - make install DESTDIR=install
gcc8:
image: debian:stretch-slim
pull: true
environment:
- LANG=C.utf8
- CXX=g++-8
- CXXFLAGS=-pipe -O2
commands:
- echo "APT::Default-Release \"stretch\";" >> /etc/apt/apt.conf.d/00default_release
- echo "deb http://deb.debian.org/debian sid main" >> /etc/apt/sources.list.d/sid.list
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main" >> /etc/apt/sources.list.d/ubuntu-toolchain-r.list
- apt-get update -q
- apt-get install -qy gnupg
- gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 0x60c317803a41ba51845e371a1e9377a2ba9ef27f
- gpg --armor --export 0x60c317803a41ba51845e371a1e9377a2ba9ef27f | apt-key add -
- apt-get update -q
- apt-get install -qy build-essential cmake pkg-config
- apt-get install -qy -t xenial g++-8
- apt-get install -qy libjsoncpp-dev libcurl4-openssl-dev libxdg-basedir-dev asciidoc
- apt-get install -qy -t sid libcurlpp-dev
- dpkg -i mastodon-cpp.deb
- rm -rf build && mkdir -p build && cd build
- cmake ..
- make VERBOSE=1
- make install DESTDIR=install
notify: notify:
image: drillster/drone-email image: drillster/drone-email
pull: true pull: true

View File

@ -1,8 +1,8 @@
cmake_minimum_required (VERSION 3.7) cmake_minimum_required (VERSION 3.7)
project (expandurl-mastodon project (expandurl-mastodon
VERSION 0.9.14 VERSION 0.9.11
LANGUAGES CXX LANGUAGES CXX
) )
include(GNUInstallDirs) include(GNUInstallDirs)
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
@ -15,10 +15,14 @@ set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS_DEBUG set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
"${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wpedantic -ftrapv \ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
-fsanitize=undefined -g -Og -fno-omit-frame-pointer") # uint_fast16_t can be bigger than 16 bit, but that doesn't matter because
# everything but the last 16 bit is padded with zeroes.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
endif()
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR})
include_directories(${CURL_INCLUDE_DIRS}) include_directories(${CURL_INCLUDE_DIRS})
@ -33,26 +37,13 @@ link_directories(${LIBXDG_BASEDIR_LIBRARY_DIRS})
# Write version in header # Write version in header
configure_file ( configure_file (
"${PROJECT_SOURCE_DIR}/src/version.hpp.in" "${PROJECT_SOURCE_DIR}/src/version.hpp.in"
"${PROJECT_BINARY_DIR}/version.hpp" "${PROJECT_BINARY_DIR}/version.hpp"
) )
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 target_link_libraries(expandurl-mastodon
${CURLPP_LIBRARIES} ${JSONCPP_LIBRARIES} ${LIBXDG_BASEDIR_LIBRARIES} ${CURLPP_LIBRARIES} ${JSONCPP_LIBRARIES}
mastodon-cpp pthread stdc++fs) ${LIBXDG_BASEDIR_LIBRARIES} mastodon-cpp pthread stdc++fs)
install(TARGETS expandurl-mastodon DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS expandurl-mastodon DESTINATION ${CMAKE_INSTALL_BINDIR})
set(WITH_MAN "YES" CACHE STRING "WITH_MAN defaults to \"YES\"")
if (WITH_MAN)
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
DEPENDS "${CMAKE_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.1.adoc"
COMMAND ${CMAKE_SOURCE_DIR}/build_manpage.sh
ARGS ${PROJECT_VERSION})
add_custom_target(run ALL
DEPENDS "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()

View File

@ -21,15 +21,13 @@ or to [@tastytea@soc.ialis.me](https://soc.ialis.me/@tastytea).
## Dependencies ## Dependencies
* Tested OS: Linux * Tested OS: Linux
* C++ compiler (tested: gcc 6/7/8) * C++ compiler (tested: gcc 6.3 / 7.3)
* [cmake](https://cmake.org/) (tested: 3.9 / 3.11) * [cmake](https://cmake.org/) (tested: 3.9 / 3.11)
* [curlpp](http://www.curlpp.org/) (tested: 0.8) * [curlpp](http://www.curlpp.org/) (tested: 0.8 / 0.7)
* [mastodon-cpp](https://schlomp.space/tastytea/mastodon-cpp) (at least: 0.106) * [mastodon-cpp](https://schlomp.space/tastytea/mastodon-cpp) (at least: 0.18)
* [jsoncpp](https://github.com/open-source-parsers/jsoncpp) (tested: 1.8 / 1.7) * [jsoncpp](https://github.com/open-source-parsers/jsoncpp) (tested: 1.8 / 1.7)
* [libxdg-basedir](http://repo.or.cz/w/libxdg-basedir.git) (tested: 1.2) * [libxdg-basedir](http://repo.or.cz/w/libxdg-basedir.git) (tested: 1.2)
* Optional:
* Manpage: [asciidoc](http://asciidoc.org/) (tested: 8.6)
## Get sourcecode ## Get sourcecode
@ -52,20 +50,48 @@ cmake ..
make make
``` ```
cmake options:
* `-DCMAKE_BUILD_TYPE=Debug` for a debug build
* `-DWITH_MAN=NO` to not compile the manpage
Install with `make install`. Install with `make install`.
# Usage # Usage
Have a look at the [manpage](https://schlomp.space/tastytea/expandurl-mastodon/src/branch/master/expandurl-mastodon.1.adoc). **The config file has changed from cfg to JSON in 0.4.0.**
Start expandurl-mastodon without parameters.
If no config file is found, you will be asked to provide your account address
and an access token is generated. The config file can be found in
`${HOME}/.config/expandurl-mastodon.json` and looks like this:
```JSON
{
"account": "expandurl@example.social",
"access_token": "abc123",
"proxy":
{
"url": "socks5h://[::1]:1080/",
"user": "user23",
"password": "supersecure"
},
"replace" :
{
"//amp\\." : "//",
"[\\?&]__twitter_impression=[^&]+" : "",
"[\\?&]utm_[^&]+" : "",
"[\\?&]wt_zmc=[^&]+" : "",
"[\\?&]wtmc=[^&]+" : ""
}
}
```
If you want to use a proxy or define your own replacements, you have to edit the
configuration file manually. After the configuration file is generated, you can
start expandurl-mastodon as
daemon.
# Copyright # Copyright
```PLAIN ```PLAIN
Copyright © 2018, 2019 tastytea <tastytea@tastytea.de>. Copyright © 2018 tastytea <tastytea@tastytea.de>.
License GPLv3: GNU GPL version 3 <https://www.gnu.org/licenses/gpl-3.0.html>. License GPLv3: GNU GPL version 3 <https://www.gnu.org/licenses/gpl-3.0.html>.
This program comes with ABSOLUTELY NO WARRANTY. This is free software, This program comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions. and you are welcome to redistribute it under certain conditions.

View File

@ -1,13 +0,0 @@
#!/bin/sh
name="expandurl-mastodon"
if [ -n "${1}" ]; then
dir="$(dirname ${0})"
version="${1}"
cp -vf "${dir}/${name}.1.adoc" .
sed -Ei "s/(Revision: +)[0-9]+\.[0-9]\.[0-9]/\1${version}/" "${name}.1.adoc"
a2x --doctype manpage --format manpage --no-xmllint "${name}.1.adoc"
else
echo "usage: ${0} VERSION" >&2
fi

View File

@ -1,75 +0,0 @@
= expandurl-mastodon(1)
:Author: tastytea
:Email: tastytea@tastytea.de
:Date: 2019-04-12
:Revision: 0.0.0
:man source: expandurl-mastodon
:man version: {revision}
:man manual: General Commands Manual
== NAME
expandurl-mastodon - Mastodon bot that expands shortened URLs.
== SYNOPSIS
*expandurl-mastodon*
== DESCRIPTION
If you want the bot to expand an URL, reply to the post with the URL in it and
mention the bot account.
This bot uses the same visibility as you, but posts unlisted instead of public.
It retains the sensitive flag and spoiler warnings.
Some tracking parameters, like those beginning with
https://en.wikipedia.org/wiki/UTM_parameters[utm_] are stripped. It also tries
to rewrite https://en.wikipedia.org/wiki/Accelerated_Mobile_Pages[AMP] URLs to
point at the real webpages.
== CONFIGURATION
If no config file is found, you will be asked to provide your account address
and an access token is generated. The config file can be found in
`${XDG_CONFIG_HOME}/expandurl-mastodon.json` and looks like this:
[source,json]
----
{
"account": "expandurl@example.social",
"access_token": "abc123",
"proxy":
{
"url": "socks5h://[::1]:1080/",
"user": "user23",
"password": "supersecure"
},
"replace" :
{
"//amp\\." : "//",
"[\\?&]__twitter_impression=[^&]+" : "",
"[\\?&]utm_[^&]+" : "",
"[\\?&]wt_zmc=[^&]+" : "",
"[\\?&]wtmc=[^&]+" : ""
}
}
----
If you want to use a proxy or define your own replacements, you have to edit the
configuration file manually. After the configuration file is generated, you can
start expandurl-mastodon as daemon.
== FILES
- *Configuration file*: `${XDG_CONFIG_HOME}/expandurl-mastodon.json`
`${XDG_CONFIG_HOME}` is usually `~/.config`.
== REPORTING BUGS
Bugtracker: https://schlomp.space/tastytea/expandurl-mastodon/issues
E-mail: tastytea@tastytea.de
Fediverse: https://soc.ialis.me/@tastytea

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -42,7 +42,7 @@ ConfigJSON::ConfigJSON(const string &filename, const string &subdir)
_filepath += '/' + filename; _filepath += '/' + filename;
} }
bool ConfigJSON::read() const bool ConfigJSON::read()
{ {
std::ifstream file(_filepath); std::ifstream file(_filepath);
if (file.is_open()) if (file.is_open())
@ -60,7 +60,7 @@ bool ConfigJSON::read()
} }
} }
bool ConfigJSON::write() const bool ConfigJSON::write()
{ {
std::ofstream file(_filepath); std::ofstream file(_filepath);
if (file.is_open()) if (file.is_open())

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -43,14 +43,14 @@ public:
* *
* @return `true` on success * @return `true` on success
*/ */
bool read(); const bool read();
/*! /*!
* @brief Write the file * @brief Write the file
* *
* @return `true` on success * @return `true` on success
*/ */
bool write(); const bool write();
/*! /*!
* @brief Returns a reference to the config as Json::Value * @brief Returns a reference to the config as Json::Value

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -27,15 +27,15 @@
#include <jsoncpp/json/json.h> #include <jsoncpp/json/json.h>
#include "configjson.hpp" #include "configjson.hpp"
using namespace Mastodon;
using std::string; using std::string;
using Mastodon::API; using Mastodon::API;
using Mastodon::Easy;
extern ConfigJSON configfile; extern ConfigJSON configfile;
void signal_handler(int signum); void signal_handler(int signum);
/*! /*!
* @brief Extract URLs from HTML * @brief Extract URLs from HTML
* *
@ -54,7 +54,7 @@ const string expand(const string &url);
/*! /*!
* @brief Filters out tracking stuff * @brief Filters out tracking stuff
* *
* Currently removes all arguments beginning with `utm_` * Currently removes all arguments beginning with `utm_`
* *
* @param url URL to filter * @param url URL to filter
@ -70,7 +70,7 @@ const string strip(const string &url);
* inserted. * inserted.
* *
*/ */
void init_replacements(); const void init_replacements();
class Listener class Listener
@ -82,24 +82,24 @@ public:
/*! /*!
* @brief Starts listening on Mastodon * @brief Starts listening on Mastodon
*/ */
void start(); const void start();
/*! /*!
* @brief Stops listening on Mastodon * @brief Stops listening on Mastodon
*/ */
void stop(); const void stop();
const std::vector<Easy::Notification> get_new_messages(); const std::vector<Easy::Notification> get_new_messages();
const std::vector<Easy::Notification> catchup(); const std::vector<Easy::Notification> catchup();
Easy::Status get_status(const string &id); Easy::Status get_status(const std::uint_fast64_t &id);
bool send_reply(const Easy::Status &to_status, const string &message); const bool send_reply(const Easy::Status &to_status, const string &message);
const string get_parent_id(const Easy::Notification &notif); const std::uint_fast64_t get_parent_id(const Easy::Notification &notif);
bool stillrunning() const; const bool stillrunning() const;
private: private:
string _instance; string _instance;
string _access_token; string _access_token;
std::unique_ptr<Easy::API> _masto; std::unique_ptr<Easy> _masto;
string _stream; string _stream;
std::unique_ptr<API::http> _ptr; std::unique_ptr<API::http> _ptr;
std::thread _thread; std::thread _thread;
@ -109,10 +109,10 @@ private:
string _proxy_password; string _proxy_password;
Json::Value &_config; Json::Value &_config;
void read_config(); const void read_config();
bool write_config(); const bool write_config();
bool register_app(); const bool register_app();
void set_proxy(Easy::API &masto); const void set_proxy(Easy &masto);
}; };
#endif // EXPANDURL_MASTODON_HPP #endif // EXPANDURL_MASTODON_HPP

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -18,16 +18,14 @@
#include <chrono> #include <chrono>
#include <csignal> #include <csignal>
#include <regex> #include <regex>
#include <numeric>
#include <syslog.h> #include <syslog.h>
#include <unistd.h> // getuid() #include <unistd.h> // getuid()
#include <curlpp/cURLpp.hpp> #include <curlpp/cURLpp.hpp>
#include "configjson.hpp" #include "configjson.hpp"
#include "expandurl-mastodon.hpp" #include "expandurl-mastodon.hpp"
using namespace Mastodon;
using std::string; using std::string;
using Mastodon::Easy;
bool running = true; bool running = true;
ConfigJSON configfile("expandurl-mastodon.json"); ConfigJSON configfile("expandurl-mastodon.json");
@ -52,15 +50,14 @@ void signal_handler(int signum)
} }
} }
int main() int main(int argc, char *argv[])
{ {
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
if (!configfile.read()) if (!configfile.read())
{ {
syslog(LOG_WARNING, "Could not open %s.", syslog(LOG_WARNING, "Could not open %s.", configfile.get_filepath().c_str());
configfile.get_filepath().c_str());
} }
init_replacements(); init_replacements();
@ -92,26 +89,25 @@ int main()
for (Easy::Notification &notif : new_messages) for (Easy::Notification &notif : new_messages)
{ {
syslog(LOG_DEBUG, "new message"); syslog(LOG_DEBUG, "new message");
const string id = listener.get_parent_id(notif); const std::uint_fast64_t id = listener.get_parent_id(notif);
syslog(LOG_DEBUG, "in_reply_to_id: %s", id.c_str()); syslog(LOG_DEBUG, "in_reply_to_id: %lu", id);
Easy::Status status; Easy::Status status;
if (!id.empty()) if (id > 0)
{ {
status = listener.get_status(id); status = listener.get_status(id);
if (status.valid()) if (status.valid())
{ {
const std::vector<string> vec = get_urls(status.content()); string message = "";
const string message = for (const string &url : get_urls(status.content()))
std::accumulate(vec.begin(), vec.end(), string(), {
[](const string &s1, const string s2) message += url + " \n";
{ return s1 + s2 + " \n"; }); }
if (!message.empty()) if (!message.empty())
{ {
if (!listener.send_reply(notif.status(), message)) if (!listener.send_reply(notif.status(), message))
{ {
syslog(LOG_ERR, "could not send reply to %s", syslog(LOG_ERR, "could not send reply to %lu", id);
id.c_str());
} }
} }
else else

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -26,7 +26,6 @@
using std::cout; using std::cout;
using std::string; using std::string;
using std::uint8_t;
Listener::Listener() Listener::Listener()
: _instance("") : _instance("")
@ -60,7 +59,7 @@ Listener::Listener()
} }
} }
_masto = std::make_unique<Easy::API>(_instance, _access_token); _masto = std::make_unique<Easy>(_instance, _access_token);
_masto->set_useragent(static_cast<const string>("expandurl-mastodon/") + _masto->set_useragent(static_cast<const string>("expandurl-mastodon/") +
global::version); global::version);
set_proxy(*_masto); set_proxy(*_masto);
@ -70,7 +69,7 @@ Listener::~Listener()
{ {
} }
void Listener::read_config() const void Listener::read_config()
{ {
_instance = _config["account"].asString(); _instance = _config["account"].asString();
_instance = _instance.substr(_instance.find('@') + 1); _instance = _instance.substr(_instance.find('@') + 1);
@ -80,15 +79,44 @@ void Listener::read_config()
_proxy_password = _config["proxy"]["password"].asString(); _proxy_password = _config["proxy"]["password"].asString();
} }
void Listener::start() const void Listener::start()
{ {
_running = true; constexpr uint_fast8_t delay_after_error = 120;
_masto->get_stream(API::v1::streaming_user, _ptr, _stream); 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));
}
syslog(LOG_NOTICE, "Connecting to %s ...", _instance.c_str()); 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));
}
} }
void Listener::stop() const void Listener::stop()
{ {
if (!configfile.write()) if (!configfile.write())
{ {
@ -120,27 +148,16 @@ const std::vector<Easy::Notification> Listener::get_new_messages()
std::lock_guard<std::mutex> lock(_ptr->get_mutex()); std::lock_guard<std::mutex> lock(_ptr->get_mutex());
if (!_stream.empty()) if (!_stream.empty())
{ {
for (const Easy::stream_event_type &event : Easy::parse_stream(_stream)) for (const Easy::stream_event &event : Easy::parse_stream(_stream))
{ {
if (event.type == Easy::event_type::Notification) if (event.first == Easy::event_type::Notification)
{ {
Easy::Notification notif(event.data); Easy::Notification notif(event.second);
if (notif.type() == Easy::notification_type::Mention) if (notif.type() == Easy::notification_type::Mention)
{ {
v.push_back(notif); 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(); _stream.clear();
lastping = system_clock::now(); lastping = system_clock::now();
@ -166,51 +183,54 @@ const std::vector<Easy::Notification> Listener::catchup()
if (last_id != "") if (last_id != "")
{ {
syslog(LOG_DEBUG, "Catching up..."); syslog(LOG_DEBUG, "Catching up...");
parameters parameter = API::parametermap parameter =
{ {
{ "since_id", { last_id } }, { "since_id", { last_id } },
{ "exclude_types", { "follow", "favourite", "reblog" } } { "exclude_types", { "follow", "favourite", "reblog" } }
}; };
return_call ret;; string answer;
std::uint_fast16_t ret;
ret = _masto->get(API::v1::notifications, parameter); ret = _masto->get(API::v1::notifications, parameter, answer);
if (ret) if (ret == 0)
{ {
for (const string str : Easy::json_array_to_vector(ret.answer)) for (const string str : Easy::json_array_to_vector(answer))
{ {
v.push_back(Easy::Notification(str)); v.push_back(Easy::Notification(str));
} }
} }
else else
{ {
syslog(LOG_ERR, "Could not catch up: Error %u", ret.error_code); syslog(LOG_ERR, "Could not catch up: Error %u", ret);
} }
} }
return v; return v;
} }
Mastodon::Easy::Status Listener::get_status(const string &id) Mastodon::Easy::Status Listener::get_status(const std::uint_fast64_t &id)
{ {
return_call ret; std::uint_fast16_t ret;
string answer;
ret = _masto->get(API::v1::statuses_id, {{ "id", { id }}}); ret = _masto->get(API::v1::statuses_id, {{ "id", { std::to_string(id) }}},
if (ret) answer);
if (ret == 0)
{ {
return Easy::Status(ret.answer); return Easy::Status(answer);
} }
else else
{ {
syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__);
return Easy::Status(); return Easy::Status();
} }
} }
bool Listener::send_reply(const Easy::Status &to_status, const bool Listener::send_reply(const Easy::Status &to_status,
const string &message) const string &message)
{ {
Easy::return_entity<Easy::Status> ret; std::uint_fast16_t ret = 0;
Easy::Status new_status; Easy::Status new_status;
if (to_status.visibility() == Easy::visibility_type::Public) if (to_status.visibility() == Easy::visibility_type::Public)
@ -226,52 +246,55 @@ bool Listener::send_reply(const Easy::Status &to_status,
new_status.sensitive(to_status.sensitive()); new_status.sensitive(to_status.sensitive());
new_status.spoiler_text(to_status.spoiler_text()); new_status.spoiler_text(to_status.spoiler_text());
ret = _masto->send_post(new_status); _masto->send_toot(new_status, ret);
if (ret) if (ret == 0)
{ {
syslog(LOG_DEBUG, "Sent reply"); syslog(LOG_DEBUG, "Sent reply");
return true; return true;
} }
else else
{ {
syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__);
return false; return false;
} }
} }
const string Listener::get_parent_id(const Easy::Notification &notif) const std::uint_fast64_t Listener::get_parent_id(const Easy::Notification &notif)
{ {
return_call ret; string answer;
std::uint_fast16_t ret;
// Retry up to 2 times // Retry up to 2 times
for (std::uint_fast8_t retries = 1; retries <= 2; ++retries) for (std::uint_fast8_t retries = 1; retries <= 2; ++retries)
{ {
// Fetch full status // Fetch full status
ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}}); ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}},
if (!ret) answer);
if (ret > 0)
{ {
syslog(LOG_ERR, "Error %u: Could not fetch status (in %s).", syslog(LOG_ERR, "Error %u: Could not fetch status (in %s).",
ret.error_code, __FUNCTION__); ret, __FUNCTION__);
return 0; return 0;
} }
ret = _masto->get(API::v1::statuses_id, ret = _masto->get(API::v1::statuses_id,
{{ "id", { notif.status().id() }}}); {{ "id", { std::to_string(notif.status().id()) }}},
answer);
if (!ret) if (ret > 0)
{ {
syslog(LOG_ERR, "Error %u: Could not get status (in %s).", syslog(LOG_ERR, "Error %u: Could not get status (in %s).",
ret.error_code, __FUNCTION__); ret, __FUNCTION__);
return 0; return 0;
} }
else else
{ {
_config["last_id"] = notif.id(); _config["last_id"] = std::to_string(notif.id());
const Easy::Status s(ret.answer); const Easy::Status s(answer);
// If parent is found, return ID; else retry // If parent is found, return ID; else retry
if (!s.in_reply_to_id().empty()) if (s.in_reply_to_id() != 0)
{ {
return s.in_reply_to_id(); return s.in_reply_to_id();
} }
@ -286,23 +309,23 @@ const string Listener::get_parent_id(const Easy::Notification &notif)
return 0; return 0;
} }
bool Listener::stillrunning() const const bool Listener::stillrunning() const
{ {
return _running; return _running;
} }
bool Listener::register_app() const bool Listener::register_app()
{ {
cout << "Account (username@instance): "; cout << "Account (username@instance): ";
std::cin >> _instance; std::cin >> _instance;
_config["account"] = _instance; _config["account"] = _instance;
_instance = _instance.substr(_instance.find('@') + 1); _instance = _instance.substr(_instance.find('@') + 1);
_masto = std::make_unique<Easy::API>(_instance, ""); _masto = std::make_unique<Easy>(_instance, "");
_masto->set_useragent(static_cast<const string>("expandurl-mastodon/") + _masto->set_useragent(static_cast<const string>("expandurl-mastodon/") +
global::version); global::version);
return_call ret; std::uint_fast16_t ret;
string client_id, client_secret, url; string client_id, client_secret, url;
ret = _masto->register_app1("expandurl-mastodon", ret = _masto->register_app1("expandurl-mastodon",
"urn:ietf:wg:oauth:2.0:oob", "urn:ietf:wg:oauth:2.0:oob",
@ -311,7 +334,7 @@ bool Listener::register_app()
client_id, client_id,
client_secret, client_secret,
url); url);
if (ret) if (ret == 0)
{ {
string code; string code;
cout << "Visit " << url << " to authorize this application.\n"; cout << "Visit " << url << " to authorize this application.\n";
@ -322,25 +345,25 @@ bool Listener::register_app()
"urn:ietf:wg:oauth:2.0:oob", "urn:ietf:wg:oauth:2.0:oob",
code, code,
_access_token); _access_token);
if (ret) if (ret == 0)
{ {
_config["access_token"] = _access_token; _config["access_token"] = _access_token;
return true; return true;
} }
else else
{ {
syslog(LOG_ERR, "register_app2(): %u", ret.error_code); syslog(LOG_ERR, "register_app2(): %u", ret);
} }
} }
else else
{ {
syslog(LOG_ERR, "register_app1(): %u", ret.error_code); syslog(LOG_ERR, "register_app1(): %u", ret);
} }
return false; return false;
} }
void Listener::set_proxy(Easy::API &masto) const void Listener::set_proxy(Mastodon::Easy &masto)
{ {
if (!_proxy.empty()) if (!_proxy.empty())
{ {

View File

@ -1,5 +1,5 @@
/* This file is part of expandurl-mastodon. /* This file is part of expandurl-mastodon.
* Copyright © 2018, 2019 tastytea <tastytea@tastytea.de> * Copyright © 2018 tastytea <tastytea@tastytea.de>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -41,10 +41,10 @@ const std::vector<string> get_urls(const string &html)
// Add URL to vector if it is not a mention.# // Add URL to vector if it is not a mention.#
if (match[2].str().find("mention") == std::string::npos) if (match[2].str().find("mention") == std::string::npos)
{ {
string url = unescape_html(match[1].str()); string url = Easy::unescape_html(match[1].str());
v.push_back(strip(expand(url))); v.push_back(strip(expand(url)));
buffer = match.suffix().str();
} }
buffer = match.suffix().str();
} }
return v; return v;
@ -65,7 +65,6 @@ const string expand(const string &url)
"Connection: close", "Connection: close",
}); });
request.setOpt<curlopts::FollowLocation>(true); request.setOpt<curlopts::FollowLocation>(true);
request.setOpt(curlopts::Timeout(30));
try try
{ {
@ -106,20 +105,18 @@ const string strip(const string &url)
return newurl; return newurl;
} }
void init_replacements() const void init_replacements()
{ {
using replace_pair = std::pair<const std::string, const std::string>; using replace_pair = std::pair<const std::string, const std::string>;
Json::Value &config = configfile.get_json(); Json::Value &config = configfile.get_json();
if (config["replace"].isNull()) if (config["replace"].isNull())
{ {
const std::array<const replace_pair, 6> replace_array = const std::array<const replace_pair, 5> replace_array =
{{ {{
{ "[\\?&]utm_[^&]+", "" }, // Google { "[\\?&]utm_[^&]+", "" }, // Google
{ "[\\?&]wt_?[^&]+", "" }, // Twitter? { "[\\?&]wt_?[^&]+", "" }, // Twitter?
{ "[\\?&]__twitter_impression=[^&]+", "" }, // Twitter? { "[\\?&]__twitter_impression=[^&]+", "" }, // Twitter?
{ "//amp\\.", "//" }, // AMP { "//amp\\.", "//" } // AMP
{ "/amp/", "" }, // AMP
{ "[\\?&]service=amp", "" } // AMP
}}; }};
for (const replace_pair &pair : replace_array) for (const replace_pair &pair : replace_array)