Skip to content
Snippets Groups Projects
Commit 3f3082e1 authored by Nicolas Werner's avatar Nicolas Werner
Browse files

Add example, that downloads all unencrypted media in a room

parent fd5208d8
No related branches found
No related tags found
No related merge requests found
......@@ -10,6 +10,9 @@ include_directories(../tests)
add_executable(room_feed room_feed.cpp)
target_link_libraries(room_feed MatrixClient::MatrixClient)
add_executable(media_downloader media_downloader.cpp)
target_link_libraries(media_downloader MatrixClient::MatrixClient)
add_executable(simple_bot simple_bot.cpp)
target_link_libraries(simple_bot MatrixClient::MatrixClient)
......
#include <boost/beast.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <variant>
#include "mtx.hpp"
#include "mtxclient/http/client.hpp"
#include "mtxclient/http/errors.hpp"
//
// Simple usage example of the /login & /sync endpoints which
// will print the stream of messages from all rooms as received by the client.
//
using namespace std;
using namespace mtx::client;
using namespace mtx::http;
using namespace mtx::events;
using TimelineEvent = mtx::events::collections::TimelineEvents;
std::string room_id_to_download_from;
namespace {
std::shared_ptr<Client> client = nullptr;
}
void
print_errors(RequestErr err)
{
if (err->status_code != boost::beast::http::status::unknown)
cout << err->status_code << "\n";
if (!err->matrix_error.error.empty())
cout << err->matrix_error.error << "\n";
if (err->error_code)
cout << err->error_code.message() << "\n";
}
// Check if the given event has a textual representation.
bool
is_room_message(const TimelineEvent &e)
{
return (std::holds_alternative<mtx::events::RoomEvent<msg::Audio>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::Emote>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::File>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::Image>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::Notice>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::Text>>(e)) ||
(std::holds_alternative<mtx::events::RoomEvent<msg::Video>>(e));
}
// Retrieves the fallback body value from the event.
std::string
get_body(const TimelineEvent &e)
{
if (auto ev = std::get_if<RoomEvent<msg::Audio>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::Emote>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::File>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::Image>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::Notice>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::Text>>(&e); ev != nullptr)
return ev->content.body;
else if (auto ev = std::get_if<RoomEvent<msg::Video>>(&e); ev != nullptr)
return ev->content.body;
return "";
}
// Retrieves the fallback body value from the event.
std::string
get_url(const TimelineEvent &e)
{
if (auto ev = std::get_if<RoomEvent<msg::Audio>>(&e); ev != nullptr)
return ev->content.url;
else if (auto ev = std::get_if<RoomEvent<msg::File>>(&e); ev != nullptr)
return ev->content.url;
else if (auto ev = std::get_if<RoomEvent<msg::Image>>(&e); ev != nullptr)
return ev->content.url;
else if (auto ev = std::get_if<RoomEvent<msg::Video>>(&e); ev != nullptr)
return ev->content.url;
return "";
}
// Retrieves the sender of the event.
std::string
get_sender(const TimelineEvent &event)
{
return std::visit([](auto e) { return e.sender; }, event);
}
// Simple print of the message contents.
void
print_message(const TimelineEvent &event)
{
if (is_room_message(event))
cout << get_sender(event) << ": " << get_body(event) << "\n";
if (auto url = get_url(event); !url.empty()) {
cout << "found url: " << url << "\n";
client->download(url,
[url](const std::string &data,
const std::string &content_type,
const std::string &original_filename,
RequestErr err) {
if (err) {
cout << "download error:\n";
print_errors(err);
return;
}
std::string_view urlv = url;
urlv.remove_prefix(std::size("mxc://") - 1);
std::filesystem::path p(urlv);
std::filesystem::create_directories(p.parent_path());
ofstream file(p, std::ios::binary);
file.write(data.data(), data.size());
if (file.good())
cout << "Wrote to: " << p
<< ", original filename was '"
<< original_filename << "', content_type '"
<< content_type << "'\n";
else
cout << "Write to '" << p << "' failed!\n";
});
}
}
// Callback to executed after a /sync request completes.
void
message_handler(const mtx::responses::Messages &res, RequestErr err)
{
MessagesOpts opts;
opts.room_id = room_id_to_download_from;
opts.limit = 300;
opts.from = res.end;
if (err) {
cout << "messages error:\n";
print_errors(err);
opts.from = res.start;
client->messages(opts, &message_handler);
return;
}
for (const auto msg : res.chunk)
print_message(msg);
if (res.chunk.empty())
{
cout << "No more messages, exiting.\n";
return;
}
client->messages(opts, &message_handler);
}
// Callback to executed after the first (initial) /sync request completes.
void
initial_sync_handler(const mtx::responses::Sync &res, RequestErr err)
{
MessagesOpts opts;
opts.room_id = room_id_to_download_from;
opts.limit = 300;
if (err) {
cout << "error during initial sync:\n";
print_errors(err);
if (err->status_code != boost::beast::http::status::ok) {
cout << "retrying initial sync ...\n";
SyncOpts sopts;
sopts.timeout = 0;
client->sync(sopts, &initial_sync_handler);
}
return;
}
if (res.rooms.join.count(opts.room_id) == 0) {
cout << "room doesn't exist?";
return;
}
opts.from = res.next_batch;
client->messages(opts, message_handler);
}
void
login_handler(const mtx::responses::Login &res, RequestErr err)
{
if (err) {
cout << "There was an error during login: " << err->matrix_error.error << "\n";
return;
}
cout << "Logged in as: " << res.user_id.to_string() << "\n";
cout << "give room id to download all media from: ";
std::getline(std::cin, room_id_to_download_from);
SyncOpts opts;
opts.timeout = 0;
client->set_access_token(res.access_token);
client->sync(opts, &initial_sync_handler);
}
int
main()
{
std::string username, server, password;
cout << "Username: ";
std::getline(std::cin, username);
cout << "HomeServer: ";
std::getline(std::cin, server);
password = getpass("Password: ");
client = std::make_shared<Client>(server);
client->login(username, password, &login_handler);
client->close();
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment